Being in Frontend-Development for the past 7 years, I still remember when the developer had to do the entire heavy lifting just to render a server-side HTML page. From hydrating HTML manually on the custom express server to injecting the global state into it. Even CRA(create-react-app) was never a good solution as it was primarily there to build the client-side apps. So to load all the HTML, CSS, JS, and even assets, one had to install all the loaders add them to the WebPack configuration, and transpile the JSX and ES6 using Babel. If you want to sneak peek at those days, be my guest and visit this repository. Luckily those days are over and we have a boon known as next.js which not only does all the heavy lifting but also ensures the performance.
As an AI fan, I know that with a single ChatGPT/Claude prompt, we can create a custom ES server solution. But I have tried it and the LLMs mess up pretty badly. So, I decided to share a clear-cut solution to fix the problem. Vercel officially has a solution which is in CommonJS. But you cannot enjoy using the latest tech with the CommonJS solution.
Why use a custom server solution?
As we have Next.js middleware in place, why do we even need to set up a custom server? Here are the reasons:
- You get granular control over implementing headers.
- It becomes easy to implement custom Authentication
- Helps in Handling Websockets, WebRTC, and other real-time features
- You might need advanced middleware features that Next.js middleware doesn't support
- When you want to proxy requests
- When you have custom routing requirements for multiple routes
- And much more…
Steps to set up the custom server:
Setting Up Next.js Project
Run the below command to install the Next.js project. After executing the CLI Command, you can select all the default options. Here is the command:
1npx create-next-app@latest next-ts-es-custom-server
Installing all the required dependencies
We would install Express.js and all the dependencies required to run it using Typescript. Here is the CLI command to achieve the same:
1yarn add express ts-node2yarn add -D @types/express @types/node typescript
Creating typescript configuration for the custom server
Create a file tsconfig.server.json
inside the root directory of your project and paste the code below:
1{2 "compilerOptions": {3 "target": "ESNext",4 "module": "ESNext",5 "lib": ["ESNext"],6 "moduleResolution": "node",7 "esModuleInterop": true,8 "strict": true,9 "skipLibCheck": true,10 "resolveJsonModule": true,11 "outDir": "./dist"12 },13 "include": ["src/**/*.ts"],14 "exclude": ["node_modules", ".next"]15 }
Setting up scripts to run the server
Paste the below commands inside package.json
1 "scripts": {2 "dev": "NODE_OPTIONS='--loader ts-node/esm' ts-node --project tsconfig.server.json src/server.ts",3 "build": "next build && tsc -p tsconfig.server.json",4 "start": "node dist/server.js",5 "lint": "next lint"6 }
Conclusion
Hurray! That’s all required to set up a custom server. Now you can use yarn dev
it to run the dev server locally. To create the production build, you can run yarn build
and to start the production server, you can run yarn start
.
In case you are stuck on any step, I have set up a GitHub repository that has everything in place.