Mousse
Let us play with bubbles
A typed Node.js framework built on top of µWebSockets, for HTTP, WebSockets and Server-Sent Events.
pnpm add @moussejs/core import { Mousse } from '@moussejs/core';
new Mousse()
.get('/hello', () => 'Hello World !')
.get('/users/:id', (c) => ({ id: c.param('id') }))
.post('/echo', (c) => c.body())
.ws('/chat', (c) => {
c.onMessage((ws, message) => { ws.send(message); });
})
.sse('/events', (c) => c.send('welcome'))
.listen(3000);Routes, parameters, composable routers. Return a value or answer yourself. Read more
import { Mousse, Router, HTTPError } from '@moussejs/core';
const users = new Router()
.use((c) => {
if (!c.getHeader('authorization'))
return c.status(401).respond(); // nothing else runs
})
.get('/:id', (c) => {
if (c.param('id') === '0')
throw new HTTPError(404, 'No such user');
return { id: c.param('id') }; // sent as json
});
new Mousse()
.use('/users', users)
.setDefaultHandler({ handle: (c) => c.status(404).respond('Nothing here') })
.listen(3000);Each stage can end the request, and nothing after it runs. What is cheap to refuse is refused first : a client over its limit never costs you a schema validation, and your authentication runs before a body gets parsed.
How requests are handledMousse runs within measurement noise of raw µWebSockets.js, while adding routing, middlewares, validation and the whole Context API.
| Framework | Compared | req/s | avg | p99 |
|---|---|---|---|---|
| Mousse | 41 085 | 2.16 ms | 5 ms | |
| µWebSockets.js (raw) | 39 277 | 2.22 ms | 5 ms | |
| node:http (raw) | 36 576 | 2.23 ms | 5 ms | |
| koa | 31 352 | 2.68 ms | 5 ms | |
| fastify | 30 286 | 2.72 ms | 7 ms | |
| hono | 26 095 | 3.35 ms | 8 ms | |
| express | 19 884 | 4.50 ms | 9 ms |
100 connections for 10 seconds with autocannon, Node 24, same machine
for all. Single runs : numbers vary with hardware, and differences under about 5% are not significant.
Run pnpm bench to reproduce on yours.
Request body, response, route params and context extensions, inferred from your schemas.
Body parser, serializer, logger, error handlers and rate limiter, per route or per router.
A self-styled HTML site or standard OpenAPI 3.1, generated from the same schemas.
router.test() fires real requests through a single ephemeral instance, no port to manage.
Body limits, trusted proxies, graceful stop, response hooks for your logs and metrics.
WebSocket and SSE sends queue when a client is slow, instead of piling up in memory.
µWebSockets.js ships prebuilt binaries only. Mousse runs on Node.js 22, 24 and 26, on glibc Linux (x64, arm64),
macOS and Windows. Alpine (musl) and odd Node versions fail at import : use an image such as node:24-slim.
µWebSockets.js is installed from GitHub rather than npm, so strict package managers may need to allow it.