I have to make some changes in my NextJS project because my endpoint API doesn't support many calls and I would like to make a refresh from the original data every 3 min.
I implemented API from NextJS: I created a pages/api/data and inside, I make the call to my endpoint, and in my getInitialProps inside index call to data file.
The get works okay, but I have 2 problems:
1: I have an alert message that says:
API resolved without sending a response for /api/data, this may result in stalled requests.
2: It doesn't reload data after 3 minutes... I suppose it is because of the Cache-Control value.
This is my code:
pages/api/data
import { getData } from "../../helper"; export default async function(req, res) { getData() .then(response => { res.statusCode = 200 res.setHeader('Content-Type', 'application/json'); res.setHeader('Cache-Control', 'max-age=180000'); res.end(JSON.stringify(response)) }) .catch(error => { res.json(error); next(); }); }; pages/index
import React, { useState, useEffect } from "react"; import fetch from 'isomorphic-unfetch' const Index = props => { return ( <>Hello World</> ); }; Index.getInitialProps = async ({ res }) => { const response = await fetch(') const users = await response.json() return { users } }; export default Index; 310 Answers
You should return a Promise and resolve/reject it.
Example:
import { getData } from "../../helper"; export default async function(req, res) { return new Promise((resolve, reject) => { getData() .then(response => { res.statusCode = 200 res.setHeader('Content-Type', 'application/json'); res.setHeader('Cache-Control', 'max-age=180000'); res.end(JSON.stringify(response)); resolve(); }) .catch(error => { res.json(error); res.status(405).end(); resolve(); // in case something goes wrong in the catch block (as vijay commented) }); }); }; 4For me it was necessary to return the result, even though the result was given to the client without return:
Before: res.status(405).json({ message: 'Method not allowed.' });
After, error resolved: return res.status(405).send('Method not allowed.');
[EDIT]: corrected to use send instead of json for error return value.
import { getData } from "../../helper"; export default async function (req, res) { try { const response = await getData(); res.statusCode = 200; res.setHeader('Content-Type', 'application/json'); res.setHeader('Cache-Control', 'max-age=180000'); res.end(JSON.stringify(response)); } catch (error) { res.json(error); res.status(405).end(); } } 2You can try adding the following additional export to your "pages/api/xxxx" API page:
// pages/api/xxxx export const config = { api: { externalResolver: true, }, }; You can find more details about following config here:
If you only get this error after using next export but it works fine with next dev or next start then you probably have a CORS issue.
In the browser network tab, is there an OPTIONS call paired with your GET (or whatever)? If so, you need to handle the OPTIONS call and set the CORS headers:
export default async (request: NextApiRequest, response: NextApiResponse<IStudy[]>) => { response.setHeader('Access-Control-Allow-Origin', '*'); response.setHeader('Access-Control-Allow-Headers', '*'); response.setHeader('Access-Control-Allow-Methods', '*'); if (request.method == 'OPTIONS') return response.status(200).end(); // rest of your function starts here... Obviously don't be so free with your headers in a production environment...
You can set these headers everywhere at once in nextjs.config.js like so:
crossOrigin: 'anonymous', headers: () => { return [ { source: '/api/(.*)', headers: [ { key: 'Access-Control-Allow-Origin', value: '*' }, { key: 'Access-Control-Allow-Methods', value: '*' }, { key: 'Access-Control-Allow-Headers', value: '*' }, ], }, ]; }, But in each api route you'll still need to prepend the line
if (request.method == 'OPTIONS') return response.status(200).end(); Here is how to fix the error since you are using async and fetching data, just add await before fetch :
import { getData } from "../../helper"; export default async function(req, res) { await getData() .then(response => { res.statusCode = 200 res.setHeader('Content-Type', 'application/json'); res.setHeader('Cache-Control', 'max-age=180000'); res.end(JSON.stringify(response)) }) .catch(error => { res.json(error); next(); }); }; You can use 'next-connect' library which eliminates the necessity of returning a promise in this scenario. If you like express.js's route->middleware->endpoint pattern this library is what you are looking for. It also provides global error handling out of the box! [next-connect docs]
Example:
import nc from 'next-connect' function onError(err, req, res, next) { logger.log(err); res.status(500).end(err.toString()); // OR: you may want to continue next(); } const handler = nc({ onError }); handler .use((req, res, next) => { if(!req.user){ throw new Error("oh no!"); // or use next next(Error("oh no")); } }) .get((req, res) => { res.end("success") }) 1export default async function handler(req, res) { return new Promise(async (resolve) => { switch (req.method) { case "GET": { try { let response = await fetch( "" ); res.status(200).send(await response.json()); return resolve(); } catch (error) { res.status(500).end(); return resolve(); } } } res.status(405).end(); return resolve(); }); } 1Not directly related to the code sample, but I also got this error using the next APIs.
The issue was a wrong HTTP method that wasn't being accounted for in the handler.
I was calling a get, and the handler was expecting a post.
So just check out for this also.
1It's also good to mention that the res.send() method is a way to send the response and automatically call res.end() under the hood.