Reputation: 1272
I'm creating a Next.js app served by a Node (Express) server which pulls in data through requests to an API. I keep my request endpoints in a separate api
file:
const apiBase = 'http://www.example.com'
export default {
news: apiBase + '/news/'
// other endpoints
}
Then I do my requests in getInitialProps
, and do conditional rendering based on whether the request gives an error or not:
static async getInitialProps( { query: { slug } } ) {
const news = await asyncReq( api.news + slug )
return { news, error: news.status }
}
render() {
return this.props.error ? <Error /> : <News />
}
asyncReq
is a helper function that looks like this:
export const asyncReq = endpoint => {
return
fetch( endpoint )
.then( res => { return res.ok ? res.json() : res } )
}
This all works fine both when the request is successful and when I get 404
or 500
errors. But suppose I intentionally use a wrong endpoint:
const apiBase = 'http://www.example.com'
export default {
news: wrongApiBase + '/news/'
// other endpoints
}
In this case, Node gives me the following error because wrongApiBase
is undefined:
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 498)
which is what it should do, but the error causes the page to never get loaded. What am I supposed to do to handle the error? My idea was to chain a catch
statement to asyncReq
, but I'm not sure what I should return from it that I can then use in getInitialProps
. I tried returning false
but nothing changes, the page just doesn't load.
export const asyncReq = endpoint => {
return
fetch( endpoint )
.then( res => { return res.ok ? res.json() : res } )
.catch( err => { // What should I return here? )
}
+++ UPDATE +++
As it turns out, there was an issue with the error I was producing. Like I said, I was using a wrong variable name (wrongBaseApi
) to trigger an error, which caused Node to never serve the page. In hindsight, it makes sense, as it's an error with Node code and not with the incoming request.
By using the right variable but assigning it a wrong value (an actually wrong API base, such as http://dahfjkdahfds.com
, which is a not a Node error but an error with the request), I was able to make the solution of using a try/catch block offered by @iKoala and @DEVCNN work. So my code became:
static async getInitialProps( { query: { slug } } ) {
const news = await asyncReq( api.news + slug )
return { news }
}
render() {
// this.props.news.data
// this.props.news.error
}
and
export async function asyncReq( endpoint ) {
try {
const res = await fetch( endpoint )
return {
data: res.ok ? await res.json().then( val => { return val } ) : null,
error: res.ok ? false : res.status
}
} catch( error ) {
return { error }
}
}
Upvotes: 1
Views: 4066
Reputation: 622
Let's say your getInitialProps calls asyncReq which in turn calls throwError method. Now if throwError method throws an error, you can catch it with the catch block in getInitialProps. So you should always put a catch block in the function that starts the function chain. To diagnose errors better, you should put the catch block in each function. But as a general rule, catch block in the first function that you call is a must.
getInitialProps = async function( { query: { slug } } ) {
try{
const news = await asyncReq( 'http://localhost:3000/' + slug )
return { news, error: news.status }
}
catch(err){
console.log('err:', err);
}
}
const throwError = () => {
throw 'New Error'
}
const asyncReq = endpoint => {
throwError();
return
fetch( endpoint )
.then( res => { return res.ok ? res.json() : res } )
}
Upvotes: 0
Reputation: 7303
Since it's NodeJS, I would use process.on()
(instead of window.addEventListener()
) with the unhandledRejection
event:
process.on("unhandledRejection", (err, promise) => {
console.log(`Unhandled rejection (promise: ${promise}, reason: ${err})`);
});
Upvotes: 0
Reputation: 1005
Not a good approach but we can achieve by adding an event listener on the root node
window.addEventListener('unhandledrejection', rj => {
this.setState({hasError: true})
})
something like this.
Upvotes: 0
Reputation: 880
I think you have to handle the error thrown from asyncReq:
static async getInitialProps( { query: { slug } } ) {
try {
const news = await asyncReq( api.news + slug )
return { news, error: news.status }
} catch (err) {
// any error, including http error 40x and 50x
return { news: null, error: err };
}
}
Upvotes: 2