Next.js Discord

Discord Forum

Dynamic API routes return 404 on Vercel but not locally (Next 14.0.1, app router)

Answered
Ben Greene posted this in #help-forum
Open in Discord
Avatar
Hi all - I have a very annoying error that I can't seem to fix. This is on Next 14.0.1 using app router.

The specific behavior is that certain API routes are returning 404 errors instead of loading, but only when deployed to Vercel. The attached image shows the directory structure of the API routes - the specific routes that fail are api/programs/[id] and api/programs/page/[page]. The api/users, api/users/[id], and api/programs routes all return results.

There does not appear to be any significant difference in the route.ts files. Does anyone have suggestions on what I could try?

Thank you 🙏
Image
Answered by Ben Greene
Update: although the why still eludes us, we were able to resolve the problem. The cause seems to be having a directory in the root of the project (at the same level as app) called api. This was being used for some library files. Renaming that directory from api to apiServices seems to be the only change required to make the dynamic routes begin functioning properly again.
View full answer

116 Replies

Avatar
Siricid woodwasp
where are you calling those apis in client or server?
Avatar
@Siricid woodwasp Sorry, I'm not completely sure I understand your question. Whether I make the API call from within a component or directly via the web browser, I get the 404 either way.
Avatar
Siricid woodwasp
i mean how are you calling api end points in your app like fetch("api/user")
Avatar
Ahh - const response = await get(programs/page/${page});
Avatar
Siricid woodwasp
are those client components?
Avatar
It's a lib used within client components, I believe - will need to check, currenly away from desk
Avatar
confimed, client components
but I'm fairly sure that the problem is somehow elsewhere, because accessing the API call directly from the browser also fails 😕
Avatar
@Siricid woodwasp I think I've made some progress - this seems to only hit API calls that have a dynamic element in them, e.g. programs/page/[page] - it turns out that users/[id] doesn't work either (on Vercel - still works fine locally).
Avatar
Northern snakehead
just going to jump in here and follow this as I believe i have the same issue 🙂
Avatar
I updated the title to be a bit more specific and hopefully garner more assistance 🙏
Avatar
Yo Folks. Let's solve this - at the beginning @Ben Greene, could you provide us with this endpoint code/repository url (if it's public) or minimal reproduction?

https://nextjs-faq.com/minimal-reproduction-repository
also if that's possible please share an screenshot of that 404 error which you get - this will help assign if we have problem with vercel (platform) or nextjs (your code)
Avatar
@Z4NR34L appreciate your help!
Here's a screenshot of the 404 on Vercel (left) and the 200 on localhost (right):
Image
The repo is not public, so it'll take me some time to create a new test repo
While I get that ready - my colleague believes that not having NEXT_PUBLIC_API_URL defined in the ENV variables may be a cause of this problem - could that be the case? And if so, how is one supposed to generate that in Vercel preview deployments?
Avatar
Ok, There is no chance to get into the cause of this issue without even minimal reproduction or code, we can just guess, and env variables is the first thing that should be double checked before deployment 😄
scan your code which env vars are you using, remember that if you use variable in route handler you dont need to add NEXT_PUBLIC_ prefix as it’s executed server-side
Avatar
lol yeah, I'm working on a repro now
Avatar
if you don't have any hardcoded credentials, just post endpoint code here, that should be fine for now
Avatar
Sure, so here is the contents of app/api/programs/page/[page]/route.ts:
import { PrismaClient } from '@prisma/client';
import { NextRequest, NextResponse } from "next/server";

const prisma = new PrismaClient();

