@ldtr/nestjs-webtransport
v0.0.8
Published
NestJS module for creating WebTransport servers and gateways.
Downloads
1,247
Maintainers
Readme
NestJS module for creating WebTransport servers and gateways.
I. Installation
npm install @ldtr/nestjs-webtransportII. Usage
First, declare a server:
import { readFile } from "node:fs/promises"
import {
WebTransportServer,
type HttpServerInit,
type WebTransportServerOptionsFactory
} from "@ldtr/nestjs-webtransport"
@WebTransportServer({ name: "main" })
export class MainWebTransportServer implements WebTransportServerOptionsFactory {
async options(): Promise<HttpServerInit> {
const cert = await readFile("./certs/cert.pem", { encoding: "utf-8" })
const privKey = await readFile("./certs/key.pem", { encoding: "utf-8" })
return {
port: 3001,
host: "0.0.0.0",
secret: "change-me",
cert,
privKey,
defaultDatagramsReadableMode: "bytes",
}
}
}Then, declare your gateways:
import {
WtStreamRO,
WtStreamRW,
WebTransportGateway,
type WebTransportGatewayLifecycle
} from "@ldtr/nestjs-webtransport"
@WebTransportGateway({ server: "main", path: "/events" })
export class EventsGateway implements WebTransportGatewayLifecycle {
async onStreamRW(stream: WtStreamRW): Promise<void> {
console.log("New bidirectional stream opened by client (Read/Write).")
await this.readStream(stream,
chunk => console.log(`Received chunk: ${chunk.length} byte(s).`),
async () => {
await stream.write(new TextEncoder().encode("Server received all chunks."))
await stream.closeWritable()
}
)
}
async onStreamRO(stream: WtStreamRO): Promise<void> {
console.log("New unidirectional stream opened by client (Read/Only).")
await this.readStream(stream)
}
private async readStream(
stream: WtStreamRW | WtStreamRO,
onChunk?: (chunk: Uint8Array) => void | Promise<void>,
onClosed?: () => void | Promise<void>
): Promise<void> {
try {
// The stream must be read immediately to consume incoming client data.
for await (const chunk of stream.read())
void onChunk?.(chunk)
// Client closed stream
await onClosed?.()
} catch (error) {
console.error("Failed to read stream:", error)
throw error
}
}
}import {
WtSession,
WebTransportGateway,
type WebTransportRequest,
type WebTransportGatewayLifecycle
} from "@ldtr/nestjs-webtransport"
import { HttpException, HttpStatus } from "@nestjs/common"
@WebTransportGateway({ server: "main", path: "/messages" })
export class MessagesGateway implements WebTransportGatewayLifecycle {
validateRequest(request: WebTransportRequest): void {
// throw HttpException if request should be rejected
}
async onSession(session: WtSession): Promise<void> {
console.log("new WebTransport session")
try {
// Create a unidirectional (Write/Only) stream from the server to the client.
const stream = await session.createStreamWO()
// This stream can be used to send data, but it cannot read data back.
await stream.write(new TextEncoder().encode("Hello from the server."))
await stream.closeWritable()
} catch (error) {
console.error(error)
}
}
}Then, import them into your application with WebTransportModule:
import { WebTransportModule } from "@ldtr/nestjs-webtransport"
import { Module } from "@nestjs/common"
@Module({
imports: [
WebTransportModule],
providers: [
EventsGateway,
MessagesGateway,
MainWebTransportServer]
})
export class AppModule {
}Note: You can define multiple gateways for the same server, as long as each gateway uses a different path.
III. Session and stream data
Gateways are singleton providers in NestJS. Do not store session-specific or stream-specific state in gateway class properties. Instead, use the data object available on sessions and streams.
You can type this data by passing a generic type to WebTransportGatewayLifecycle:
import {
WtSession,
WtStreamRW,
type GatewayData,
type WebTransportGatewayLifecycle
} from "@ldtr/nestjs-webtransport"
type EventsGatewayData = {
session: {
connectedAt: Date }
}
@WebTransportGateway({ server: "main", path: "/events" })
export class EventsGateway implements WebTransportGatewayLifecycle<EventsGatewayData> {
async onSession(session: WtSession<EventsGatewayData>): Promise<void> {
session.data = { connectedAt: new Date() }
}
onStreamRO(stream: WtStreamRO<EventsGatewayData>): void {
const { connectedAt } = stream.session.getDataOrThrow()
}
}Bug tracker
Found a bug? Please report it on the GitHub issue tracker:
