Do you need help with setting up a simple custom server in Next.js? This trick will get you a long way!
For many frameworks it is necessary to create your own server that listens and handles user interactions with your application.
In Next.js, a server is automatically generated 🤯. And, so, for most, there is no need to think about creating your own server. However, sometimes, it might be necessary with a custom server. In particular, due to two main reasons:
- Wanting to run some code before the Next.js application is running (a common issue)
- Creating custom server patterns
The reason why I wanted to create a custom server was due to reason 1. After multiple attempts, I figured that one way to do it was to replicate the server that Next.js generated when building a standalone application. This is a bit “hacky”, but it is a lot easier than many of the other alternatives! In addition, it serves as a baseline for more complicated custom servers 📈
A standalone is just a smaller version of the application you are creating which only contains the necessary parts for running the application. You can create a standalone by adding output: ‘standalone’ to your config:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
};
module.exports = nextConfig;
After having built the application (with yarn build or etc.), a standalone directory will be available in the root of your project. Inside it you will find ‘server.js’ which looks something like this:
process.env.NODE_ENV = 'production'
process.chdir(__dirname)
const NextServer = require('next/dist/server/next-server').default
const http = require('http')
const path = require('path')
// Make sure commands gracefully respect termination signals (e.g. from Docker)
// Allow the graceful termination to be manually configurable
if (!process.env.NEXT_MANUAL_SIG_HANDLE) {
process.on('SIGTERM', () => process.exit(0))
process.on('SIGINT', () => process.exit(0))
}
let handler
const server = http.createServer(async (req, res) => {
try {
await handler(req, res)
} catch (err) {
console.error(err);
res.statusCode = 500
res.end('internal server error')
}
})
const currentPort = parseInt(process.env.PORT, 10) || 3000
server.listen(currentPort, (err) => {
if (err) {
console.error("Failed to start server", err)
process.exit(1)
}
const nextServer = new NextServer({
hostname: 'localhost',
port: currentPort,
dir: path.join(__dirname),
dev: false,
customServer: false,
conf: {"super-duper-long config automatically generated by Next.js"},
})
handler = nextServer.getRequestHandler()
console.log("Listening on port", currentPort)
})
An easy way to create a custom server would be to copy the file above. However, we can't do this as there is a super-duper-long config that we need to get from ‘required-server-files.json' 🤔. My trick for getting this was by using fs and JSON.parse. Hence, I ended up with a server file that looked like this:
process.env.NODE_ENV = 'production';
process.chdir(__dirname);
const NextServer = require('next/dist/server/next-server').default;
const http = require('http');
const path = require('path');
const fs = require('fs');
const { config } = JSON.parse(fs.readFileSync('required-server-files.json'));
// Make sure commands gracefully respect termination signals (e.g. from Docker)
process.on('SIGTERM', () => process.exit(0));
process.on('SIGINT', () => process.exit(0));
let handler;
const server = http.createServer(async (req, res) => {
try {
await handler(req, res);
} catch (err) {
console.error(err);
res.statusCode = 500;
res.end('internal server error');
}
});
const currentPort = parseInt(process.env.PORT, 10) || 3000;
server.listen(currentPort, (err) => {
if (err) {
console.error('Failed to start server', err);
process.exit(1);
}
const addr = server.address();
const nextServer = new NextServer({
hostname: 'localhost',
port: currentPort,
dir: path.join(__dirname),
dev: false,
conf: config,
});
handler = nextServer.getRequestHandler();
});
It is important that the import path of ‘required-server-files.json’ is correct.
Tada! You now have a basic custom server that you can use and that you are free to edit it is as much as you like 👑.
Run your application with your new custom server by using the command below instead of next start:
yarn/npm build
node server.js
That was all for now! Bye 👋