Next.js Discord

Discord Forum

Failed to parse URL

Answered
Spectacled bear posted this in #help-forum
Open in Discord
Spectacled bearOP
Hi everyone, I can't solve an issue I am having. I have a "getTokens" function that currently runs server side.
I want to be sure that this can be imported only in client components, to avoid adding the base url in the fetch call.
So I've tried to add the import client-only at the top of the file.
But no matter what I do this always run in the server side. Even though I'm sure it is getting imported only from server side components.
I know I can't call my own endpoints from a server component but this is clearly being imported only by clients, otherwise it would not even compile. I've also tried to use "use client" but that doesn't work either.
import "client-only"

export async function getTokens(): Promise<Map<ChainId, Map<Address, Token>>> {
  const tokensResponse = await fetch("/api/tokens")
  if (tokensResponse.status >= 400) {
    throw Error(
      `lifi getTokens request http error code: ${tokensResponse.status}`,
    )
  }

  // rest of the code
  return tokens
}

The error I'm receiving is:
Error fetching tokens [TypeError: Failed to parse URL from /api/tokens] {
  [cause]: TypeError: Invalid URL
      at <unknown> (.next/server/chunks/1893.js:4:32774)
      at <unknown> (.next/server/chunks/1893.js:22:24552)
      at <unknown> (.next/server/chunks/1893.js:22:20037)
      at a.with (.next/server/chunks/1893.js:22:43460)
      at a.with (.next/server/chunks/1893.js:22:44549) {
    code: 'ERR_INVALID_URL',
    input: '/api/tokens'
  }
}
Answered by alfonsüs ardani
try commenting that code
View full answer

51 Replies

@Spectacled bear Hi everyone, I can't solve an issue I am having. I have a "getTokens" function that currently runs server side. I want to be sure that this can be imported only in client components, to avoid adding the base url in the fetch call. So I've tried to add the import client-only at the top of the file. But no matter what I do this always run in the server side. Even though I'm sure it is getting imported only from server side components. I know I can't call my own endpoints from a server component but this is clearly being imported only by clients, otherwise it would not even compile. I've also tried to use "use client" but that doesn't work either. import "client-only" export async function getTokens(): Promise<Map<ChainId, Map<Address, Token>>> { const tokensResponse = await fetch("/api/tokens") if (tokensResponse.status >= 400) { throw Error( `lifi getTokens request http error code: ${tokensResponse.status}`, ) } // rest of the code return tokens } The error I'm receiving is: Error fetching tokens [TypeError: Failed to parse URL from /api/tokens] { [cause]: TypeError: Invalid URL at <unknown> (.next/server/chunks/1893.js:4:32774) at <unknown> (.next/server/chunks/1893.js:22:24552) at <unknown> (.next/server/chunks/1893.js:22:20037) at a.with (.next/server/chunks/1893.js:22:43460) at a.with (.next/server/chunks/1893.js:22:44549) { code: 'ERR_INVALID_URL', input: '/api/tokens' } }
Hi, thanks for reaching out to help-forum. theres a little bit of discrepancies here.

when you make a function, you can't programmatically define if its going to run in server or client.

the thing that determines if a function is going to be run in the server or client is where you import it.

if you import getTokens in the server -> going to be run in the server
if you import getTokens in the client -> going to be run in the client.

so doing 'import "client-only" does not make your function suddenly run in the client. It only prevents usage of being accidentally imported in the server and to be honest, im not sure if its reliable.

Better yet just create 2 function that clearly differentiate usage if its going to be in the client or server.
such as:
- async function getTokens() and
- function useTokens() or authClient.getTokens()

This will make it easier to debug and reason with rather than forcing a function to work in both environment. Not to mention that fetch has completely different behavior in server and client. as you noticed the reason your getTokens function is producing that error is that the url of the fetch parameter needs to be a full url since it is going to be run in the server.

Adding "use client" wont help because it is meant to be a boundary to travel from server components to client environment. not as an escape hatch to make sure your function is only run in the client.

It seems that import "client-only" doesn't work properly here so if i were you i would just not use getTokens at all in the server and create a separate function for either server or client.
Spectacled bearOP
Hi @alfonsüs ardani thank you for your answer. By digging deeper I actually came to the same conclusion. I'm now importing the function only form client side components. Unfortunatelly the problem persists. I will have to debug it further
Spectacled bearOP
server console
I think i know why
can I see the part of your code where you call getTokens?
Spectacled bearOP
Sure. Give me 5
Sorry if I'm not clear.
The fetch function is inside lifi.ts. I'm calling that from getTokens inside tokens.ts. I'm calling this last function from tokensStore (a proxy). I've wrapped the tokensStore around a useTokenBalances and I use this useTokenBAlances from my client component: <TokenList>.
I'm gonna send the code of the component now.
Sorry
I have to modify the message one sec
```ts
paste code here
```

use this while ure at it
Spectacled bearOP
The code is 200 characters too big, can I send it as a file?
Sorry for the inconvenience
sure
Spectacled bearOP
Thanks
or u can just share link to repository
whichever works
@alfonsüs ardani or u can just share link to repository
Spectacled bearOP
Unfortunately I can't. I'm just gonna create a gist! That's better
I've included all the files I've cited before
This has been bugging me for the last 3 days 😄
hmmm
why is this being called in the global scope? :ThinkEyes:
Spectacled bearOP
We want to trigger call the endpoint as soon as the file is imported
try throwing an error in your deepest getToken and see the stack in the server so u know who calls it
wait nevermind
Spectacled bearOP
I've tried to console.trace() but I can just see bundle files so it's unreadable
But in this case If I remove the rendering of my component the error goes away so it must be the one causing it
at this point you just have to make sure the imports of
- getToken
- iife.getToken
- tokensStore
and make sure none of them are being imported in client comps 😭
Answer
i have suspicion that its that line of code
yeah my bad
try commenting the global scope refreshTokens()
@alfonsüs ardani try commenting that code
Spectacled bearOP
It is 100% that line of code
nice
i really dont recommend doing that honestly
Spectacled bearOP
Sorry if I didn't mention it
so.. all good?
why do you need to run refreshtoken as soon as file is imported?
@alfonsüs ardani why do you need to run refreshtoken as soon as file is imported?
Spectacled bearOP
Still can't understand why If I'm importing a file from a client component it runs on the server tbh
because it needs to be checked first in the server
before being bundled to the client
when nextjs wants to check the file for "use client" at the top, it will import the file.. and welp that refreshToken() gets called in the server but then it has invalid path...
reason being is that thats where you "package" client components and functions from server to the client, and also injections such as NEXT_PUBLIC_ environment variables, checking for import tree too so that it knows if its a client bundle or server bundle, etc
Spectacled bearOP
That's more clear now, I'll report this back to my lead and make sure to be clear about this
yeah here in Next.js things are very FP. so everything must go inside a function even if you want it to run as soon as possible
and only then you can build layers on top of the "entry functions" like route handler or page.js layout.ts middleware.ts

layers like data layer or DTO, or even OOP
Spectacled bearOP
This is very interesting, it gets to the core of how nextjs bundles files together and how rendering priority works
Thanks