next.js: Failed to parse URL when fetching localhost using Server Components
Verify canary release
- I verified that the issue exists in the latest Next.js canary release
Provide environment information
Operating System:
Platform: linux
Arch: x64
Version: #22 SMP Tue Jan 10 18:39:00 UTC 2023
Binaries:
Node: 16.17.0
npm: 8.15.0
Yarn: 1.22.19
pnpm: 7.1.0
Relevant packages:
next: 13.3.1-canary.6
eslint-config-next: 13.3.0
react: 18.2.0
react-dom: 18.2.0
Which area(s) of Next.js are affected? (leave empty if unsure)
App directory (appDir: true)
Link to the code that reproduces this issue
To Reproduce
run pnpm dev
and go to port 3000 for it to crash in runtime
Describe the Bug
Calling fetch
within a server component in order to get data from the same host fails:
fetch('/api/posts')
throws an error: TypeError: Failed to parse URL from /api/posts
.
However, fetching the same URL within a client component works fine.
Trying to access the resource with fetch('http://localhost:3000/api/posts')
does work.
In the attached CodeSandbox repo: Fetch in a server component -> app/components/post-list/index.tsx - line 3 Fetch in a client component -> app/components/add-post/index.tsx - line 15
Expected Behavior
Calling fetch('/api/posts')
should work inside server components
Which browser are you using? (if relevant)
Chrome 111.0.5563.147
How are you deploying your application? (if relevant)
No response
About this issue
- Original URL
- State: closed
- Created a year ago
- Reactions: 18
- Comments: 24 (4 by maintainers)
Commits related to this issue
- test: importing the api following https://github.com/vercel/next.js/issues/48344 — committed to TrustfulDev/airsoft-armory by TrustfulDev a year ago
I found this solution and it works fine:
if you are not using a server component you can directly call with a relative URL
How do you pass query params in this case?
Got it. However, I could expect it to know the absolute URL when using dynamic data fetches, because Next knows the requested URL.
Besides that, I think throwing a more guiding error will help. Something like “Failed to parse URL. Pass an absolute URL when using server components”
guys try this.
any update about it? I have found the same solution but methods needs request and param but they are sended by url
Is it a valid way to fix this by using the
host
header to construct the absolute API URL? That should work both in development and production environment if I’m not mistaking.Yeah, but that’s a fetch, a native API to node, that’s crashing. Not sure it’s a good idea to hijack that.
Another framework, Nuxt, uses $fetch, made by them to allow for the request to call into the API route from the server. https://nuxt.com/docs/guide/concepts/server-engine#direct-api-calls
Without such helpers, the problem is that you’d be making yet another request round trip, when you are already at the server side.
So you should just invoke the data you want directly, that’s one of the reasons we are writing the components on the server.
That means connecting to your database or data endpoints from the server component without having to involve API routes at all.
I see, it’s true that the request is really redundant when I can just import the function directly, and it’s even typesafe because I don’t need to cast the response after fetching. Thanks 😃
If you run the
node
REPL, in node 18+, and writefetch("/api/route")
, what would you expect? It is similar in Server Side code, namely GetServerSideProps, GetStaticProps, and Server Components.That is to say that you need an absolute URL. The browser, per spec IIRC, assumes paths relative to the document base URL/origin or something like that.
It is a fun read 😅
This is even happening in client components, is that expected?
What is the correct way to “opt out” of code running on the server? Docs (https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic-rendering) seem to suggest that using the
useSearchParams()
hook will ensure components do not render on server, but that does not seem to be the case (on v13.4.7).Because server components run on the server, they essentially act like a node server. That’s why requesting ‘/path’ won’t resolve (same behavior as a regular server). You need to specify absolute paths for the URL to be parsed (e.g ‘http://localhost/path’)
I used the following technique and we can pass query params by adding question mark after the ‘api/test?id=123’. the url itself doesn’t matter as we know which api function we are calling and only require query params.
import { NextRequest } from ‘next/server’
import TestListClient from ‘./list’; async function getData() { const apis = await import(‘…/…/api/test/route’); const body = await (await apis.GET(new NextRequest(‘http://localhost/api/test’))).json() return body.data; }