Which route is considered as 'static routing'?

I am using Nextjs app routing.

[problem]
I have implemented an api called player-wallets using app router.
The problem is that this api is treated as a static route, which means that the result is generated at build time and returns cached data on request.
So it doesn’t reflect the user’s wallet information in real time.

// src/app/api/player-wallets/route.ts

import { NextResponse, NextRequest } from 'next/server'
export const GET = async (request: NextRequest) => {
  try {
    const playerWallets = await prisma.playerWallet.findMany({
      where: {
        walletType: WalletType.CUSTOM,
      },
    })
    return NextResponse.json(playerWallets, {
      status: 200,
    })
  } catch (error) {
    console.error(error)
    return NextResponse.json(
      { error: 'Internal Server Error' },
      { status: 500 },
    )
  }
}

[solution]
I changed it to a dynamic router by adding export const dynamic = 'force-dynamic' to solve the urgent problem.

[question]
But my question is, which routes are handled as dynamic and which routes are handled as static? Is there any specific way to determin which route is static?

When I write this route, it’s hard to imagine that it will be static (not querying the db every time).
This seems like a very error-prone behavior, and I’d love to hear your thoughts on it.

[more]
I contacted the vercel team about this and received the following response.
However, this is still not resolved, so I am asking here.

“”
The determination of whether a route is dynamic or static depends on several factors:

Routes using dynamic functions like cookies(), headers(), or accessing request-specific properties are automatically made dynamic.
Routes with dynamic segments in the URL path (like [id]) may trigger dynamic behavior
The data patterns and imports in your code can influence caching decisions
Your other API routes might be using patterns that Next.js automatically detects as requiring dynamic behavior, while this particular route didn’t trigger those same conditions.

For more detailed information on this behavior, you can refer to the Next.js documentation on caching.
“”

Understanding Static vs. Dynamic Routes in Next.js App Router

In the App Router, Next.js applies automatic caching strategies that might not always align with your expectations, especially when coming from the Pages Router or other frameworks.

Default Behavior for Route Handlers

As of Next.js 15, the default caching behavior for GET route handlers was changed from static to dynamic . However, in earlier versions (which you might be using), GET handlers were static by default.

This means your API route was being cached at build time rather than executing the database query on each request, which explains why you weren’t seeing real-time wallet information.

Determining Static vs. Dynamic Routes

Next.js uses several signals to determine if a route should be dynamic:

  1. Explicit declarations: Using export const dynamic = 'force-dynamic' (as you did)
  2. Dynamic functions: Using cookies(), headers(), or accessing request-specific properties
  3. Dynamic segments: Routes with parameters like [id] in the path
  4. Data patterns: Certain data fetching patterns can influence caching decisions

Solutions and Best Practices

Your solution of adding export const dynamic = 'force-dynamic' is the correct approach for ensuring your route handler executes on every request.

// src/app/api/player-wallets/route.ts
import { NextResponse, NextRequest } from 'next/server'
export const dynamic = 'force-dynamic' // Forces dynamic behavior

export const GET = async (request: NextRequest) => {
  // Your code will now run on every request
}

Alternative Approaches

  1. Use dynamic functions: Including cookies() or headers() will automatically make your route dynamic
  2. Use request-specific properties: Accessing query parameters from the request will trigger dynamic behavior
  3. Use a dynamic segment: Adding a parameter to your route path can help

Why This Is Error-Prone

You’re right that this behavior can be error-prone. The automatic static optimization is great for performance but can cause unexpected behavior when you need real-time data.

The best practice is to be explicit about your caching intentions by using the dynamic export or other cache control directives when building API routes that need fresh data.

1 Like

Thanks so much.
But I have one more question.

In other APIs, I don’t use [id], I don’t have any headers, I don’t use force-dynamic, and yet it’s being treated as dynamic.
Perhaps the
Data patterns: Certain data fetching patterns can influence caching decisions
and it’s being treated as dynamic.

Can you tell me under what circumstances this pattern is triggered?

There are several data fetching patterns that can cause Next.js to treat a route as dynamic, even if you’re not explicitly using dynamic segments, headers, or force-dynamic. Here are the main scenarios:

  1. Using searchParams prop in a page component, which makes the route dynamic by default
  2. Using cookies() function anywhere in the route, which also triggers dynamic rendering
  3. Using fetch() with cache: 'no-store' option, which opts out of caching
  4. Using third-party libraries or ORMs that don’t use the fetch API under the hood (without proper caching configuration)
  5. Using useSearchParams() hook in a Client Component that’s imported in the route
  6. Using next/headers in any component within the route

If you want to ensure a route is statically rendered, you can explicitly set:

export const dynamic = 'force-static'

This will force static rendering and cache the data, even overriding any dynamic APIs by making them return empty values .

Alternatively, if you’re using a database or ORM, you can use the unstable_cache API to cache database queries:

import { unstable_cache } from 'next/cache'
import { db } from '@/lib/db'

const getData = unstable_cache(
  async () => {
    return await db.query()
  },
  ['cache-key'],
  { revalidate: 3600 }
)

This allows you to cache database queries that would otherwise make the route dynamic .

Thankyou for your answers.
But I’m still have question.

In my case, as you can see, I’m using prisma ORM.
But it was treated as ‘static’.
Why is it?

Below is the full code of my api.

// src/client/prisma/item.ts
declare global {
  var prismaItem: PrismaClient | undefined
}
const prisma =
  global.prismaItem ||
  new PrismaClient({
    datasources: {
      db: {
        url: process.env.DATABASE_URL_ITEM,
      },
    },
  })
if (process.env.NODE_ENV === 'development') global.prismaItem = prisma
export default prisma

// src/app/api/player-wallets/route.ts
import prismaInstance from 'src/client/prisma/item'
import { NextResponse, NextRequest } from 'next/server'
export const GET = async (request: NextRequest) => {
  try {
    const playerWallets = await prismaInstance.playerWallet.findMany({
      where: {
        walletType: WalletType.CUSTOM,
      },
    })
    return NextResponse.json(playerWallets, {
      status: 200,
    })
  } catch (error) {
    console.error(error)
    return NextResponse.json(
      { error: 'Internal Server Error' },
      { status: 500 },
    )
  }
}

I believe as mentiooned, routes are statically rendered by default unless they contain dynamic behavior . Your API route doesn’t use any of the Dynamic APIs (like cookies, headers, searchParams, etc.) that would automatically opt the route into dynamic rendering .

When using an ORM like Prisma, the database query itself doesn’t automatically make the route dynamic . This is because Next.js doesn’t inherently know that your Prisma query needs fresh data on each request.

Here’s why your route is being treated as static:

  1. You’re not using any Dynamic APIs in your route handler
  2. You haven’t explicitly marked the route as dynamic using export const dynamic = 'force-dynamic'
  3. Your Prisma query doesn’t have any caching directives that Next.js recognizes
1 Like

Oh, I see, I understand.
APIs that use prisma to simply retrieve data from the DB and pass it on (like the one I gave as an example)
must be set to dynamic when using it.

Thank you for your kind response.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.