Next.js Edge API Routes

Conclusion

runtime: "experimental-edge" behaves differently from runtime: "nodejs".

It returns different HTTP response headers and I will explain the detail in this post (although this might be wrong partially.)

My Concern

Next.js v12.2 introduces Edge Runtime.

I’d just understood it couldn’t use Node.js standard APIs and was lightweight.

But I was not sure how this worked and how differed from runtime: "nodejs" and wondered whether this affected when choosing self-hosted.

The official document says like this:

Edge API Routes can stream responses from the server and run after cached files (e.g. HTML, CSS, JavaScript) have been accessed.

…OK, I will test the new API routes!

Test Code

Set experimental.runtime: "nodejs" in next.config.js

pages/api/test.ts uses runtime: "nodejs":

import { NextApiHandler } from "next";

const handler: NextApiHandler = async (_req, res) => {
  res.status(200).json({ name: "Jim Halpert" });
};

export default handler;

pages/api/test-edge.ts uses runtime: "experimental-edge":

import { NextApiHandler } from "next";

export const config = {
  runtime: "experimental-edge",
};

const handler: NextApiHandler = async (_req, res) => {
  // see: https://nextjs.org/docs/api-routes/edge-api-routes#json-response
  return new Response(
    JSON.stringify({
      name: "Jim Halpert",
    }),
    {
      status: 200,
      headers: {
        "Content-Type": "application/json; charset=utf-8",
      },
    },
  );
};

export default handler;

Both of them returns the same response body.

Result

I picked up only response headers part.

curl -v http://localhost:xxx/api/test

< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< ETag: "16-DlZOPLD8Q/zEnYFRYsdmnqlUPWI"
< Content-Length: 22
< Vary: Accept-Encoding
< Date: Thu, 30 Jun 2022 11:42:50 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5

curl -v http://localhost:xxx/api/test-edge

< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Date: Thu, 30 Jun 2022 11:43:39 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< Transfer-Encoding: chunked

experimental-edge runtime:

  • lacks ETag, Content-Length, and Vary headers
  • adds Tranfer-Encoding: chunked header

Transfer-Encoding: chunked sends HTTP repsonse as chunk (or stream) according to MDN doc.

  • With this header, Content-Length is omitted from the doc.
  • It’s understandable Vary: Accepct-Encoding is omitted for this endpoint because Transfer-Encoding is specified (chunks are always sent).
  • The reason ETag is dropped, I guess, might be that the endpoint could not end in a single request.

Again, my understanding for HTTP is not enough and this post might be wrong. Please check correct sources by yourself.

References

Contents