Next-js App-Router dynamic route: ‘params should be awaited’ warning in /api/…/[id]/… handler — why and how to fix?

Next-js App-Router dynamic route: ‘params should be awaited’ warning in /api/…/[id]/… handler — why and how to fix?
typescript
Ethan Jackson

I added a dynamic route handler:

src/app/api/meetings/[meetingId]/respond/route.ts
import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from '@/src/app/api/auth/[...nextauth]/auth-options'; import { getMeetingsCollection } from '@/src/lib/db'; import { ObjectId } from 'mongodb'; interface RequestBody { action: 'ACCEPT' | 'DECLINE'; } export async function PUT( req: NextRequest, { params }: { params: { meetingId: string } } // <-- read params like usual ) { const session = await getServerSession(authOptions); const { meetingId } = params; // <-- triggers warning /* ...update meeting... */ }

Every time I hit

PUT /api/meetings/6828463d63c61a6feda6ab12/respond

the dev-server logs:

Error: Route "/api/meetings/[meetingId]/respond" used `params.meetingId`. `params` should be awaited before using its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis

I’ve always used the App Router; nothing was upgraded. I just created this [meetingId] folder.

What is this warning and how do I fix it?

Answer

In the App Router, when a route (page, layout, or route handler) lives inside a dynamic segment ([meetingId], [slug], etc.) the params object is a Promise.

Accessing it synchronously causes the “params should be awaited” warning.

Fix = await the promise (or use React.use() in a client component).

// src/app/api/meetings/[meetingId]/respond/route.ts import { NextRequest, NextResponse } from 'next/server'; export const dynamic = 'force-dynamic'; // optional: disables static optimisation import { getServerSession } from 'next-auth'; import { authOptions } from '@/src/app/api/auth/[...nextauth]/auth-options'; import { getMeetingsCollection } from '@/src/lib/db'; import { ObjectId } from 'mongodb'; import { MeetingSchema } from '@/src/schemas/MeetingSchema'; /* ---- types ---- */ interface RequestBody { action: 'ACCEPT' | 'DECLINE'; } interface RouteContext { params: Promise<{ meetingId: string }>; } export async function PUT(req: NextRequest, context: RouteContext) { /* 1. auth check ------------------------------------------------------- */ const session = await getServerSession(authOptions); if (!session?.user?.id) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } /* 2. await the params ---------------------------------------------- */ const { meetingId } = await context.params; if (!ObjectId.isValid(meetingId)) { return NextResponse.json({ error: 'Invalid meetingId' }, { status: 400 }); } /* 3. handle ACCEPT / DECLINE ----------------------------------------- */ const meetings = await getMeetingsCollection(); const _id = new ObjectId(meetingId); const body = (await req.json()) as RequestBody; const now = new Date(); if (body.action === 'ACCEPT') { const updated = await meetings.findOneAndUpdate( { _id, inviteeId: new ObjectId(session.user.id), status: 'PENDING_INVITEE_ACTION' }, { $set: { status: 'ACCEPTED', selectedTime: '$proposedTime', updatedAt: now } }, { returnDocument: 'after' } ); return NextResponse.json({ meeting: updated }, { status: 200 }); } if (body.action === 'DECLINE') { const updated = await meetings.findOneAndUpdate( { _id, inviteeId: new ObjectId(session.user.id), status: 'PENDING_INVITEE_ACTION' }, { $set: { status: 'DECLINED_BY_INVITEE', updatedAt: now } }, { returnDocument: 'after' } ); return NextResponse.json({ meeting: updated }, { status: 200 }); } return NextResponse.json({ error: 'Invalid action' }, { status: 400 }); }
  • await context.params unwraps the Promise so you can use meetingId.

  • Adding export const dynamic = 'force-dynamic' is optional; it just tells Next.js not to attempt static optimisation for this handler, eliminating a separate warning.

Restart the dev server. no more “params should be awaited” message, and the handler works normally.

Related Articles