JWJames Wallis
Cover for How to use Socket.io with Next.js, Express and TypeScript (ES6 import instead of require statements)

How to use Socket.io with Next.js, Express and TypeScript (ES6 import instead of require statements)

14th January 2021

I'm currently working on a TypeScript project that is using Socket.io to communicate between a Next.js frontend and a custom Express server backend.

While setting up Socket.io I struggled to find documentation explaining how you could set up Socket.io in a TypeScript project using the ES6 import syntax rather than require. It was even more difficult to find anything that explained how it should all fit together with Next.js.

And so this post was born...

If you're starting from scratch...

If you want to make a TypeScript/Express custom server Next.js project, mine was created by combining the custom Express Server example and custom TypeScript Server example located in the Next.js repository.

First I created the project using the command npx create-next-app --example custom-server-typescript to create the custom TypeScript server. Then I retrofitted Express into it by looking at the custom Express server example. The resulting server.ts is at the bottom of this post.

Why didn't I follow another example?

Most of the examples I saw online want you to do something like the following:

import express from 'express';

const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);

But I didn't want two or any random require statements in my TypeScript code if I thought they could be avoided.

My server.ts with only ES6 import

The dependencies you need (In addition to Next.js/React/TypeScript):

npm install -s express @types/express socket-io

The code you've been waiting for:

import express, { Express, Request, Response } from 'express';
import * as http from 'http';
import next, { NextApiHandler } from 'next';
import * as socketio from 'socket.io';

const port: number = parseInt(process.env.PORT || '3000', 10);
const dev: boolean = process.env.NODE_ENV !== 'production';
const nextApp = next({ dev });
const nextHandler: NextApiHandler = nextApp.getRequestHandler();

nextApp.prepare().then(async() => {
    const app: Express = express();
    const server: http.Server = http.createServer(app);
    const io: socketio.Server = new socketio.Server();
    io.attach(server);

    app.get('/hello', async (_: Request, res: Response) => {
        res.send('Hello World')
    });

    io.on('connection', (socket: socketio.Socket) => {
        console.log('connection');
        socket.emit('status', 'Hello from Socket.io');

        socket.on('disconnect', () => {
            console.log('client disconnected');
        })
    });

    app.all('*', (req: any, res: any) => nextHandler(req, res));

    server.listen(port, () => {
        console.log(`> Ready on http://localhost:${port}`);
    });
});

server.ts explanation

The main difference between my server.ts and the ones produced by the Next.js examples is the use of the http module to run the server whereas before Express ran it. This is required so that Socket.io can attach to the server once it's setup.

Additional changes:

  • Changed app to be nextApp so that it is clearer that it was a next app, also changed handler to nextHandler for the same reason. In addition, it's the convention to use the app variable with Express.
  • Used http.CreateServer() rather than const server = require("http").Server(app); to create the HTTP server.
  • Used io.attach() to attach to the HTTP server rather than using require e.g. const io = require("socket.io")(server);.

Summary

This post demonstrates how to use Socket.io with a Next.js custom server using ES6 import rather than require.

If this post helped you drop me a reaction! Found something I could improve? Let me know in the comments.

Thanks for reading!

React, comment and follow on