next.js: [NEXT-1194] After upgrade to 13.3 request.json() in DELETE route handler fails

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
      Platform: win32
      Arch: x64
      Version: Windows 10 Pro
    Binaries:
      Node: 18.12.1
      npm: N/A
      Yarn: N/A
      pnpm: N/A
    Relevant packages:
      next: 13.3.1-canary.1
      eslint-config-next: 13.2.4
      react: 18.2.0
      react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

No response

Link to the code that reproduces this issue

N/A

To Reproduce

import { NextResponse } from "next/server"
const DATA_SOURCE_URL = "https://jsonplaceholder.typicode.com/todos"

type Todo = {
    userId: number,
    id: number,
    title: string,
    completed: boolean,
}

export async function DELETE(request: Request) {

    const { id }: Partial<Todo> = await request.json() // error

    if (!id) return NextResponse.json({ "message": "Todo id required" })

    await fetch(`${DATA_SOURCE_URL}/${id}`, {
        method: 'DELETE',
    })

    return NextResponse.json({ "message": `Todo ${id} deleted` })
}

Describe the Bug

I only encounter this error on the DELETE route handler. My POST and PUT route handlers can await request.json() with no issues.

I run the same code in 13.2.4 and do not receive the error.

I am sending valid and very simple JSON:

{
   "id": 3
}

Nevertheless, I receive this error:

