PHASE 2 — Topic 7: Setting Up a Custom Node.js Server Alongside Next.js for Socket.IO

We understood why Socket.IO needs a persistent server. Now let's actually build it. By the end of this post, your project will run through a custom server instead of the default next dev process.

Step 1: Install the Required Packages

We will use tsx to run our TypeScript server directly, without a separate compile step. From your project root, run:


    npm install socket.io
    npm install --save-dev tsx @types/node cross-env

We are only installing socket.io for now (the server-side package). We will install socket.io-client in the next post, when we build the client side.

Step 2: Create the Custom Server File

Create a new file called server.ts at the root of your project, at the same level as package.json (outside the src folder).


    import { createServer } from "http";
    import { parse } from "url";
    import next from "next";
    import { Server } from "socket.io";

    const dev = process.env.NODE_ENV !== "production";
    const hostname = "localhost";
    const port = parseInt(process.env.PORT || "3000", 10);

    const app = next({ dev, hostname, port });
    const handle = app.getRequestHandler();

    app.prepare().then(() => {
        const httpServer = createServer((req, res) => {
            const parsedUrl = parse(req.url!, true);
            handle(req, res, parsedUrl);
        });

        const io = new Server(httpServer, {
            path: "/api/socket",
        });

        io.on("connection", (socket) => {
            console.log("A client connected:", socket.id);

            socket.on("disconnect", () => {
                console.log("Client disconnected:", socket.id);
            });
        });

        httpServer.listen(port, () => {
            console.log(`Server ready on http://${hostname}:${port}`);
        });
    });

Let's break down what each part is doing:

  • next({ dev, hostname, port }) creates the Next.js app instance, the same one that normally runs behind next dev
  • handle is the function that lets Next.js process every normal page and route request
  • createServer(...) creates one plain Node.js HTTP server, and every incoming request is handed off to Next.js through handle
  • new Server(httpServer, ...) attaches Socket.IO to that exact same HTTP server, instead of creating a separate one
  • The path: "/api/socket" option tells Socket.IO which URL path to listen on for its own connections, so it does not clash with your normal Next.js routes
  • io.on("connection", ...) runs every time a new client connects, and we currently just log it

Step 3: Update Your package.json Scripts

Open package.json and replace the existing scripts section with this:


  "scripts": {
    "dev": "tsx watch server.ts",
    "build": "next build",
    "start": "cross-env NODE_ENV=production tsx server.ts",
    "lint": "eslint"
  }

Here is what changed:

  • dev now runs server.ts using tsx watch, which restarts the server automatically whenever you save a file, similar to how next dev behaved before
  • start runs the same server file, but with NODE_ENV=production, so Next.js knows to serve the optimized production build instead of development mode
  • build stays exactly the same, since building the Next.js app is unrelated to which server runs it afterward

Step 4: Run the Custom Server

Stop any previous next dev process that might still be running, then start the project again with:


    npm run dev

You should see this in your terminal:


    Server ready on http://localhost:3000

Open http://localhost:3000 in your browser. Your Next.js app should load exactly as before. Nothing looks different on the surface, but under the hood, your app is now running through the custom server we just built, with Socket.IO already attached and listening.

Step 5: Confirm Socket.IO Is Actually Running

Right now, nothing in our frontend tries to connect yet, so you will not see any connection logs. That is expected, since we have not written any client-side code. We are simply confirming the server starts correctly and is ready to accept connections once we do.

A Note on next build

One important detail: running next build will still work fine, since it only builds the Next.js application itself. But you must always start the app afterward using npm run start, which runs through server.ts. Running the plain next start command directly will not include Socket.IO at all, since that command boots the default Next.js server, not our custom one.

Summary

  • We installed socket.io and tsx, then created a server.ts file at the project root
  • This file creates one plain HTTP server, hands normal requests to Next.js, and attaches Socket.IO to the same server on a dedicated path
  • We updated package.json so both dev and start run through this custom server instead of the default Next.js server
  • The app currently starts correctly with Socket.IO attached, but nothing connects to it yet, since we have not written any client-side code

In the next post, we will install socket.io-client, add proper TypeScript types, and connect our React frontend to this server for the first time.

No comments:

Post a Comment

PHASE 2 — Topic 7: Setting Up a Custom Node.js Server Alongside Next.js for Socket.IO

We understood why Socket.IO needs a persistent server. Now let's actually build it. By the end of this post, your project will run throu...