Home > Back-end >  Best practice to call an API in next.js and avoid CORS errors
Best practice to call an API in next.js and avoid CORS errors

Time:04-02

I am at early stages setting up a next.js application, I only had experience using react so far.

I setup docker with a frontend app (next.js) on localhost:3000 and a backend app (node.js/express) on localhost:5000. They both work.

Now I am trying to call an express endpoint from the frontend, what I am doing is:

const registerUser = async event => {
        event.preventDefault()
        
        const res = await fetch(
            process.env.NEXT_PUBLIC_SERVER   '/user/signup',
            {
                body: JSON.stringify({
                    username: event.target.name.value,
                    email: event.target.email.value,
                    password: event.target.password.value
                }),
                headers: {
                    'Content-Type': 'application/json'
                },
                method: 'POST'
            }
        )

        result = await res.json()
    }

and I am getting an error saying

Access to fetch at 'http://localhost:5000/user/signup' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

just a note: the endpoint works as expected using Postman.

I made some research and I find a few resources saying I should call an internal next.js endpoint (pages/api), and from there call my api. Is this the best practice with next.js? In react I just use to call the api directly.

Other than just how to solve this, I would like to know what's the best practice in this case? Thanks.

CodePudding user response:

If you have separate servers for frontend and backend (for example, next.js and express) that cannot listen on the same port, there are two broad alternatives:

Either the browser loads the frontend from one server and makes API requests to the other server

next.js <-- browser --> express

This requires the backend app to set CORS headers, for example, using cors and the statement

app.use(cors({origin: "host of next.js", ...}));

Or the browser makes all requests to the port of next.js, and this forwards all API requests to the other server

browser --> next.js --> express

No CORS is necessary in this case, but API requests take more hops than before. So it is simplicity vs performance (like so often).

CodePudding user response:

First of all, are you sure you need an Express BE? The power of Next.js relies in its serverless approach, most of times, unless you have a very complex BE, you can do everything with serverless functions.

If you really need to have a separate express server for your Next application remember that you will lose some important Next features:

Before deciding to use a custom server, please keep in mind that it should only be used when the integrated router of Next.js can't meet your app requirements. A custom server will remove important performance optimizations, like serverless functions and Automatic Static Optimization.

Usually to address the CORS issues in dev environment, since you need FE to run on a different PORT from BE to have Hot Reload, when you use React the best approach is the proxy approach, you can just add an entry to package.json on the React project,

   "proxy": "http://localhost:5000" (if your server runs on PORT 5000)

Source: https://create-react-app.dev/docs/proxying-api-requests-in-development/

This way all the http traffic is going to be redirected on port 5000 and will reach your Express server, while keeping having hot reload features and your client files running on port 3000.

By the way, that's the case if you have a standard React FE and a custom Express BE, if you are using NextJS even with a custom Express Server, you need to create the server and to connect it using Next:

    / server.js
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const hostname = 'localhost'
const port = 3000
// when using middleware `hostname` and `port` must be provided below
const app = next({ dev, hostname, port })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  createServer(async (req, res) => {
    try {
      // Be sure to pass `true` as the second argument to `url.parse`.
      // This tells it to parse the query portion of the URL.
      const parsedUrl = parse(req.url, true)
      const { pathname, query } = parsedUrl

      if (pathname === '/a') {
        await app.render(req, res, '/a', query)
      } else if (pathname === '/b') {
        await app.render(req, res, '/b', query)
      } else {
        await handle(req, res, parsedUrl)
      }
    } catch (err) {
      console.error('Error occurred handling', req.url, err)
      res.statusCode = 500
      res.end('internal server error')
    }
  }).listen(port, (err) => {
    if (err) throw err
    console.log(`> Ready on http://${hostname}:${port}`)
  })
})

source: https://nextjs.org/docs/advanced-features/custom-server

Again, I suggest you to deeply evaluate if you really need a custom express server for your app, because most of times you don't, and development experience is much smoother in a serverless environment!

  • Related