Hello,
I am just starting with next.js and the examples provided do not deal with http error codes.
Lets say I have a page: /blog/:slug
if the slug does not exists, it just returns http status code 200 instead of 404.
As I understand, one needs to call most of the apollo queries in getInitialProps() to be able to return 404 error code.
So the entire idea of apollo queries living near the component that needs them goes out of the window with next.js
What is the best way to return the correct http status codes while having queries live in the components?
Or do I need to call query twice? Ince in getInitialProps() and then in the component itself?
Thank you
One way to handle this is in the catch of getDataFromTree: https://github.com/zeit/next.js/blob/dbe28e1471d381a517c236d2c6c07e56542cf406/examples/with-apollo/lib/with-apollo-client.js#L37
Here you could inspect the errors, and if any of them (take a look at error.graphQLErrors) indicate 404 not found (thats up to your backend how you handle it, it could throw a specific error code, or the resource you want could be null). Then you could do a res.statusCode = 404.
There is probably many more ways to handle this, but thats one take at least. I find error handling/404 with GraphQL kind of tricky, since a query might contain multiple queries, and some might fulfil, while others might throw errors.
thank you @jonespen,
I am familiar with apollo error handling. I was just hoping for a solution where I do not need to query twice the same thing while generating page on the server. Once in getInitialProps and then in HOC/query component.
Your proposed solution is novel, but it will not work for me, since api will just return null, without any error.
The perfect solution would be to be able to include NotFound component in the render, which would result in http code 404, so there would be no need for getInitialProps
Ah, i see. This is not really documented, but I have used the error code ENOENT for throwing 404 in render functions.
Something like this:
// keep this wherever
function notFoundError (page) {
const err = new Error(`Cannot find page: ${page}`)
err.code = 'ENOENT'
return err
}
// call this in render if something is missing
throw new Error(notFoundError("pagename"))
Your code was giving me error500, but after these changes, it is working.
But you cannot send error message, it will be default next.js error404
// keep this wherever
function notFoundError () {
const err = new Error(`this will not show up anywhere`)
err.code = 'ENOENT'
return err
}
// call this in render if something is missing
throw notFoundError()
I ended up doing something like:
https://github.com/zeit/next.js/blob/9320d9f006164f2e997982ce76e80122049ccb3c/examples/with-next-routes/pages/blog.js#L9-L17
@timneutkens @oliviertassinari In the case of using redux or redux sagas this becomes complicated because the result is not yet fetched after getInitialProps finishes.
import { fetchPost } from 'action/posts';
class Post extends Component {
static async getInitialProps ({ query, res }) {
this.props.dispatch(fetchPost(query.slug));
}
}
I saw somewhere, something like this was proposed
import { fetchPost } from 'action/posts';
class Post extends Component {
static async getInitialProps ({ query, res }) {
const post = fetchPost(query.slug);
this.props.dispatch(post);
// somehow wait here until `post.promise` resolves
const result = await post.promise;
// (this does look a bit weird coz we want asynchronicity by the use of dispatch but also want to wait)
if (!result) ctx.res.statusCode = 404;
}
}
I am not happy with both the above approaches. How about introducing a new function, something like preRender(ctx, props, state) where we can set the statusCode and also return extra props to render?
import { fetchPost } from 'action/posts';
class Post extends Component {
static async getInitialProps ({ query, res }) {
this.props.dispatch(fetchPost(query.slug));
}
preRender = (ctx, props, state) => {
if (!props.post) ctx.res.statusCode = 404;
// return { extra: 'Not found msg' } can also return more props to render
}
}
because the result is not yet fetched after getInitialProps finishes.
This the reason we invented getInitialProps in the first place. To fetch asynchronous data before rendering. preRender as you're showing is 100% the same as getInitialProps 馃槄
Right, yea that makes sense!
The only thing you'll have to figure out is how to cleanly await asynchronous dispatches.
@zsolt-dev Thanks for documenting this as I've just come across the same problem.
From my understanding GraphQL intends null to be returned for those parts of the graph data wasn't available rather than erring the whole response. Very different to the way 404 behaves.
As Apollo queries aren't run inside the getInitialProps method using the documented behaviour of next.js isn't an option.
The undocumented approach, throwing an error with code 'ENOENT', works as explained.
@oliviertassinari I assume this approach is intended to be internal to Next.js and hence isn't documented and is without guarantee upon upgrading version?
@timneutkens Any ideas here? Seems like a valid issue. If you use parameterized routing with the apollo example, there's currently no way to handle 404 errors. Using @jonespen suggestion doesn't work unfortunately.
Alright, here's a workaround that works on the server _and_ client.
import Error from 'next/error'
export const throw404 = () => {
if (process.browser) {
return <Error statusCode={404} />
}
const e = new Error()
e.code = 'ENOENT'
throw e
}
Replied here: https://github.com/zeit/next.js/issues/4452#issuecomment-450642052
Most helpful comment
Ah, i see. This is not really documented, but I have used the error code
ENOENTfor throwing 404 in render functions.Something like this: