How to use Socket.io with Next.js, Express and TypeScript (ES6 import instead of require statements)
Tags: javascript, tutorial, nextjs, typescript
Published: 2021-01-14 17:41:55

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](https://github.com/vercel/next.js/tree/canary/examples/custom-server-express) and [custom TypeScript Server example](https://github.com/vercel/next.js/blob/canary/examples/custom-server-typescript/server/index.ts) 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:

```javascript
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):
```bash
npm install -s express @types/express socket-io
```

The code you've been waiting for:

```typescript
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!

<img width="70%" style="width:70%" src="https://media.giphy.com/media/xUPOqo6E1XvWXwlCyQ/giphy.gif">