TypeSafe WebSocket events.
Report Bug
·
Request Feature
Table of Contents
- Easy Event Subscription & Emission
- Bi-directional Communication
- Schema Validation
- works with zod, yup and many other validation libraries.
- Middleware Support
- Clean & modular structure
If not done already, install a schema validator. This example uses Zod
but you can use any validator using the StandardSchema
. You can find the list of featured compatible libraries here.
npm install zod
Add tswsrpc
to your project:
npm install tswsrpc@latest
Before setting up the server and client, define the events they will handle. The event registry ensures structured communication by defining valid message types.
server.ts
import { tswsrpc } from "tswsrpc";
import { z } from "zod";
export const serverEvents = tswsrpc.$eventRegistry({
message: tswsrpc.$event({
type: z.string(),
}),
});
client.ts
import { client } from "tswsrpc/client";
export const clientEvents = client.$eventRegistry({
"nested.ping": client.$event({}),
});
The WebSocket server listens for incoming connections and handles registered events.
server.ts
import type { clientEvents } from "./client"; // Import as type
const server = await tswsrpc<typeof clientEvents>()({
server: {
port: 8000,
},
events: serverEvents,
});
server.on.message((ctx) => {
console.log(ctx.data);
});
server.emit.nested.ping();
The WebSocket client connects to the server and listens for events.
client.ts
import type { serverEvents } from "./server"; // Import as type
const socket = client<typeof serverEvents>()({
address: "ws://localhost:8000",
events: clientEvents,
});
socket.on.nested.ping(() => {
console.log("PING");
socket.emit.message("PONG");
});
This setup establishes a bidirectional messaging system with minimal configuration.
-
The server emits a
nested.ping
event. -
The client receives the event, logs
"PING"
, and responds withmessage
. -
The server receives the
message
event and logs"PONG"
.
Middlewares allow you to preprocess, validate, and modify incoming event data before execution. You can use a single middleware or an array of middlewares for tasks like authentication and data transformation.
In the example below, the middleware validates an access token before processing the event:
export const events = tswsrpc.$eventRegistry({
// or client.$eventRegistry
message: tswsrpc.$event({
// or client.$event
type: z.object({
accessToken: z.string(),
}),
use: async (ctx, _error) => {
// Or an array of middlewares
const isValid = await validateAccessToken(ctx.data?.accessToken);
if (!isValid) {
return ctx.error("UNAUTHORIZED");
// or throw ctx.error("UNAUTHORIZED");
}
return; // or return modified data.
},
}),
});
server.on.message((ctx, error) => {
if (error) {
// Handle error
}
const isValid = await validateAccessToken(ctx.data?.accessToken);
if (!isValid) {
return ctx.error("UNAUTHORIZED");
// or throw ctx.error("UNAUTHORIZED");
}
});
Distributed under the MIT License. See LICENSE.md for more information.