How To: Execute Node.js Code on Next.js Server Startup

How To: Execute Node.js Code on Next.js Server Startup

The Good, The Bad, and The Ugly... Solutions

·

4 min read

Problem

Imagine there is a function (foo) provided by a third-party package (awesome-pkg) that needs to be called when the Next.js server is starting up and this foo function is using Node.js specific module so it cannot be called directly on the client side because that will lead to errors related to Module Not Found.

So, what are some of the ways through which this foo function can be called when the Next.js server is starting up? Let's have a look at them in the next section.

Possible Solutions

1) Setup Custom Server (The Ugly)

One way of achieving this is by setting up a custom server in Next.js and calling the foo function there when the server is starting up. For example:

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

const port = parseInt(process.env.PORT || '3000', 10)
const url = `http://localhost:${port}`
const dev = process.env.NODE_ENV !== 'production'
const environment = dev ? 'development' : process.env.NODE_ENV
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  foo()

  createServer((req, res) => {
    const parsedUrl = parse(req.url!, true)
    handle(req, res, parsedUrl)
  }).listen(port)

  console.log(`> Server listening at ${url} as ${environment}`)
})

But setting up a custom server has some drawbacks that should be considered before deciding to use it. One of the main disadvantages is that it can remove some of the default optimizations provided by Next.js. For instance, serverless functions and Automatic Static Optimization (ASO) might not be available in a custom server, which can lead to decreased performance and slower load times.

Furthermore, setting up a custom server can lead to additional complexity and make it error-prone resulting in a less robust and less maintainable server.

2) Expose API Endpoint (The Bad)

Another way to accomplish this is by creating a new API endpoint and calling the foo function inside the endpoint's handler like below:

// src/pages/api/bar/index.js
const { foo } = require('awesome-pkg');

export default function handler(req, res) {
    foo()
    // rest of the code here...
}

Then, make a call to this new endpoint inside the _app.js page's useEffect block:

// src/pages/_app.js
export default function App(props) {
    useEffect(() => {
        apiCall("/bar")
    }, [])
    // rest of the code here...
}

However, this approach is more of a workaround and it won't exactly be calling the foo function at the Next.js server startup. Another problem with this implementation is that it can result in multiple API calls, which can be problematic if a large number of users are using the application. To avoid this issue, some additional logic will need to be added to ensure that the foo function is only called once.

3) Using next.config.js (The Good)

The most preferred way to execute the foo function only when the Next.js server's starting up is by writing the logic in the next.config.js file. However, since the next config can be loaded in different lifecycles/phases of Next.js, it's important to specifically target the phase when the server is starting up. To achieve this, the constants provided by the Next.js team in next/constants (defined here) can be used which will make the next.config.js file look something like this:

// next.config.js
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants')

module.exports = (phase, { defaultConfig }) => {
  if (phase === PHASE_DEVELOPMENT_SERVER) {
    foo()
  }

  const nextConfig = {
    // config options here...
  }
  return nextConfig
}

This technique will ensure that the foo function runs only when the Next.js server (development server in this case) is starting up, without making unnecessary API calls or losing optimizations.

Conclusion

In this article, we explored various ways to call a third-party package function during Next.js server startup. We started by discussing the common error of module not found that can occur when trying to call a function on the client side that uses Node.js specific modules. We then looked at three possible solutions to this problem.

The first solution was to set up a custom server using Next.js, but this can remove important performance optimizations provided by Next.js and require more work in setting up the server.

The second solution was to expose an API endpoint and call the function inside the endpoint's handler. However, this solution could result in multiple API calls, which may not be ideal for large-scale applications.

Finally, we discussed the third solution of writing the logic in the next.config.js file to ensure that the code runs only during the Next.js server's startup phase. This approach can avoid unnecessary API calls and performance issues and ensure that the code runs only when the server is starting up.