One handler. Seven protocols. Zero duplication.
// Define once
server.procedure('users.create')
.input(z.object({ name: z.string(), email: z.string().email() }))
.handler(async (input) => db.users.create({ data: input }))
// Expose everywhere
// ✓ HTTP POST /users.create
// ✓ WebSocket { procedure: 'users.create', payload: {...} }
// ✓ gRPC UsersService.Create()
// ✓ JSON-RPC { method: 'users.create', params: {...} }
// ✓ GraphQL mutation { usersCreate(...) }
// ✓ TCP/UDP raw protocol supportSame validation. Same errors. Same auth. Same metrics. All protocols.
pnpm add raffel zodPrefer another validator? Swap Zod for Yup/Joi/Ajv and register its adapter.
import { createServer, registerValidator, createZodAdapter } from 'raffel'
import { z } from 'zod'
registerValidator(createZodAdapter(z))
const server = createServer({
port: 3000,
websocket: true,
jsonrpc: '/rpc',
})
server
.procedure('hello')
.input(z.object({ name: z.string() }))
.handler(async ({ name }) => ({ message: `Hello, ${name}!` }))
await server.start()# Test all protocols
curl -X POST localhost:3000/hello -d '{"name":"World"}'
wscat -c ws://localhost:3000/ws -x '{"procedure":"hello","payload":{"name":"World"}}'Raffel's docs are the product - dense, example-first, and protocol-accurate.
👉 Full documentation: https://forattini-dev.github.io/raffel
| Path | What you get |
|---|---|
| Quickstart | 5-minute multi-protocol server |
| Core Model | Envelope, Context, handler lifecycle |
| Handlers | Procedures, Streams, Events with real examples |
| Protocols | HTTP/WS/gRPC/JSON-RPC/GraphQL/TCP/UDP mappings |
| Interceptors | Rate limit, retry, timeout, caching, fallback |
| Auth | Bearer/API key/OAuth2/OIDC/Sessions |
| Routing & Discovery | File-based routing, REST Auto-CRUD |
| Observability | Prometheus metrics + OpenTelemetry tracing |
| USD & OpenAPI | Universal docs generated from schemas |
| MCP Server | AI tools, resources, prompts |
| Category | Features |
|---|---|
| Protocols | HTTP • WebSocket • gRPC • JSON-RPC • GraphQL • TCP • UDP |
| Handler Types | Procedures (RPC) • Streams (Server/Client/Bidi) • Events (Pub/Sub) |
| Validation | Zod • Yup • Joi • Ajv • fastest-validator |
| Auth | JWT • API Key • OAuth2 • OIDC • Basic • Session |
| Resilience | Rate Limit • Circuit Breaker • Retry • Timeout • Bulkhead • Fallback |
| Observability | Prometheus Metrics • OpenTelemetry Tracing • Structured Logging |
| Caching | Memory • Redis • S3DB • Read-through • Write-through |
| Real-time | Channels (Pusher-like) • Presence • Broadcasting |
| Documentation | USD (Universal Service Docs) • Auto-generated from schemas |
| DX | Hot Reload • File-based Routing • REST Auto-CRUD |
Every request becomes a normalized Envelope - same processing for all protocols:
interface Envelope {
id: string // Request correlation
procedure: string // Handler name
type: 'request' | 'response' | 'stream:data' | 'event'
payload: unknown // Your data
context: Context // Auth, tracing, deadline
}// Procedures - Request → Response
server.procedure('math.add')
.handler(async ({ a, b }) => ({ result: a + b }))
// Streams - Request → Multiple Responses
server.stream('logs.tail')
.handler(async function* ({ file }) {
for await (const line of readLines(file)) {
yield { line }
}
})
// Events - Fire and Forget with Guarantees
server.event('emails.send')
.delivery('at-least-once')
.handler(async (payload, ctx, ack) => {
await sendEmail(payload)
ack()
})Write middleware once, apply everywhere:
server.use(async (envelope, ctx, next) => {
const start = Date.now()
const result = await next()
console.log(`${envelope.procedure}: ${Date.now() - start}ms`)
return result
})
// Runs for HTTP, WebSocket, gRPC, JSON-RPC, TCP, UDP...Built-in Interceptors
import {
// Auth
createAuthMiddleware,
createBearerStrategy,
createApiKeyStrategy,
// Resilience
createRateLimitInterceptor,
createCircuitBreakerInterceptor,
createRetryInterceptor,
createTimeoutInterceptor,
createBulkheadInterceptor,
createFallbackInterceptor,
// Observability
createMetricsInterceptor,
createTracingInterceptor,
createLoggingInterceptor,
// Caching
createCacheInterceptor,
// Response
createEnvelopeInterceptor,
} from 'raffel'Drop files in folders, get endpoints automatically:
src/
├── http/
│ └── users/
│ ├── get.ts → users.get
│ └── create.ts → users.create
├── streams/
│ └── logs/
│ └── tail.ts → logs.tail
├── rest/
│ └── users.ts → Auto-CRUD
└── channels/
└── chat-room.ts → WebSocket channel
const server = createServer({ port: 3000, discovery: true })Define a schema, get a full REST API:
// src/rest/users.ts
export const schema = z.object({
id: z.string().uuid(),
name: z.string(),
email: z.string().email(),
})
export const adapter = prisma.userGET /users → list
GET /users/:id → get
POST /users → create
PUT /users/:id → update
PATCH /users/:id → patch
DELETE /users/:id → delete
Pusher-like real-time with authentication:
const server = createServer({
websocket: {
channels: {
authorize: async (socketId, channel, ctx) => {
if (channel.startsWith('private-')) {
return ctx.auth?.authenticated ?? false
}
return true
},
},
},
})
// Public channels
server.channels.broadcast('news', 'update', { headline: '...' })
// Private channels
server.channels.broadcast('private-user-123', 'notification', {...})
// Presence channels
const members = server.channels.getMembers('presence-lobby')Throw once, convert automatically per protocol:
import { RaffelError } from 'raffel'
throw new RaffelError('NOT_FOUND', 'User not found', { userId: '123' })
// HTTP → 404 Not Found + JSON body
// JSON-RPC → { error: { code: -32601, message: '...' } }
// gRPC → status: NOT_FOUND (5)
// WebSocket → { type: 'error', code: 'NOT_FOUND' }Raffel includes a complete HTTP toolkit - no extra dependencies needed:
import {
// Server
HttpApp, serve,
// Middleware
cors, compress, secureHeaders, bodyLimit,
basicAuth, bearerAuth, cookieSession, oauth2, oidc,
rateLimitMiddleware, validate,
// Static files
serveStatic, serveStaticS3,
// Responses
success, error, list, created, notFound, validationError,
// Session
createSessionTracker, createRedisSessionStore,
// Utils
getCookie, setCookie, healthCheck,
} from 'raffel/http'Raffel includes an MCP server for AI-powered development:
# Add to Claude Code
claude mcp add raffel npx raffel-mcp
# Or run directly
npx raffel-mcp --category minimal
npx raffel-mcp --category docs,codegen
npx raffel-mcp --transport http --port 3200Available Categories
| Category | Tokens | Tools |
|---|---|---|
minimal |
~2.5K | Essential docs & patterns |
docs |
~3K | Documentation search |
codegen |
~4K | Code generation |
full |
~8K | All 16 tools |
Docs & Reference
raffel_getting_started- Quick start guideraffel_search- Search all documentationraffel_list_interceptors- List interceptors by categoryraffel_get_interceptor- Interceptor details + examplesraffel_list_adapters- List protocol adaptersraffel_get_adapter- Adapter details + protocol mappingraffel_api_patterns- Critical - Correct code patternsraffel_explain_error- Error code explanations
Codegen
raffel_create_server- Generate server boilerplateraffel_create_procedure- Generate RPC endpointsraffel_create_stream- Generate streaming handlersraffel_create_event- Generate event handlersraffel_add_middleware- Add interceptorsraffel_create_module- Generate router modulesraffel_boilerplate- Multi-file project templates
Meta
raffel_version- Version + compatibility info
create_rest_api- Build complete REST APIcreate_realtime_server- WebSocket + channelscreate_grpc_service- gRPC services (unary + streaming)create_microservice- Production-ready serviceadd_authentication- Add JWT/API key authadd_caching- Add caching driversadd_rate_limiting- Add per-route or global limitsadd_observability- Metrics + tracingmigrate_from_express- Convert from Expressmigrate_from_fastify- Convert from Fastifymigrate_from_trpc- Convert from tRPCdebug_middleware- Diagnose interceptor order/issuesoptimize_performance- Perf review + tuning ideas
The MCP server also exposes documentation and boilerplates as resources:
raffel://guide/quickstartraffel://interceptor/{name}raffel://adapter/{name}raffel://pattern/{name}raffel://error/{code}raffel://boilerplate/{template}
The docs go deep on every adapter, interceptor, and design choice.
| Topic | Highlights |
|---|---|
| Quickstart | Multi-protocol in 5 minutes |
| Core Model | Envelope + Context deep dive |
| Handlers | Procedures, Streams, Events |
| Interceptors | Retry, timeout, bulkhead, cache |
| Auth | Bearer/API key/OAuth2/OIDC |
| Routing | Modules, discovery, REST Auto-CRUD |
| USD + OpenAPI | Specs from schemas |
| MCP Server | Tools, resources, prompts |
| Metric | Value |
|---|---|
| Protocols | 7 (HTTP, WS, gRPC, JSON-RPC, GraphQL, TCP, UDP) |
| Interceptors | 20+ built-in |
| Validation Libraries | 5 supported |
| Auth Strategies | 8+ (JWT, API Key, OAuth2, OIDC, Basic, Session, etc.) |
| MCP Tools | 16 |
| MCP Prompts | 13 |
# Clone and run examples
pnpm tsx examples/00-hello-world.ts
pnpm tsx examples/01-rest-api.ts
pnpm tsx examples/02-websocket-server.ts
pnpm tsx examples/03-rpc-server.ts| Feature | Express/Koa | Raffel |
|---|---|---|
| HTTP routing | ✅ | ✅ |
| WebSocket | ❌ separate | ✅ same handlers |
| gRPC | ❌ separate | ✅ same handlers |
| JSON-RPC | ❌ separate | ✅ same handlers |
| GraphQL | ❌ separate | ✅ same handlers |
| Unified validation | ❌ | ✅ one schema |
| Unified errors | ❌ | ✅ auto-converted |
| Unified auth | ❌ | ✅ all protocols |
| Feature | tRPC | Raffel |
|---|---|---|
| Type-safe RPC | ✅ | ✅ |
| HTTP | ✅ | ✅ |
| WebSocket | ✅ | ✅ |
| gRPC | ❌ | ✅ |
| JSON-RPC | ❌ | ✅ |
| TCP/UDP | ❌ | ✅ |
| Channels/Presence | ❌ | ✅ |
| File routing | ❌ | ✅ |
MIT
Documentation · GitHub · npm