Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failed to parse URL when fetching localhost using Server Components #48344

Closed
1 task done
Amirko28 opened this issue Apr 13, 2023 · 24 comments
Closed
1 task done

Failed to parse URL when fetching localhost using Server Components #48344

Amirko28 opened this issue Apr 13, 2023 · 24 comments
Labels
area: app App directory (appDir: true) bug Issue was opened via the bug report template. locked

Comments

@Amirko28
Copy link

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

https://codesandbox.io/p/github/Amirko28/next13-test/draft/intelligent-cherry?file=%2Fapp%2Fcomponents%2Fpost-list%2Findex.tsx&selection=%5B%7B%22endColumn%22%3A51%2C%22endLineNumber%22%3A2%2C%22startColumn%22%3A30%2C%22startLineNumber%22%3A2%7D%5D&workspace=%257B%2522activeFilepath%2522%253A%2522%252Fapp%252Fcomponents%252Fpost-list%252Findex.tsx%2522%252C%2522openFiles%2522%253A%255B%2522%252FREADME.md%2522%252C%2522%252Fapp%252Fapi%252Fposts%252FgetAll.ts%2522%252C%2522%252Fapp%252Fapi%252Fposts%252Fcreate.ts%2522%252C%2522%252Fapp%252Fcomponents%252Fpost-list%252Findex.tsx%2522%252C%2522%252Fapp%252Fcomponents%252Fadd-post%252Findex.tsx%2522%255D%252C%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522gitSidebarPanel%2522%253A%2522COMMIT%2522%252C%2522spaces%2522%253A%257B%2522clgf51ety000x356ilg8ae35d%2522%253A%257B%2522key%2522%253A%2522clgf51ety000x356ilg8ae35d%2522%252C%2522name%2522%253A%2522Default%2522%252C%2522devtools%2522%253A%255B%257B%2522type%2522%253A%2522UNASSIGNED_PORT%2522%252C%2522port%2522%253A3000%252C%2522url%2522%253A%2522jiwe3j-3000.csb.app%2522%252C%2522key%2522%253A%2522clgf57pcp02n0356igg3gw2oz%2522%252C%2522isMinimized%2522%253Afalse%257D%252C%257B%2522type%2522%253A%2522TERMINAL%2522%252C%2522shellId%2522%253A%2522clgf53qdd0010g0i3cey03oke%2522%252C%2522key%2522%253A%2522clgf52ztc008e356ixn6ferp2%2522%252C%2522isMinimized%2522%253Afalse%257D%255D%257D%257D%252C%2522currentSpace%2522%253A%2522clgf51ety000x356ilg8ae35d%2522%252C%2522spacesOrder%2522%253A%255B%2522clgf51ety000x356ilg8ae35d%2522%255D%252C%2522hideCodeEditor%2522%253Afalse%257D

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

@Amirko28 Amirko28 added the bug Issue was opened via the bug report template. label Apr 13, 2023
@github-actions github-actions bot added the area: app App directory (appDir: true) label Apr 13, 2023
@icyJoseph
Copy link
Contributor

If you run the node REPL, in node 18+, and write fetch("/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 😅

@Amirko28
Copy link
Author

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"

@icyJoseph
Copy link
Contributor

icyJoseph commented Apr 13, 2023

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.

@Amirko28
Copy link
Author

Amirko28 commented Apr 13, 2023

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 :)

@Amirko28
Copy link
Author

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')

@negar-75
Copy link

negar-75 commented May 15, 2023

I found this solution and it works fine:

async function getData() {
  const res = await import("../api/top-rated/route");  <---- this is the location of my api file

  return await (await res.GET()).json();
}

@antoineol
Copy link

antoineol commented May 19, 2023

Similar use case here, I try to fetch an asset in the public directory (a huge CSV) in the React code. It works on the client, but fails when running the code on the server.

My understanding is that it would require to write server-specific code (if server, use server fetch, else on the client, use client fetch), although we are fetching the same resource on the same path in both cases. Isn't it against the spirit of simplifying the SSR to make it just work, by running the same code on client and server?