SyntaxError: Unexpected end of JSON input
    at JSON.parse (<anonymous>)
    at NextRequest.json (node:internal/deps/undici/undici:6160:23)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async DELETE (webpack-internal:///(sc_server)/./src/app/api/todos/route.ts:63:21)

Expected Behavior

I expect await request.json() to work as it does in the POST and PUT route handlers.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

NEXT-1194

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 75
  • Comments: 53 (2 by maintainers)

Commits related to this issue

Most upvoted comments

I stumbled across this but then found that DELETE generally should not have a request body - the fact that nextjs previously supported it sounds like a bug to me: https://www.rfc-editor.org/rfc/rfc9110.html#name-delete

Although request message framing is independent of the method used, content received in a DELETE request has no generally defined semantics, cannot alter the meaning or target of the request, and might lead some implementations to reject the request and close the connection because of its potential as a request smuggling attack (Section 11.2 of [HTTP/1.1]). A client SHOULD NOT generate content in a DELETE request unless it is made directly to an origin server that has previously indicated, in or out of band, that such a request has a purpose and will be adequately supported. An origin server SHOULD NOT rely on private agreements to receive content, since participants in HTTP communication are often unaware of intermediaries along the request chain.

Though it sounds frustrating if you were previously depending on this behavior…

The way that work for me with DELETE is using the params (same as the doc) and stop to use the body like other on top suggest


export async function DELETE(
  request: Request,
  { params }: { params: { id: string } }
)

maybe is not in the doc but the magic here is too create the route like this :

--- api 
    --- myRouteFolder        // named as you prefer 
        --- route.tsx
        --- [myRouteFolderid]  // create this folder to receive the ID for example
             ---route.tsx             // add a new route file and here u go to create wherever u want to pass (id) to ur function

for example :

export async function DELETE(
  request: Request,
  { params }: { params: { id: string } }
) {
  const { id } = params;
  console.log("###", id)
}

and then u just need to

await fetch(`api/myRouteFolder/${deleteByID}`, {
      method: "DELETE",
    });

I hope it will be helpful

The problem exist also in 13.4.1

+1 Facing the issue with dev server. Production build works fine.

Still occurs in Next.js v13.4.2 and is impacting the commerce template: https://github.com/vercel/commerce

@alecf thanks.

I use the following codes for those new to app /api and do not know how to get the query params.

DELETE /api/post?_id=12345

export async function DELETE(request: Request) {

  const { searchParams } = new URL(request.url);
  const _id = searchParams.get('_id') || ''; // for ts

  // ...
}

+1: work in production, don’t work in localhost

Just some clarity on method bodies. It’s not common practice for methods other than PUT, POST and PATCH to have a

There’s no reason you can’t have use a POST request for multiple purposes, as in, adding content and deleting, you could change the action depending on a URL param. That being said, obviously be extra careful not to delete content where you want to add it, and vice versa.

As for the issue, I can’t even get an empty DELETE request (without body) to work.

Working with next@13.4.4 by using POST Method facing this issue as well.

here are the error messages.

By useing request.body

export async function POST(request: Request) {
  const res = request.body;
}

Throws

error StaticGenBailoutError: Page with `dynamic = "error"` couldn't be rendered statically because it used `request.body`.

By useing request.json()

export async function POST(request: Request) {
  const res = request.json();
}

Throws

error StaticGenBailoutError: Page with `dynamic = "error"` couldn't be rendered statically because it used `request.json`.

By useing request.text()

export async function POST(request: Request) {
  const res = request.text();
}

Throws

error StaticGenBailoutError: Page with `dynamic = "error"` couldn't be rendered statically because it used `request.text`.

Could anyone tell me how can I retrive BODY in request ? 😿😿😿😿

i dont know if this will also work for delete i got this issue with POST where i wasn’t able to get body i’m a noob in this kind of this discussions

export async function POST(request: Request, response: NextApiResponse) {
    if (request.method !== 'POST') {

    }
    else {

        const a = await request.text()
        const data = JSON.parse(a)
        const { id, email, password, role } = data;
        const modipassword = process.env.SECRATE_KEY + password
        const salt = await bcrypt.genSalt(10)
        const hashedPassword = await bcrypt.hash(modipassword, salt);
        



        const newUser: typeAuthor = {
            id: id,
            name: '',
            email: email,
            password: hashedPassword,
            role: role,
            facebook_link: '',
            insta_link: '',
            twitter_link: '',
            status: 'Active',
            created_at: new Date(),
            updated_at: new Date(),
        };

        await db.insert(Author).values(newUser);



        const custom_data_array = [{ message: 'Successfully Added' }]
        return new Response(JSON.stringify(custom_data_array), { status: 200 })
    }
}

well i got work around for my problem like this hope it would be some use to everyone here

Tried. Still doesn’t work.

image

image

I believe that this should work, maybe ur problem is coming from the way u call it, can u share with us your handle, and maybe try to change ur Response with NextResponse for example :

return new NextResponse(data, { status: 200 });


@daveteu

export async function DELETE(request: Request) {
  const { query } = request; // query will hold all of them

  // ...
}

I am having the same issue with the POST endpoint. Debugging for two days, decided to switch back to /pages router and it works on first try. The app router is not ready for production yet, in my case atleast.

Just some clarity on method bodies. It’s not common practice for methods other than PUT, POST and PATCH to have a There’s no reason you can’t have use a POST request for multiple purposes, as in, adding content and deleting, you could change the action depending on a URL param. That being said, obviously be extra careful not to delete content where you want to add it, and vice versa. As for the issue, I can’t even get an empty DELETE request (without body) to work.

Working with next@13.4.4 by using POST Method facing this issue as well.

here are the error messages.

By useing request.body

export async function POST(request: Request) {
  const res = request.body;
}

Throws

error StaticGenBailoutError: Page with `dynamic = "error"` couldn't be rendered statically because it used `request.body`.

By useing request.json()

export async function POST(request: Request) {
  const res = request.json();
}

Throws

error StaticGenBailoutError: Page with `dynamic = "error"` couldn't be rendered statically because it used `request.json`.

By useing request.text()

export async function POST(request: Request) {
  const res = request.text();
}

Throws

error StaticGenBailoutError: Page with `dynamic = "error"` couldn't be rendered statically because it used `request.text`.

Could anyone tell me how can I retrive BODY in request ? 😿😿😿😿

Add this to the bottom of your route file to make your route handler a dynamic route;

export const dynamic = "force-dynamic";

You need to make your route handler a dynamic route. NextJs failed to do this automatically, I think it has a bug that sets dynamic as error when trying to calculate it automatically.

See References

Hi guys ! I just found working solution to get ID when performing DELETE Request …

const deleteResponse = await fetch(/api/yourRoute/route`, { method: “DELETE”,

		headers: {
			"Content-Type": "application/json",
			"X-ID": id,
		},
	});`

Basically what we are passing ID in our own header… On other hand in NextJS

app/api/route we can access it by using

const head = headers();

	console.log(request.headers);

	const head_token = head.get("x-id");

	console.log(head_token);

It works for me… Thanks

Phew. I’m not alone. 13.4.0

+1