60

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('http://localhost:3000/api/data')
  const users = await response.json()
  return { users }
};

export default Index;
3
  • 1
    have you tried to await getData()? Commented Mar 14, 2020 at 18:38
  • Hi Alessio, I try whit the await and the alert message disappear! but the data returns 4 time now... Commented Mar 14, 2020 at 21:39
  • You need to add a return statement before getData(), e.g. return getData().then(...).catch(...) Commented Jul 26, 2022 at 18:09

10 Answers 10

46

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)
      });
  });
};
Sign up to request clarification or add additional context in comments.

4 Comments

Code snipped in the answer might throw UnhandledPromiseRejectionWarning if there is an error. In the catch block you should do: res.status(405).end(); return resolve()
Returning a promise from an async function is redundant. Better to use await on getData, ditch all the nesting, and return whatever values you please rather than resolveing.
I totally aggree that returning an explicit Promise is not always necessary in in an async function. But in my opinion there is nothing useful to return if we're using await. The thing here is that NextJS only wants some return value from that function it doesn't matter what it is. Returning something like data.someValue might be confusing here.
I had the same error. I forgott to add await in front of an async function inside my api default function.
15

For 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.

2 Comments

Not sure about this, .json returns void, and isn't async, so it wouldn't be much different from a plain return anyway would it?
changed .send({message: 'not found'}) to .send('not found') for it to work
13
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();
  }
}

2 Comments

.end() fixed it - thanks
Why is it better to use the async flow instead of .then().catch()? Don't they behave the same effectively?
8

You 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:
https://nextjs.org/docs/api-routes/request-helpers#custom-config

2 Comments

Thank you, this is the answer I was looking for. I am using express on the API routes with nextjs and awaiting a promise was resolving the request too soon.
Is there a solution to implement this kind of config for every api in one place?
2

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();
    });
};

Comments

1
export default async function handler(req, res) {
  return new Promise(async (resolve) => {
    switch (req.method) {
      case "GET": {
        try {
          let response = await fetch(
            "https://jsonplaceholder.typicode.com/todos/1"
          );
          res.status(200).send(await response.json());
          return resolve();
        } catch (error) {
          res.status(500).end();
          return resolve();
        }
      }
    }
    res.status(405).end();
    return resolve();
  });
}

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
1

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();

Comments

0

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")
  })

1 Comment

This doesn't solve the issue. The warning still persists.
0

Not 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.

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
-1

It'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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.