I didn't want to write the domain in the config for that use case, so I used the await import() trick, which works (with webpack configured). But it doesn't seem as clean as the ability to just fetch it 😄

Thanks!

@jackblackCH
Copy link

Confirm: Within a page aka server component fetching a local api endpoint returns a: Error [TypeError]: Failed to parse URL...

@Than-DE
Copy link

Than-DE commented Jun 1, 2023

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.

import { headers } from "next/headers";

const fetchData = async (host: string) => {
  const res = await fetch(`http://${host}/api/quizzes`);
  return res.json();
};

export default async function Dashboard() {
  const host = headers().get("host");
  const data = await fetchData(host!);

  return <div>{JSON.stringify(data)}</div>;
}

@icyJoseph
Copy link
Contributor

The problem is still that you trigger a new round trip, out to the internet and back to the server itself. Just fetch the data from the root source in the Server Component.

@wc-ab
Copy link

wc-ab commented Jun 1, 2023

I found this solution and it works fine:

async function getData() {
  const res = await import("../api/top-rated/route");  <---- this is the location of my api file

  return await (await res.GET()).json();
}

How do you pass query params in this case?

@angelcgdev
Copy link

any update about it? I have found the same solution but methods needs request and param but they are sended by url

I found this solution and it works fine:

async function getData() {
  const res = await import("../api/top-rated/route");  <---- this is the location of my api file

  return await (await res.GET()).json();
}

How do you pass query params in this case?

@ruskid
Copy link

ruskid commented Jun 18, 2023

Why is this issue marked as closed?

A local API endpoint fetching another local API endpoint is broken for some reason. 500 error response

Sorry, not an issue. Just make sure the localhost you are calling is either public or there is a correct secret passed via headers

TrustfulDev added a commit to TrustfulDev/airsoft-armory that referenced this issue Jun 19, 2023
@zgotsch
Copy link

zgotsch commented Jun 28, 2023

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).

@usamaali777
Copy link

any update about it? I have found the same solution but methods needs request and param but they are sended by url

I found this solution and it works fine:

async function getData() {
  const res = await import("../api/top-rated/route");  <---- this is the location of my api file

  return await (await res.GET()).json();
}

How do you pass query params in this case?

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;
}

@pasindu-pr
Copy link

Any update for this?

@boazdev
Copy link

boazdev commented Jul 11, 2023

Following

@bmadone
Copy link

bmadone commented Jul 14, 2023

Update?

@mohanas-scg
Copy link

mohanas-scg commented Jul 16, 2023

async function getAllPosts(){
    const host = headers().get("host");
    const protocal = process?.env.NODE_ENV==="development"?"http":"https"
    let res = await fetch(`${protocal}://${host}/api`, { cache: "no-store" });
    let posts = res.json();
    return posts;
}

guys try this.

@danicunhac
Copy link

    const host = headers().get("host");

What if you're not using Server Components and still getting the Failed to parse URL error?

@mohanas-scg
Copy link

    const host = headers().get("host");

What if you're not using Server Components and still getting the Failed to parse URL error?

if you are not using a server component you can directly call with a relative URL

fetch('/api');

@fracuci
Copy link

fracuci commented Jul 27, 2023

I think it may help those who are looking for this error on pages marked as 'use client'.

In my case I have this folder structure:

|-- app/
  |-- weather/
    |-- page.js
    |-- api/
        |-- cities/
          |-- route.js

I was unable to fetch from /weather/page.js vs /weather/api/cities until I switched from:

  fetch('/weather/api/cities')

to

fetch('/weather/api/cities', {method: 'GET'})

@tonoli
Copy link

tonoli commented Aug 10, 2023

I had the same problem you just need to create a util function getbaseurl that you concat in the beginning of your fetch request:

export function getBaseUrl() {
  if (typeof window !== 'undefined') return '';
  const vc = process.env.VERCEL_URL;
  if (vc) return `https://${vc}`;
  return 'http://localhost:8081';
}

@github-actions
Copy link
Contributor

This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 25, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: app App directory (appDir: true) bug Issue was opened via the bug report template. locked
Projects
None yet
Development

No branches or pull requests