Next.js Discord

Discord Forum

How to show server errors using services & hooks

Answered
Arthur posted this in #help-forum
Open in Discord
I would like to show server side errors into my frontend
Answered by B33fb0n3
inside the network tab are those things visible and yes, these things can be seen and modified
View full answer

168 Replies

In dev I can see the errors, but once I built it, there is like a standard error message;
An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error
simple answer you dont, the only way would be you to try catch the errors and send them with plain text
Why isnt there a good solution for this
because it can be a huge security issue
the amount of server side libs that throw errors tend to include the secret keys and so
As api keys?
also its only the custom errors that user care about, like what would they do with random errors anyway?
They aren't random
Our authentication and all CRUD operations are made through the external API
So when a user provided incrorect data from using an CRUD operation we send an API error message back to the frontend
and can you use a try catch on wherever it throws?
on the server side
Yes
and then you return the error as text
The problem is

our error looks something like this:

{
  "error": {
    "message": "The error msg",
    "details": [{"key": "asasdasasd", "message": "asasdasdasd"}]
  }
}
`
But since the Error class only allows a error message and not anything else,
we cannot show the details
I tried making a custom error class which inherited the Error class but nextjs convert this error to just the Error class again
@Arthur Our authentication and all CRUD operations are made through the external API
So you have an endpoint (nextjs endpoint as route handler or server action) that the client is calling and this endpoint then will call your external api. Did I understood that right?
If you are saying that we're using the nextjs API then no
Now I am confused. How do you call your external api? You might also want to share some code as well

„…when the user provides incorrect data…“

The only ways I know where the user can attach data are via server actions and route handlers. Maybe I am missing something right now. So please clarify
Yes lemme show some code
So I have in the services folder an axios instance like this:

import axios from 'axios';

export const instance = axios.create({
  baseURL: process.env.BASE_URL,
  headers: {
    Authorization: `Basic ${process.env.BASIC_AUTH}`,
  },
  validateStatus: (status) => status >= 200 && status <= 504,
});
This is the service (using 'use server')
Then we use the tanstack useMutation hook
As you can see I still tried onError here, but we never are receiving an actual error
@Arthur Click to see attachment
wow, that are a lot of wrappers. When the external backend is already finished, why you are building another backend (wrapper -> the 'use server' server action) around it?
Then how would you implement an external API?
@Arthur Then how would you implement an external API?
using fetch:
const handleSave = (values: UpdateWooUser) => {
  console.log(values);
  const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/users/woo/${wooUserId}/`, {
  method: "POST",
  body: JSON.stringify(values)
});

  const json = await res.json()
  // work with data or error.
}
@Arthur because I would need to call the route ofc
I think a backend is made to be called
lmao
This is why we use an axios instance
We have like 40+ routes that will be called
@B33fb0n3 well... we wouldn't: https://www.adios-axios.com/
Okay well, it will be the same for fetch. Because I dont want to manually have to add an try catch for all my routes
But that package ky looks cool tho.
@Arthur Okay well, it will be the same for fetch. Because I dont want to manually have to add an try catch for all my routes
why not doing it like this:
const handleSave = (values: UpdateWooUser) => {
  console.log(values);
  const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/users/woo/${wooUserId}/`, {
  method: "POST",
  body: JSON.stringify(values)
}).catch(handleExternalAPIError);

  const json = await res.json()
  // work with data or error.
}

As you can see it's just one function more
@riský does fetch api really error that much.. like it doesnt throw error on 500, maybe on network error but not sure
hm yea, he need to try that. Catching the error should work, but maybe there need to be some adjustments
@Arthur This would implicate that the api call can be seen in the frontend right? When using services it isn't possible correct?
it is yea. Your current call (to your nextjs server action "updateWooUser") is also visible right now
@Arthur Yea but they dont know the actual route
ok? Why is this important? Server actions are still accessable endpoints. So it's like a normal route handler, just with the dynamic genertaed path
@B33fb0n3 ok? Why is this important? Server actions are still accessable endpoints. So it's like a normal route handler, just with the dynamic genertaed path
Because they are not allowed to know the API infrastructure, that is strictly made for the application
Server actions are still accessable endpoints
What do you mean by this?
@Arthur > Server actions are still accessable endpoints What do you mean by this?
a server action is still accessible by everyone. So I can directly call this server action (endpoint) without your application ever needed. And yes, like that you expose your external api as well. Huge security vulnability if you ask me, ...
Because I kinda dont believe this tbh
@Arthur Do you have an sample of this?
Well.. there is a reason for a "security"-section inside the docs:
https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#security

You should treat Server Actions as you would public-facing API endpoints, and ensure that the user is authorized to perform the action.

There was a discussion about that topic some time ago. You might also want to read it: https://discord.com/channels/752553802359505017/752647196419031042/1262745292177145950

And if you need to know how you really secure your server actions/endpoints, you should read this: https://nextjs.org/blog/security-nextjs-server-components-actions
@B33fb0n3 okay thanks for addressing these issues
In the discussion you stated [this package](https://next-safe-action.dev/)
Can those hooks that are made be in the src/hooks folder?
I did, yea. The next-safe-action is a package that can be used to validate and confirm user input. The "safeActionClient" can be created in any file you want that can export this specific function
@Arthur And this allows me to have the server side errors intom the frontend iirc
I think it's a very good package to validate and confirm user input. At the end, it's also a server action. In your case I don't think it's needed. I would suggest to do it like this: https://nextjs-forum.com/post/1286568311584329760#message-1286584264044843020
and it is lacking and has too many features we dont use
This package looks small and easy to use
We also have schemas in the frontend so we can directly use it
@Arthur Well I think it is tbh, because we are using tanstack now
yea, in my opinion also just another wrapper, that can do so much more, but you don't need them. And that's fine. I like to use: react query (for clientside fetching) and next-safe-actions (for clientside mutations).

No need for more stuff around it
Im just gonna try out next-safe-actions to make sure it's what I need and want
If not idk what I should do tbh
Keep in mind, that next-safe-action WONT solve this issue: https://nextjs-forum.com/post/1286568311584329760#message-1286586287909896232 (<--- click)
@Arthur this will solve that users cannot call the computed id?
when using your fetch directly, you can get rid of 2 (unused layers):
1. useMutation from react query (just a wrapper)
2. server action from nextjs (just a wrapper)

So your efficiency will raise and beyond that you archive the same functionallity you have right now. Execpt: you retrieve the full error details as well and can handle them (either in a reusable function or in each fetch)
@B33fb0n3 when using your fetch directly, you can get rid of 2 (unused layers): 1. useMutation from react query (just a wrapper) 2. server action from nextjs (just a wrapper) So your efficiency will raise and beyond that you archive the same functionallity you have right now. Execpt: you retrieve the full error details as well and can handle them (either in a reusable function or in each fetch)
so changing from axios to fetch allows me to show server side errors (including custom errors) and remove server actions.

Another important thing. Some fetch calls will be used in multiple files. So wouldn't it be good to still have those server actions?
@Arthur so changing from axios to fetch allows me to show server side errors (including custom errors) and remove server actions. Another important thing. Some fetch calls will be used in multiple files. So wouldn't it be good to still have those server actions?
removing the server action allows that, yea. When having the same call in multiple places it makes sense to build a function for that (not a server action in this case). This function can be reused in mutliple files. And think clearly about whether it is not yet another wrapper (in this case a wrapper function) that simply passes the parameters to the function. If this is the case, it is probably just an unnecessary wrapper
Alr. I am going to test somethings and let you know what I came up with
When will your test be finished?
@B33fb0n3 When will your test be finished?
can you also do a refetch with the next-safe-action? or is this not possible?
Because then I probably just could use the react-query again with fetch (I think, because this also allows me to show server side errors right? If so, what is the benefit of next-safe-action)
@Arthur can you also do a refetch with the next-safe-action? or is this not possible?
next-safe-action (or server actions in general) are made for client side mutations. They can fetch stuff, but there are some disadvantages with them. So don't use next-safe-action (or server actions) to fetch data
I am receiving an weird error with react-query @B33fb0n3
my mutation (on top of the file 'use server')
page.tsx
using v5.53.2
@Arthur my mutation (on top of the file 'use server')
useMutation is a clientside method. So iirc these can only be used inside a client component. Also your env variables are not exposed right now. You can expose them using NEXT_PUBLIC_... if needed
@Arthur solved?
sure
@Arthur sure
which message solved your initial question the most?
tbh
My issue is never solved
I wanted to use custom errors
But that is not possible
but an other programmer is working on it, so I kinda stopped thinking about it
@Arthur But that is not possible
with the things that we had in this chat he is able to show the client a custom error and above that save a lot of resources with less wrappers
right?
code used:
@Arthur receiving an network error
the api call is never made
looks like next sends a OPTIONS request first:
I have nothing for this
@Arthur code used:
can you console.log the "res"? And also fix the headers? Then share the results
@B33fb0n3 can you console.log the "res"? And also fix the headers? Then share the results
header issue is an eslint problem so not really an issue
res does not log anything it looks like
it does a preflight request it looks like, but kinda dont know how to disable that
But this is horrible
You can just see the base url and the API Key
@Arthur res does not log anything it looks like
there must be in any case anything inside the request. Else you won't even call the function. And I guess you call the function... so there MUST be something inside the "res"
well it didnt show a console message @B33fb0n3
@B33fb0n3 I just tried it again
there is no console log
maybe because no try catch?
idk
@Arthur Click to see attachment
can you give them some names like console.log("request: ", res) so that we know what's the request and what's the data and maybe what are other things and so on? That would help to find the correct problem
oke 2 sec
@B33fb0n3
thanks. Give this one also a label (see attached). Can you add a catch to your fetch? Like:
await fetch(...).catch((e: any) => e);

I guess the res throws an error and that error won't continue the function execution (no console.log after that) and the error is never logged (because useMutation)
@B33fb0n3
@Arthur Click to see attachment
looks like what I mentioned [here](https://nextjs-forum.com/post/1286568311584329760#message-1288063096182538272) is exactly the case.

It also looks like the request wasnt successful. The request itself also looks like it was create, so check the network tab for this specific request and see, what status code the backend dropped and why that happend
A 401 error sounds to me more like an authentication error than a cors error
it was never a cors error
What the cors issue is that I am sending a PUT request. PUT request is not a simple request, thus making a pre-flight request (e.g. OPTIONS)
@Arthur looks like next sends a OPTIONS request first:
I do not as: I give this
But I guess I need to return a 200, with the header Access-Control-Allow-Headers: *
@Arthur I do not as: I give this
answer the options correctly and the cors issue will be gone
@B33fb0n3 answer the options correctly and the cors issue will be gone
But this is not maintainable for me
Because now the base url & api key of the api are public right
they are, yes. So your public backend should act like a public backend and allow requests from your origin as well
So cannot do this
I would need to change a LOT for this to work
you said it's an external backend (https://nextjs-forum.com/post/1286568311584329760#message-1286581640528920640) so when you can't access them from anywhere else, it looks like a very unuseable backend. Even if you send it thought a nextjs server (for example via server action or route handler) it would be publically accesable

Btw, if you are on localhost right now, it won't send cors header with it. You can still send them using [an extension](https://chromewebstore.google.com/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf)
Even if you send it thought a nextjs server (for example via server action or route handler) it would be publically accesable
So I can access the .env variables in the browser? Then show me
@B33fb0n3 yes, you already doing it
yea with this
but it was processn.env.BASE_URL
those aren't accessible
And they will stay like that because this theory unfortunately does not work how I would like it to work
@Arthur those aren't accessible
you are right. env variables, that don't begin with NEXT_PUBLIC are not accessable to the browser. Having multiple wrappers (serverside & clientside) just to make a request seems uneffective to me.

Also when you do it via server actions/route handlers these points are publicly available as well. So at the end your external API is publicly available, and should be, else you can't work with that

To get back to the initial issue (showing the client the plain error everytime, what still isn't recommended) you should create a correct error handler that redirected the error though all the wrapper back to your client. You see, that it is also at this part very ineffective and unmaintable, but it would work.

From the things that I know right now (user want to update user data) I would suggest (to keep efficiency and maintainability) to create the fetch call on your client so that the client can handle the (plain) error
Also when you do it via server actions/route handlers these points are publicly available as well
How would an end-user execute those? Using network tab or something?
To get back to the initial issue (showing the client the plain error everytime, what still isn't recommended) you should create a correct error handler that redirected the error though all the wrapper back to your client. You see, that it is also at this part very ineffective and unmaintable, but it would work.
Harder to maintain I agree, but still the only option for now
Answer
@B33fb0n3 inside the network tab are those things visible and yes, these things can be seen and modified
I just tested it, and you are correct, you can just spam it. So this is not maintainable and would require me to update the back-end...

This is what I want to do now:
1. Remove services and only work with 'use client' hooks
2. Add rate limits on every endpoint based on api key.
3. Allow OPTIONS to be send so that the not so simple request can be sent from the front-end.
- This pre-flight request must have some sort of api key (is that possible?)
@B33fb0n3 ?
for sure, thanks for the help
@Arthur for sure, thanks for the help
which message helped you the most?
idk
@Arthur idk
haha now you need to help me 🤣
lmao
happy to help