Hosting on Node.js

@pluv/io can be hosted on the Node.js runtime. You can define your handler and websocket server manually if you need more control. If you only need a basic server, you can use createPluvHandler to get started quicky.

Using with Node.js (manual)

Let's step through how we'd put together a real-time API for Node.js.

Install dependencies

# For the server
npm install @pluv/io @pluv/platform-node

# Server peer-dependencies
npm install ws zod

Create PluvIO instance

Define an io (websocket client) instance on the server codebase:

// backend/io.ts

import { createIO } from "@pluv/io";
import { platformNode } from "@pluv/platform-node";

export const io = createIO(platformNode());

export const ioServer = io.server();

Integrate PluvIO with ws

Integrate with ws on your Node.js server.

// backend/server.ts

import type { InferIORoom } from "@pluv/io";
import express from "express";
import Http from "http";
import WebSocket from "ws";
import { ioServer } from "./io";

const PORT = 3000;

const app = express();
const server = Http.createServer(app);
const wsServer = new WebSocket.Server({ server });

const parseRoomId = (url: string): string => {
    /* get room from req.url */
};

const rooms = new Map<string, InferIORoom<typeof ioServer>>();
const getRoom = (roomId: string): InferIORoom<typeof ioServer> => {
    const existing = rooms.get(roomId);

    if (existing) return existing;

    const room = ioServer.createRoom(roomId, {
        onDelete: (event) => {
            rooms.delete(event.room);
        },
    });
    rooms.set(roomId, room);

    return room;
};

wsServer.on("connection", async (ws, req) => {
    const roomId = parseRoomId(req.url);
    const room = getRoom(roomId);

    // Only needed if you have configured authentication
    const token = new URL(req.url).searchParams.get("token");

    await room.register(
        ws,
        // Only needed if you have configured authentication
        { token },
    );
});

server.listen(PORT, () => {
    console.log(`Server is listening on port: ${port}`);
});

createPluvHandler

If you don't need to modify your websocket server or handler too particularly, @pluv/platform-node also provides a function createPluvHandler to create a websocket server and handler more concisely.

import { createIO } from "@pluv/io";
import { createPluvHandler, platformNode } from "@pluv/platform-node";
import bodyParser from "body-parser";
import cors from "cors";
import express from "express";
import Http from "http";
import WS from "ws";
import { Database } from "./database";

const db = new Database(process.env.DATABASE_URL);

const io = createIO(
    platformNode({
        context: () => ({ db }),
    })
);
const ioServer = io.server();

const app = express();
const server = Http.createServer(app);

const Pluv = createPluvHandler({
    // Your PluvServer instance
    io: ioServer,
    // Optional: Specify the base path from which endpoints are defined
    endpoint: "/api/pluv", // defaults to "/api/pluv"
    // Your Http.Server instance
    server,
    // If your PluvIO instance defines authorization, add your authorization
    // logic here. Return a user if authorized, return null or throw an error
    // if not authorized.
    async authorize({ req, room }) {
        return {
            id: "abc123",
            name: "pluvrt",
        };
    },
});

// Create your WS.Server instance, which listens to "connection" events
const wsServer = Pluv.createWsServer();

// Alternatively, define your own websocket server
const wsServer = new WS.Server({ server });

wsServer.on("connection", async (ws, req) => {
    const { matched } = await Pluv.wsHandler(ws, req);

    if (matched) return;

    // didn't match with the Pluv handler, add your own logic
    // ...
});

app.use(bodyParser.json());
app.use(cors({ origin: "*" }));
app.use(Pluv.handler);

server.listen(3000);