How to disable route pre-rendering in Next.js?
Asked Answered
H

2

8

I am using route handlers.

I have a route such as app/blog/rss.xml/route.ts:

import { getBlogPosts } from '@/routines/getBlogPosts.ts';
import Rss from 'rss';

const SITE_URL = 'https://ray.run';

export const GET = async () => {
  const blogPosts = await getBlogPosts();

  const feed = new Rss({
    description: 'Lessons learned scaling Playwright test automation',
    feed_url: `${SITE_URL}/blog/rss.xml`,
    language: 'en',
    site_url: SITE_URL,
    title: 'Rayrun Blog',
  });

  for (const blogPost of blogPosts) {
    if (!blogPost.publishedAt) {
      continue;
    }

    feed.item({
      author: blogPost.author.name,
      date: blogPost.publishedAt,
      description: blogPost.description,
      guid: `${SITE_URL}/blog/${blogPost.guid}`,
      title: blogPost.title,
      url: `${SITE_URL}/blog/${blogPost.slug}`,
    });
  }

  return new Response(feed.xml(), {
    headers: {
      'content-type': 'application/xml',
    },
  });
};

Next.js tries to pre-render this page when I run next build. How do I disable that?

I've already looked through documentation and there is no mention of this.

I should have mentioned that the problem is that it seems no matter what settings I set, Next.js attempts to pre-render the page, i.e. even if I add export const dynamic = "force-dynamic"; the build fails with error complaining about export const GET = async () => {} failing.

GET fails during the build because the build environment does not have access to the database, i.e. I need to entirely skip GET during build.

The only way I found around this is checking if the variable is unset and returning dummy content, e.g.

export const GET = async () => {
  if (!process.env.POSTGRES_DSN) {
    return new Response('OK');
  }
  // ...
};

The same is also true for sitemap.ts.

Update: Turns out that for routes what Fabio suggested is the correct course of action. However, I was thrown off by the fact that sitemap.ts does not behave that way – it ignores entirely the dynamic attribute. I ended up rewriting sitemap.ts to sitemap.xml/route.ts

Happen answered 3/7, 2023 at 11:26 Comment(2)
That is because sitemap.ts is not a valid name for a route handler. It should always be /my-folder/route.ts. If the answer was correct please accept it so people after you can profit from the information provided.Cleric
Confusingly, they have it as a special route nextjs.org/docs/app/api-reference/file-conventions/metadata/…Happen
C
10

As seen in the documentation here, route handlers are statically evaluated by default when using the GET method with the Response object.

import { NextResponse } from 'next/server';
 
export async function GET() {
  const res = await fetch("https://example.com/api/test");
  const data = await res.json();
  return NextResponse.json(data);
}

However if you want a dynamic route handler, one of the following requirements or configuration options need to be met:

If none of the first three options suit your needs, you could use a segment configuration as seen in the example below:

import { NextResponse } from 'next/server';
 
export async function GET() {
  const res = await fetch("https://example.com/api/test");
  const data = await res.json();
  return NextResponse.json(data);
}

// forces the route handler to be dynamic
export const dynamic = "force-dynamic";

Please note that stack overflow expects you to do a certain degree on research yourself before posting a question, this answer is curated information from the official documentation, ultimately from the link you have provided yourself.

Cleric answered 3/7, 2023 at 14:2 Comment(1)
Sorry, I didn't make it clear what's the problem I am solving in my original question. Yes, export const dynamic = "force-dynamic"; makes the route dynamic, but the problem is that next.js still attempts to pre-render it. I added details to the original question.Happen
A
2

By default Next.js treats an API route as static if you do not pass any parameter to it. Look at the GET function in your code.

export const GET = async () => { <----- No parameter passed
  const blogPosts = await getBlogPosts();
  ...
  ...
};

Simply change it to this:

export const GET = async (req) => {
  console.log("url", req.url); // just to use passed request object, not necessary I guess.
  const blogPosts = await getBlogPosts();
  ...
  ...
};

Or if you are using TypeScript, use this:

export const GET = async (req: NextRequest) => {
  console.log("url", req.url); // just to use passed request object, not necessary I guess.
  const blogPosts = await getBlogPosts();
  ...
  ...
};

And Next.js will treat your route as dynamic.

Akkad answered 13/5 at 12:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.