@staffetta/server
v0.2.0
Published
Server side of the staffetta speedtest: stream primitives, a Web-standard fetch handler and a Node http adapter. Framework-agnostic.
Maintainers
Readme
@staffetta/server
Server side of the staffetta speedtest: stream
primitives, a Web-standard fetch handler and a Node http adapter. Framework-agnostic —
no third-party measurement servers, you mount three endpoints on your own backend and measure
the path your users actually use.
Install
npm install @staffetta/serverFetch-based runtimes (Hono, Next.js, Bun, Deno, Workers…)
import {createSpeedtestFetchHandler} from '@staffetta/server'
const speedtest = createSpeedtestFetchHandler()
// Hono
app.all('/speedtest/*', c => speedtest(c.req.raw))
// Next.js — app/speedtest/[[...slug]]/route.ts
export const GET = speedtest
export const POST = speedtestNode http / Express
import {createServer} from 'node:http'
import {createSpeedtestNodeListener} from '@staffetta/server/node'
createServer(createSpeedtestNodeListener()).listen(8080)
// Express
app.use(createSpeedtestNodeListener())Express / NestJS / Fastify middleware
createSpeedtestNodeMiddleware is the pass-through variant of the listener: requests outside
the three protocol endpoints fall through to next(), so it composes with the rest of the
application without claiming a route prefix. Register it globally (mounting it under a
sub-path would strip the prefix the URL is matched against).
import {createSpeedtestNodeMiddleware} from '@staffetta/server/node'
// Express
app.use(createSpeedtestNodeMiddleware())
// NestJS — main.ts (Express adapter, or Fastify via @fastify/middie)
const app = await NestFactory.create(AppModule)
app.use(createSpeedtestNodeMiddleware())The middleware handles the endpoints before the framework's routing and body parsing, which is what you want on the measured path: no interceptor, guard or JSON body parser adds latency to ping or buffers the upload stream.
Options
createSpeedtestFetchHandler({
basePath: '/speedtest', // path prefix of the three endpoints
maxSizeBytes: 209_715_200, // per-transfer cap (200 MiB)
authorize: (request, phase) => checkToken(request), // download/upload only; ping stays anonymous
})authorize never runs for ping — the ping endpoint is anonymous by design, so the client
measures pure network RTT instead of auth + session time. For anything stronger, wrap the
handler with your own middleware.
Raw primitives
If you'd rather wire the endpoints into your own controllers (NestJS, Fastify, anything):
import {createDownloadStream, consumeUploadStream} from '@staffetta/server'
createDownloadStream(sizeBytes) // ReadableStream of incompressible bytes
await consumeUploadStream(body, {maxBytes}) // → {bytesReceived, serverElapsedMs}The endpoints implement an open protocol — any client speaking it can measure this server,
and the @staffetta/client engine can
measure any server implementing it. Protocol spec and methodology:
github.com/staffetta/staffetta.