export async function GET(request: NextRequest) {
  const prisma = new PrismaClient();
  const programs = await prisma.program.findMany();
  return NextResponse.json(programs);
}
Avatar
also check your env vars on vercel, if you have selected all needed environments (Development/Preview/Production)
Image
Avatar
And that exact same code works in app/api/programs/route.ts
Avatar
huh, I see
are you running prisma generate during the build step? 😄
Avatar
Image
Avatar
ok, did you checked env vars?
you need preview + production on vercerl to get it work there
Avatar
Well I don't have DATABASE_URL at all, but the above returns data from the DB on the non-dynamic route
I have POSTGRES_URL
Avatar
ok, I meant any env vars that are used there
Avatar
@Ben Greene Sure, so here is the contents of `app/api/programs/page/[page]/route.ts`: import { PrismaClient } from '@prisma/client'; import { NextRequest, NextResponse } from "next/server"; const prisma = new PrismaClient(); export async function GET(request: NextRequest) { const prisma = new PrismaClient(); const programs = await prisma.program.findMany(); return NextResponse.json(programs); }
Avatar
first, let's try to check if everything is just building fine, replace this code with:

import { NextRequest, NextResponse } from "next/server";

export async function GET(request: NextRequest, {params}: { params: { page: number }}) {
  return NextResponse.json({page: params.page});
}


and verify if you get back number you provided in path
Avatar
pushed to vercel
still a 404
I'm not an expert on Next.js by any means, but I don't think the code content of the route itself is playing much of a role
Avatar
that's what we needed to verify
Now, please share your next.config and middleware (if any), also vercel.json if used
and to be sure, post file path in repository, and URL without domain
Avatar
next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  images: {
    domains: ["lh3.googleusercontent.com", "vercel.com"],
  },
  async redirects() {
    return [
      {
        source: "/github",
        destination: "https://github.com/steven-tey/precedent",
        permanent: false,
      },
    ];
  },
};

module.exports = nextConfig;

(I should probably change that github source from the repo I cloned)
middleware.ts:
import createMiddleware from 'next-intl/middleware';
import { LOCALES } from '@/lib/constants';
 
export default createMiddleware({
  // A list of all locales that are supported
  locales: LOCALES,
 
  // If this locale is matched, pathnames work without a prefix (e.g. `/about`)
  defaultLocale: 'en'
});
 
export const config = {
  // Skip all paths that should not be internationalized. This example skips the
  // folders "api", "_next" and all files with an extension (e.g. favicon.ico)
  matcher: ['/((?!api|_next|.*\\..*).*)']
};
No vercel.json
Avatar
ok, so url without domain and file path in repository - just for double-check
also please check your vercel logs if there is no errors 😉
Avatar
the relative file path (copied from VS Code) is: app/api/programs/page/[page]/route.ts
And the URL without domain: /api/programs/page/1
the deployment logs say 0 errors and 0 warnings
I've read through it and not seen anything particularly interesting
although it does identify the routes in question:
Route (app)                              Size     First Load JS
┌ ○ /_not-found                          882 B          89.2 kB
├ λ /[locale]                            559 B           124 kB
├ λ /[locale]/about                      550 B           124 kB
├ λ /[locale]/auth/error                 957 B           124 kB
├ λ /[locale]/auth/signin                863 B           135 kB
├ λ /[locale]/auth/verify-request        1 kB            124 kB
├ λ /[locale]/programs                   2.24 kB         125 kB
├ λ /[locale]/programs/[id]              2.4 kB          125 kB
├ λ /[locale]/programs/edit/[id]         1.11 kB         227 kB
├ λ /[locale]/programs/new               572 B           227 kB
├ λ /[locale]/users/[id]/edit            1.59 kB         125 kB
├ λ /api/auth/[...nextauth]              0 B                0 B
├ λ /api/programs                        0 B                0 B
├ λ /api/programs/details/[id]           0 B                0 B
├ λ /api/programs/page/[page]            0 B                0 B
├ ○ /api/users                           0 B                0 B
└ λ /api/users/[id]                      0 B                0 B
+ First Load JS shared by all            88.3 kB
  ├ chunks/472-e4fe7de096be0269.js       33 kB
  ├ chunks/fd9d1056-1de2bf583fdbc1a8.js  53.2 kB
  ├ chunks/main-app-892c3dff08e9cd4c.js  232 B
  â”” chunks/webpack-fbec2224bf4eac50.js   1.87 kB
Avatar
huh
Avatar
sounds like we're on the same page
Avatar
wait a sec
I'm using dynamic route handlers in my projects but I see that there is small difference
I'm on edge runtime
try to change our test route to edge 😄

export const runtime = 'edge'
Avatar
It's off 🍿
👎 😞
Avatar
you are using next-auth
you are not locking those urls? 😄
Avatar
...?
Avatar
maybe I will ask in other words: are you using next-auth to auth users before accessing those routes?
I mean api routes
Avatar
oh - no, I'm not - though I certainly will down the line
Avatar
the problem is - I cannot reproduce that issue in clean next.js - so maybe one of packages that you had used is messing around
but let's try one more thing - remove middleware for now
just to get as clean access to route as possible
Avatar
What version of nextjs are you on
Avatar
do you see any errors from the logs on vercel dashboard?
Avatar
not really:
Image
though I didn't expect a 204...
Avatar
I'm using 14.0.2 currently
you are viewing a HEAD method request log, and you are supposed to make an GET method
Avatar
yeah.... weird
Avatar
but /api/programs is working fine from what I can see
Avatar
correct
Avatar
but, can you see more logs on vercel now?
Avatar
this is so bizarre
here's chrome inspector:
Image
but there's no corresponding 404 in vercel:
Image
I'm on 14.0.1, so I'm gonna upgrade that just to see
Avatar
there is still middleware that's working
try disabling/removing it for now
Avatar
sorry that middleware is from a different branch
Avatar
ok
I remember that I was not using next-intl for a reason with appdir 😄 But not sure what exacly problem that was
Avatar
::blinks::
Avatar
I've made my own middlware for locales routing
/api/users/[id] is not working too from what I checked now
Avatar
correct
no dynamic api route is
I wonder if it worked when I started this with 13.5......
Avatar
hmm, that's pretty wide problem, and I think I'm not able to help you without testing this right on your repository :/
Avatar
i can't reproduce it too
Avatar
Interesting.
Also interesting: after upgrading to 14.0.2, I now get this error:
app/api/auth/[...nextauth]/route.ts
Type error: Route "app/api/auth/[...nextauth]/route.ts" does not match the required types of a Next.js Route.
  "authOptions" is not a valid Route export field.
Avatar
^^^ this is not so much interesting as just annoying
Avatar
what version of next-auth are you using
Avatar
4.22.1
Avatar
does the api work if you change the middleware file to _middleware
Avatar
sorry had to get back to a working state - so just rename it to _middleware...
Avatar
I mean the short answer is that obviously kills my locale-enabled pages... working on actually getting a successful deploy through Vercel... been making a lot of changes quickly...
Ok! Results are in, and removing/renaming middleware file does not make the dynamic API routes functional.
Avatar
So that case too wide to debug it just by telling you what to do, badly. Easiest way would be making a reproduction or allowing us to view the origin code
Avatar
@Z4NR34L I DMed you
Avatar
Update: This topic is in progress - if we find a solution, I'll post it there
Avatar
Update: although the why still eludes us, we were able to resolve the problem. The cause seems to be having a directory in the root of the project (at the same level as app) called api. This was being used for some library files. Renaming that directory from api to apiServices seems to be the only change required to make the dynamic routes begin functioning properly again.
Answer
Avatar
brilliant 😄
Avatar
Right?! 😞
Avatar
that's perfect example why it's important for others to see whole code/repository or reproduction 😄
Avatar
Berger Picard
hey sorry to revive this, I'm having the exact same issue but with localhost
/api/players returns 404
Image
even in postman
its really bare too
Image
Avatar
@Berger Picard /api/players returns 404
Avatar
why are you not using route.ts as players.ts is going to be ignored.... also for future can you make a new thread as everyones are diferent
Avatar
Berger Picard
why
Avatar
what do you mean by "why"?
your /api isn't going to be usable, idk if thats intentional