typescriptnext.jsapp-router

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


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?


Solution

  • 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';
    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 });
    }
    

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