raffel 0.1.2 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +314 -346
- package/dist/adapters/index.d.ts +3 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +3 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/s3db/adapter.d.ts.map +1 -1
- package/dist/adapters/s3db/adapter.js +0 -3
- package/dist/adapters/s3db/adapter.js.map +1 -1
- package/dist/adapters/udp.d.ts +83 -0
- package/dist/adapters/udp.d.ts.map +1 -0
- package/dist/adapters/udp.int.test.d.ts +5 -0
- package/dist/adapters/udp.int.test.d.ts.map +1 -0
- package/dist/adapters/udp.int.test.js +397 -0
- package/dist/adapters/udp.int.test.js.map +1 -0
- package/dist/adapters/udp.js +391 -0
- package/dist/adapters/udp.js.map +1 -0
- package/dist/cache/drivers/file.d.ts.map +1 -1
- package/dist/cache/drivers/file.js +13 -1
- package/dist/cache/drivers/file.js.map +1 -1
- package/dist/cache/drivers/memory.d.ts.map +1 -1
- package/dist/cache/drivers/memory.js +1 -0
- package/dist/cache/drivers/memory.js.map +1 -1
- package/dist/cache/types.d.ts +1 -0
- package/dist/cache/types.d.ts.map +1 -1
- package/dist/docs/generators/http-generator.d.ts.map +1 -1
- package/dist/docs/generators/http-generator.js +0 -1
- package/dist/docs/generators/http-generator.js.map +1 -1
- package/dist/graphql/graphql.int.test.d.ts +10 -0
- package/dist/graphql/graphql.int.test.d.ts.map +1 -0
- package/dist/graphql/graphql.int.test.js +698 -0
- package/dist/graphql/graphql.int.test.js.map +1 -0
- package/dist/graphql/schema-generator.d.ts.map +1 -1
- package/dist/graphql/schema-generator.js +20 -7
- package/dist/graphql/schema-generator.js.map +1 -1
- package/dist/http/auth.d.ts.map +1 -1
- package/dist/http/auth.js +15 -1
- package/dist/http/auth.js.map +1 -1
- package/dist/http/http.int.test.d.ts +7 -0
- package/dist/http/http.int.test.d.ts.map +1 -0
- package/dist/http/http.int.test.js +604 -0
- package/dist/http/http.int.test.js.map +1 -0
- package/dist/http/index.d.ts +2 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +2 -0
- package/dist/http/index.js.map +1 -1
- package/dist/http/oauth2.d.ts.map +1 -1
- package/dist/http/oauth2.js +39 -0
- package/dist/http/oauth2.js.map +1 -1
- package/dist/http/oidc.d.ts.map +1 -1
- package/dist/http/oidc.js +9 -1
- package/dist/http/oidc.js.map +1 -1
- package/dist/http/session-redis.d.ts +187 -0
- package/dist/http/session-redis.d.ts.map +1 -0
- package/dist/http/session-redis.int.test.d.ts +8 -0
- package/dist/http/session-redis.int.test.d.ts.map +1 -0
- package/dist/http/session-redis.int.test.js +492 -0
- package/dist/http/session-redis.int.test.js.map +1 -0
- package/dist/http/session-redis.js +320 -0
- package/dist/http/session-redis.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/cli.js +2 -1
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/docs/adapters.d.ts.map +1 -1
- package/dist/mcp/docs/adapters.js +175 -145
- package/dist/mcp/docs/adapters.js.map +1 -1
- package/dist/mcp/docs/interceptors.d.ts +1 -1
- package/dist/mcp/docs/interceptors.d.ts.map +1 -1
- package/dist/mcp/docs/interceptors.js +231 -305
- package/dist/mcp/docs/interceptors.js.map +1 -1
- package/dist/mcp/docs/patterns.d.ts.map +1 -1
- package/dist/mcp/docs/patterns.js +20 -18
- package/dist/mcp/docs/patterns.js.map +1 -1
- package/dist/mcp/docs/quickstart.d.ts +1 -1
- package/dist/mcp/docs/quickstart.d.ts.map +1 -1
- package/dist/mcp/docs/quickstart.js +48 -46
- package/dist/mcp/docs/quickstart.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +6 -7
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/version.d.ts +7 -0
- package/dist/mcp/version.d.ts.map +1 -0
- package/dist/mcp/version.js +20 -0
- package/dist/mcp/version.js.map +1 -0
- package/dist/middleware/auth/oauth2.d.ts +294 -0
- package/dist/middleware/auth/oauth2.d.ts.map +1 -0
- package/dist/middleware/auth/oauth2.int.test.d.ts +2 -0
- package/dist/middleware/auth/oauth2.int.test.d.ts.map +1 -0
- package/dist/middleware/auth/oauth2.int.test.js +714 -0
- package/dist/middleware/auth/oauth2.int.test.js.map +1 -0
- package/dist/middleware/auth/oauth2.js +671 -0
- package/dist/middleware/auth/oauth2.js.map +1 -0
- package/dist/middleware/auth.d.ts +2 -0
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +16 -0
- package/dist/middleware/auth.js.map +1 -1
- package/dist/middleware/index.d.ts +5 -2
- package/dist/middleware/index.d.ts.map +1 -1
- package/dist/middleware/index.js +4 -0
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/interceptors/circuit-breaker.d.ts.map +1 -1
- package/dist/middleware/interceptors/circuit-breaker.js +0 -1
- package/dist/middleware/interceptors/circuit-breaker.js.map +1 -1
- package/dist/middleware/interceptors/envelope.d.ts +176 -0
- package/dist/middleware/interceptors/envelope.d.ts.map +1 -0
- package/dist/middleware/interceptors/envelope.int.test.d.ts +5 -0
- package/dist/middleware/interceptors/envelope.int.test.d.ts.map +1 -0
- package/dist/middleware/interceptors/envelope.int.test.js +409 -0
- package/dist/middleware/interceptors/envelope.int.test.js.map +1 -0
- package/dist/middleware/interceptors/envelope.js +294 -0
- package/dist/middleware/interceptors/envelope.js.map +1 -0
- package/dist/middleware/interceptors/index.d.ts +2 -0
- package/dist/middleware/interceptors/index.d.ts.map +1 -1
- package/dist/middleware/interceptors/index.js +2 -0
- package/dist/middleware/interceptors/index.js.map +1 -1
- package/dist/middleware/types.d.ts +25 -0
- package/dist/middleware/types.d.ts.map +1 -1
- package/dist/rate-limit/drivers/drivers.int.test.d.ts +7 -0
- package/dist/rate-limit/drivers/drivers.int.test.d.ts.map +1 -0
- package/dist/rate-limit/drivers/drivers.int.test.js +466 -0
- package/dist/rate-limit/drivers/drivers.int.test.js.map +1 -0
- package/dist/server/builder.d.ts.map +1 -1
- package/dist/server/builder.int.test.js +41 -0
- package/dist/server/builder.int.test.js.map +1 -1
- package/dist/server/builder.js +72 -15
- package/dist/server/builder.js.map +1 -1
- package/dist/server/channel-utils.d.ts +4 -1
- package/dist/server/channel-utils.d.ts.map +1 -1
- package/dist/server/channel-utils.js +12 -2
- package/dist/server/channel-utils.js.map +1 -1
- package/dist/server/errors.d.ts.map +1 -1
- package/dist/server/errors.js +0 -22
- package/dist/server/errors.js.map +1 -1
- package/dist/server/fs-routes/watcher.js +1 -1
- package/dist/server/fs-routes/watcher.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/types.d.ts +37 -33
- package/dist/server/types.d.ts.map +1 -1
- package/dist/tracing/interceptor.d.ts.map +1 -1
- package/dist/tracing/interceptor.js +4 -5
- package/dist/tracing/interceptor.js.map +1 -1
- package/dist/types/envelope.d.ts +1 -1
- package/dist/types/envelope.d.ts.map +1 -1
- package/dist/types/envelope.js.map +1 -1
- package/dist/types/handlers.d.ts +8 -0
- package/dist/types/handlers.d.ts.map +1 -1
- package/dist/ui/core/index.d.ts +7 -0
- package/dist/ui/core/index.d.ts.map +1 -0
- package/dist/ui/docs/generators/content-types.d.ts +10 -0
- package/dist/ui/docs/generators/content-types.d.ts.map +1 -0
- package/dist/ui/docs/generators/errors-types.d.ts +409 -0
- package/dist/ui/docs/generators/errors-types.d.ts.map +1 -0
- package/dist/ui/docs/generators/errors.d.ts +88 -0
- package/dist/ui/docs/generators/errors.d.ts.map +1 -0
- package/dist/ui/docs/generators/grpc-generator.d.ts +53 -0
- package/dist/ui/docs/generators/grpc-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/http-generator.d.ts +49 -0
- package/dist/ui/docs/generators/http-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/index.d.ts +17 -0
- package/dist/ui/docs/generators/index.d.ts.map +1 -0
- package/dist/ui/docs/generators/jsonrpc-generator.d.ts +53 -0
- package/dist/ui/docs/generators/jsonrpc-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/schema-converter.d.ts +117 -0
- package/dist/ui/docs/generators/schema-converter.d.ts.map +1 -0
- package/dist/ui/docs/generators/streams-generator.d.ts +85 -0
- package/dist/ui/docs/generators/streams-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/tcp-generator.d.ts +133 -0
- package/dist/ui/docs/generators/tcp-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/udp-generator.d.ts +119 -0
- package/dist/ui/docs/generators/udp-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/usd-generator.d.ts +182 -0
- package/dist/ui/docs/generators/usd-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/websocket-generator.d.ts +49 -0
- package/dist/ui/docs/generators/websocket-generator.d.ts.map +1 -0
- package/dist/ui/docs/index.d.ts +31 -0
- package/dist/ui/docs/index.d.ts.map +1 -0
- package/dist/ui/docs/usd-middleware.d.ts +157 -0
- package/dist/ui/docs/usd-middleware.d.ts.map +1 -0
- package/dist/ui/errors/factories.d.ts +142 -0
- package/dist/ui/errors/factories.d.ts.map +1 -0
- package/dist/ui/errors/index.d.ts +9 -0
- package/dist/ui/errors/index.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/index.d.ts +66 -0
- package/dist/ui/server/fs-routes/index.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/loader.d.ts +28 -0
- package/dist/ui/server/fs-routes/loader.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/middleware-processor.d.ts +19 -0
- package/dist/ui/server/fs-routes/middleware-processor.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/resources/index.d.ts +8 -0
- package/dist/ui/server/fs-routes/resources/index.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/resources/loader.d.ts +16 -0
- package/dist/ui/server/fs-routes/resources/loader.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/resources/types.d.ts +256 -0
- package/dist/ui/server/fs-routes/resources/types.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/rest/index.d.ts +8 -0
- package/dist/ui/server/fs-routes/rest/index.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/rest/loader.d.ts +11 -0
- package/dist/ui/server/fs-routes/rest/loader.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/rest/types.d.ts +288 -0
- package/dist/ui/server/fs-routes/rest/types.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/tcp/index.d.ts +8 -0
- package/dist/ui/server/fs-routes/tcp/index.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/tcp/loader.d.ts +15 -0
- package/dist/ui/server/fs-routes/tcp/loader.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/tcp/types.d.ts +215 -0
- package/dist/ui/server/fs-routes/tcp/types.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/types.d.ts +437 -0
- package/dist/ui/server/fs-routes/types.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/udp/index.d.ts +8 -0
- package/dist/ui/server/fs-routes/udp/index.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/udp/loader.d.ts +15 -0
- package/dist/ui/server/fs-routes/udp/loader.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/udp/types.d.ts +164 -0
- package/dist/ui/server/fs-routes/udp/types.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/watcher.d.ts +34 -0
- package/dist/ui/server/fs-routes/watcher.d.ts.map +1 -0
- package/dist/ui/types/envelope.d.ts +1 -1
- package/dist/ui/types/envelope.d.ts.map +1 -1
- package/dist/ui/types/handlers.d.ts +8 -0
- package/dist/ui/types/handlers.d.ts.map +1 -1
- package/dist/ui/usd/builder/document.d.ts.map +1 -1
- package/dist/ui/usd/export/openapi.d.ts.map +1 -1
- package/dist/ui/usd/parser/normalize.d.ts.map +1 -1
- package/dist/ui/usd/spec/types.d.ts +14 -20
- package/dist/ui/usd/spec/types.d.ts.map +1 -1
- package/dist/ui/usd/utils/refs.d.ts.map +1 -1
- package/dist/ui/usd/validator/index.d.ts.map +1 -1
- package/dist/ui/usd/validator/schema.d.ts.map +1 -1
- package/dist/ui/usd/validator/semantic.d.ts.map +1 -1
- package/dist/ui/utils/logger.d.ts +15 -0
- package/dist/ui/utils/logger.d.ts.map +1 -0
- package/dist/usd/builder/document.d.ts.map +1 -1
- package/dist/usd/builder/document.js.map +1 -1
- package/dist/usd/export/openapi.d.ts.map +1 -1
- package/dist/usd/export/openapi.js +2 -4
- package/dist/usd/export/openapi.js.map +1 -1
- package/dist/usd/parser/normalize.d.ts.map +1 -1
- package/dist/usd/parser/normalize.js +0 -1
- package/dist/usd/parser/normalize.js.map +1 -1
- package/dist/usd/usd.int.test.d.ts +10 -0
- package/dist/usd/usd.int.test.d.ts.map +1 -0
- package/dist/usd/usd.int.test.js +719 -0
- package/dist/usd/usd.int.test.js.map +1 -0
- package/dist/usd/utils/refs.d.ts.map +1 -1
- package/dist/usd/validator/index.d.ts.map +1 -1
- package/dist/usd/validator/index.js.map +1 -1
- package/dist/usd/validator/schema.d.ts.map +1 -1
- package/dist/usd/validator/schema.js.map +1 -1
- package/dist/usd/validator/semantic.d.ts.map +1 -1
- package/dist/usd/validator/semantic.js.map +1 -1
- package/package.json +1 -1
- package/dist/middleware/rate-limit.d.ts +0 -105
- package/dist/middleware/rate-limit.d.ts.map +0 -1
- package/dist/middleware/rate-limit.int.test.d.ts +0 -5
- package/dist/middleware/rate-limit.int.test.d.ts.map +0 -1
- package/dist/middleware/rate-limit.int.test.js +0 -350
- package/dist/middleware/rate-limit.int.test.js.map +0 -1
- package/dist/middleware/rate-limit.js +0 -206
- package/dist/middleware/rate-limit.js.map +0 -1
- package/dist/openapi/index.d.ts +0 -9
- package/dist/openapi/index.d.ts.map +0 -1
- package/dist/openapi/index.js +0 -9
- package/dist/openapi/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,485 +1,453 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# Raffel
|
|
4
4
|
|
|
5
|
-
###
|
|
6
|
-
|
|
7
|
-
One handler. Seven protocols. Zero duplication.
|
|
5
|
+
### Build APIs Like Express. Scale Like Nothing Else.
|
|
8
6
|
|
|
9
7
|
[](https://www.npmjs.com/package/raffel)
|
|
10
8
|
[](https://www.typescriptlang.org/)
|
|
11
9
|
[](https://nodejs.org/)
|
|
12
10
|
[](LICENSE)
|
|
13
11
|
|
|
14
|
-
[Documentation](https://forattini-dev.github.io/raffel) · [
|
|
12
|
+
[Quick Start](#quick-start) · [Full Documentation](https://forattini-dev.github.io/raffel) · [Examples](./examples) · [Migration from Express](#migration-from-express)
|
|
15
13
|
|
|
16
14
|
</div>
|
|
17
15
|
|
|
18
16
|
---
|
|
19
17
|
|
|
20
|
-
##
|
|
18
|
+
## If You Know Express, You Know Raffel
|
|
21
19
|
|
|
22
20
|
```typescript
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
21
|
+
import { createServer } from 'raffel'
|
|
22
|
+
|
|
23
|
+
const app = createServer({ port: 3000 })
|
|
24
|
+
|
|
25
|
+
app.get('/users', async () => {
|
|
26
|
+
return db.users.findMany()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
app.get('/users/:id', async ({ id }) => {
|
|
30
|
+
return db.users.findById(id)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
app.post('/users', async (body) => {
|
|
34
|
+
return db.users.create(body)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
await app.start()
|
|
35
38
|
```
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
```bash
|
|
41
|
+
curl http://localhost:3000/users
|
|
42
|
+
curl http://localhost:3000/users/123
|
|
43
|
+
curl -X POST http://localhost:3000/users -d '{"name":"John"}'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**That's it. Familiar, right?**
|
|
38
47
|
|
|
39
48
|
---
|
|
40
49
|
|
|
41
50
|
## Quick Start
|
|
42
51
|
|
|
43
52
|
```bash
|
|
44
|
-
pnpm add raffel
|
|
53
|
+
pnpm add raffel
|
|
45
54
|
```
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
### Hello World
|
|
48
57
|
|
|
49
58
|
```typescript
|
|
50
|
-
import { createServer
|
|
51
|
-
import { z } from 'zod'
|
|
59
|
+
import { createServer } from 'raffel'
|
|
52
60
|
|
|
53
|
-
|
|
61
|
+
const app = createServer({ port: 3000 })
|
|
54
62
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
websocket: true,
|
|
58
|
-
jsonrpc: '/rpc',
|
|
63
|
+
app.get('/hello/:name', async ({ name }) => {
|
|
64
|
+
return { message: `Hello, ${name}!` }
|
|
59
65
|
})
|
|
60
66
|
|
|
61
|
-
|
|
62
|
-
.procedure('hello')
|
|
63
|
-
.input(z.object({ name: z.string() }))
|
|
64
|
-
.handler(async ({ name }) => ({ message: `Hello, ${name}!` }))
|
|
65
|
-
|
|
66
|
-
await server.start()
|
|
67
|
+
await app.start()
|
|
67
68
|
```
|
|
68
69
|
|
|
69
70
|
```bash
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
wscat -c ws://localhost:3000/ws -x '{"procedure":"hello","payload":{"name":"World"}}'
|
|
71
|
+
curl http://localhost:3000/hello/World
|
|
72
|
+
# → {"message":"Hello, World!"}
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
## Documentation (Docsify Deep Dive)
|
|
78
|
-
|
|
79
|
-
**Raffel's docs are the product** - dense, example-first, and protocol-accurate.
|
|
75
|
+
### CRUD API in 30 Seconds
|
|
80
76
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
| Path | What you get |
|
|
84
|
-
|------|--------------|
|
|
85
|
-
| [Quickstart](https://forattini-dev.github.io/raffel/#/quickstart) | 5-minute multi-protocol server |
|
|
86
|
-
| [Core Model](https://forattini-dev.github.io/raffel/#/core-model) | Envelope, Context, handler lifecycle |
|
|
87
|
-
| [Handlers](https://forattini-dev.github.io/raffel/#/handlers/procedures) | Procedures, Streams, Events with real examples |
|
|
88
|
-
| [Protocols](https://forattini-dev.github.io/raffel/#/protocols/http) | HTTP/WS/gRPC/JSON-RPC/GraphQL/TCP/UDP mappings |
|
|
89
|
-
| [Interceptors](https://forattini-dev.github.io/raffel/#/interceptors) | Rate limit, retry, timeout, caching, fallback |
|
|
90
|
-
| [Auth](https://forattini-dev.github.io/raffel/#/auth/overview) | Bearer/API key/OAuth2/OIDC/Sessions |
|
|
91
|
-
| [Routing & Discovery](https://forattini-dev.github.io/raffel/#/file-system-discovery) | File-based routing, REST Auto-CRUD |
|
|
92
|
-
| [Observability](https://forattini-dev.github.io/raffel/#/metrics) | Prometheus metrics + OpenTelemetry tracing |
|
|
93
|
-
| [USD & OpenAPI](https://forattini-dev.github.io/raffel/#/usd) | Universal docs generated from schemas |
|
|
94
|
-
| [MCP Server](https://forattini-dev.github.io/raffel/#/mcp) | AI tools, resources, prompts |
|
|
77
|
+
```typescript
|
|
78
|
+
import { createServer } from 'raffel'
|
|
95
79
|
|
|
96
|
-
|
|
80
|
+
const app = createServer({ port: 3000 })
|
|
97
81
|
|
|
98
|
-
|
|
82
|
+
const users = new Map()
|
|
99
83
|
|
|
100
|
-
|
|
101
|
-
|----------|----------|
|
|
102
|
-
| **Protocols** | HTTP • WebSocket • gRPC • JSON-RPC • GraphQL • TCP • UDP |
|
|
103
|
-
| **Handler Types** | Procedures (RPC) • Streams (Server/Client/Bidi) • Events (Pub/Sub) |
|
|
104
|
-
| **Validation** | Zod • Yup • Joi • Ajv • fastest-validator |
|
|
105
|
-
| **Auth** | JWT • API Key • OAuth2 • OIDC • Basic • Session |
|
|
106
|
-
| **Resilience** | Rate Limit • Circuit Breaker • Retry • Timeout • Bulkhead • Fallback |
|
|
107
|
-
| **Observability** | Prometheus Metrics • OpenTelemetry Tracing • Structured Logging |
|
|
108
|
-
| **Caching** | Memory • Redis • S3DB • Read-through • Write-through |
|
|
109
|
-
| **Real-time** | Channels (Pusher-like) • Presence • Broadcasting |
|
|
110
|
-
| **Documentation** | USD (Universal Service Docs) • Auto-generated from schemas |
|
|
111
|
-
| **DX** | Hot Reload • File-based Routing • REST Auto-CRUD |
|
|
84
|
+
app.get('/users', async () => [...users.values()])
|
|
112
85
|
|
|
113
|
-
|
|
86
|
+
app.get('/users/:id', async ({ id }) => {
|
|
87
|
+
const user = users.get(id)
|
|
88
|
+
if (!user) throw app.errors.notFound('User not found')
|
|
89
|
+
return user
|
|
90
|
+
})
|
|
114
91
|
|
|
115
|
-
|
|
92
|
+
app.post('/users', async (body) => {
|
|
93
|
+
const user = { id: crypto.randomUUID(), ...body }
|
|
94
|
+
users.set(user.id, user)
|
|
95
|
+
return user
|
|
96
|
+
})
|
|
116
97
|
|
|
117
|
-
|
|
98
|
+
app.put('/users/:id', async ({ id, ...body }) => {
|
|
99
|
+
if (!users.has(id)) throw app.errors.notFound('User not found')
|
|
100
|
+
const user = { id, ...body }
|
|
101
|
+
users.set(id, user)
|
|
102
|
+
return user
|
|
103
|
+
})
|
|
118
104
|
|
|
119
|
-
|
|
105
|
+
app.delete('/users/:id', async ({ id }) => {
|
|
106
|
+
if (!users.delete(id)) throw app.errors.notFound('User not found')
|
|
107
|
+
return { success: true }
|
|
108
|
+
})
|
|
120
109
|
|
|
121
|
-
|
|
122
|
-
interface Envelope {
|
|
123
|
-
id: string // Request correlation
|
|
124
|
-
procedure: string // Handler name
|
|
125
|
-
type: 'request' | 'response' | 'stream:data' | 'event'
|
|
126
|
-
payload: unknown // Your data
|
|
127
|
-
context: Context // Auth, tracing, deadline
|
|
128
|
-
}
|
|
110
|
+
await app.start()
|
|
129
111
|
```
|
|
130
112
|
|
|
131
|
-
###
|
|
113
|
+
### Add Validation (with Zod)
|
|
132
114
|
|
|
133
115
|
```typescript
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
.handler(async ({ a, b }) => ({ result: a + b }))
|
|
116
|
+
import { createServer } from 'raffel'
|
|
117
|
+
import { z } from 'zod'
|
|
137
118
|
|
|
138
|
-
|
|
139
|
-
server.stream('logs.tail')
|
|
140
|
-
.handler(async function* ({ file }) {
|
|
141
|
-
for await (const line of readLines(file)) {
|
|
142
|
-
yield { line }
|
|
143
|
-
}
|
|
144
|
-
})
|
|
119
|
+
const app = createServer({ port: 3000 })
|
|
145
120
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
121
|
+
app.post('/users', {
|
|
122
|
+
body: z.object({
|
|
123
|
+
name: z.string().min(2),
|
|
124
|
+
email: z.string().email(),
|
|
125
|
+
}),
|
|
126
|
+
handler: async (body) => {
|
|
127
|
+
return db.users.create(body)
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
await app.start()
|
|
153
132
|
```
|
|
154
133
|
|
|
155
|
-
|
|
134
|
+
Invalid request? Automatic error response:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"error": "VALIDATION_ERROR",
|
|
139
|
+
"message": "Validation failed",
|
|
140
|
+
"details": [
|
|
141
|
+
{ "path": "email", "message": "Invalid email" }
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
```
|
|
156
145
|
|
|
157
|
-
|
|
146
|
+
### Add Middleware
|
|
158
147
|
|
|
159
148
|
```typescript
|
|
160
|
-
|
|
149
|
+
import { createServer } from 'raffel'
|
|
150
|
+
|
|
151
|
+
const app = createServer({ port: 3000 })
|
|
152
|
+
|
|
153
|
+
// Global middleware
|
|
154
|
+
app.use(async (req, next) => {
|
|
161
155
|
const start = Date.now()
|
|
162
156
|
const result = await next()
|
|
163
|
-
console.log(`${
|
|
157
|
+
console.log(`${req.method} ${req.path} - ${Date.now() - start}ms`)
|
|
164
158
|
return result
|
|
165
159
|
})
|
|
166
|
-
|
|
160
|
+
|
|
161
|
+
// Auth middleware
|
|
162
|
+
const requireAuth = async (req, next) => {
|
|
163
|
+
const token = req.headers.authorization?.replace('Bearer ', '')
|
|
164
|
+
if (!token) throw app.errors.unauthorized()
|
|
165
|
+
req.user = await verifyToken(token)
|
|
166
|
+
return next()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
app.get('/profile', requireAuth, async (_, req) => {
|
|
170
|
+
return req.user
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
await app.start()
|
|
167
174
|
```
|
|
168
175
|
|
|
169
|
-
|
|
170
|
-
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Wait, There's More
|
|
179
|
+
|
|
180
|
+
Here's where Raffel gets interesting. **That same API you just wrote? It already works over WebSocket, JSON-RPC, and more.**
|
|
171
181
|
|
|
172
182
|
```typescript
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
createBulkheadInterceptor,
|
|
185
|
-
createFallbackInterceptor,
|
|
186
|
-
|
|
187
|
-
// Observability
|
|
188
|
-
createMetricsInterceptor,
|
|
189
|
-
createTracingInterceptor,
|
|
190
|
-
createLoggingInterceptor,
|
|
191
|
-
|
|
192
|
-
// Caching
|
|
193
|
-
createCacheInterceptor,
|
|
194
|
-
|
|
195
|
-
// Response
|
|
196
|
-
createEnvelopeInterceptor,
|
|
197
|
-
} from 'raffel'
|
|
183
|
+
const app = createServer({
|
|
184
|
+
port: 3000,
|
|
185
|
+
websocket: { path: '/ws' },
|
|
186
|
+
jsonrpc: { path: '/rpc' },
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
app.get('/users/:id', async ({ id }) => {
|
|
190
|
+
return db.users.findById(id)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
await app.start()
|
|
198
194
|
```
|
|
199
195
|
|
|
200
|
-
|
|
196
|
+
**Same handler. Three protocols. Zero extra code.**
|
|
201
197
|
|
|
202
|
-
|
|
198
|
+
```bash
|
|
199
|
+
# HTTP (as usual)
|
|
200
|
+
curl http://localhost:3000/users/123
|
|
203
201
|
|
|
204
|
-
|
|
202
|
+
# WebSocket
|
|
203
|
+
wscat -c ws://localhost:3000/ws
|
|
204
|
+
> {"method":"users.get","params":{"id":"123"}}
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
│ └── users/
|
|
210
|
-
│ ├── get.ts → users.get
|
|
211
|
-
│ └── create.ts → users.create
|
|
212
|
-
├── streams/
|
|
213
|
-
│ └── logs/
|
|
214
|
-
│ └── tail.ts → logs.tail
|
|
215
|
-
├── rest/
|
|
216
|
-
│ └── users.ts → Auto-CRUD
|
|
217
|
-
└── channels/
|
|
218
|
-
└── chat-room.ts → WebSocket channel
|
|
206
|
+
# JSON-RPC
|
|
207
|
+
curl -X POST http://localhost:3000/rpc \
|
|
208
|
+
-d '{"jsonrpc":"2.0","method":"users.get","params":{"id":"123"},"id":1}'
|
|
219
209
|
```
|
|
220
210
|
|
|
221
|
-
|
|
222
|
-
const server = createServer({ port: 3000, discovery: true })
|
|
223
|
-
```
|
|
211
|
+
### Why Does This Matter?
|
|
224
212
|
|
|
225
|
-
|
|
213
|
+
- **Write once** - Same validation, auth, and error handling everywhere
|
|
214
|
+
- **Client choice** - HTTP for REST, WebSocket for real-time, JSON-RPC for internal services
|
|
215
|
+
- **Zero friction** - No adapters, no mappings, no duplicate code
|
|
226
216
|
|
|
227
|
-
|
|
217
|
+
---
|
|
228
218
|
|
|
229
|
-
|
|
230
|
-
// src/rest/users.ts
|
|
231
|
-
export const schema = z.object({
|
|
232
|
-
id: z.string().uuid(),
|
|
233
|
-
name: z.string(),
|
|
234
|
-
email: z.string().email(),
|
|
235
|
-
})
|
|
219
|
+
## The Full Picture
|
|
236
220
|
|
|
237
|
-
|
|
238
|
-
```
|
|
221
|
+
Under the hood, Raffel normalizes all requests into a unified format called an **Envelope**. But you don't need to think about that - it just works.
|
|
239
222
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
223
|
+
| What You Write | What Raffel Exposes |
|
|
224
|
+
|----------------|---------------------|
|
|
225
|
+
| `app.get('/users/:id', handler)` | HTTP GET, WS, JSON-RPC, gRPC, GraphQL |
|
|
226
|
+
| `app.post('/users', handler)` | HTTP POST, WS, JSON-RPC, gRPC, GraphQL |
|
|
227
|
+
| Validation schema | Same validation, all protocols |
|
|
228
|
+
| Auth middleware | Same auth, all protocols |
|
|
229
|
+
| Error handling | Protocol-appropriate errors |
|
|
230
|
+
|
|
231
|
+
### Supported Protocols
|
|
232
|
+
|
|
233
|
+
| Protocol | Status | Use Case |
|
|
234
|
+
|----------|--------|----------|
|
|
235
|
+
| HTTP | Production | REST APIs, webhooks |
|
|
236
|
+
| WebSocket | Production | Real-time, bi-directional |
|
|
237
|
+
| JSON-RPC | Production | Internal services, batch |
|
|
238
|
+
| gRPC | Production | Microservices, high-perf |
|
|
239
|
+
| GraphQL | Production | Flexible queries |
|
|
240
|
+
| TCP | Production | IoT, custom protocols |
|
|
241
|
+
| UDP | Production | Gaming, streaming |
|
|
242
|
+
|
|
243
|
+
---
|
|
248
244
|
|
|
249
|
-
|
|
245
|
+
## Going Deeper: The Procedure API
|
|
250
246
|
|
|
251
|
-
|
|
247
|
+
For power users who want full control, Raffel exposes its native API:
|
|
252
248
|
|
|
253
249
|
```typescript
|
|
250
|
+
import { createServer, registerValidator, createZodAdapter } from 'raffel'
|
|
251
|
+
import { z } from 'zod'
|
|
252
|
+
|
|
253
|
+
registerValidator(createZodAdapter(z))
|
|
254
|
+
|
|
254
255
|
const server = createServer({
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
authorize: async (socketId, channel, ctx) => {
|
|
258
|
-
if (channel.startsWith('private-')) {
|
|
259
|
-
return ctx.auth?.authenticated ?? false
|
|
260
|
-
}
|
|
261
|
-
return true
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
},
|
|
256
|
+
port: 3000,
|
|
257
|
+
websocket: { path: '/ws' },
|
|
265
258
|
})
|
|
266
259
|
|
|
267
|
-
//
|
|
268
|
-
server.
|
|
260
|
+
// Full procedure definition
|
|
261
|
+
server.procedure('users.create')
|
|
262
|
+
.description('Create a new user')
|
|
263
|
+
.input(z.object({
|
|
264
|
+
name: z.string().min(2),
|
|
265
|
+
email: z.string().email(),
|
|
266
|
+
}))
|
|
267
|
+
.output(z.object({
|
|
268
|
+
id: z.string().uuid(),
|
|
269
|
+
name: z.string(),
|
|
270
|
+
email: z.string(),
|
|
271
|
+
}))
|
|
272
|
+
.handler(async (input, ctx) => {
|
|
273
|
+
// ctx has auth, tracing, request metadata
|
|
274
|
+
return db.users.create(input)
|
|
275
|
+
})
|
|
269
276
|
|
|
270
|
-
//
|
|
271
|
-
server.
|
|
277
|
+
// Streaming (server → client)
|
|
278
|
+
server.stream('logs.tail')
|
|
279
|
+
.handler(async function* ({ file }) {
|
|
280
|
+
for await (const line of readLines(file)) {
|
|
281
|
+
yield { line, timestamp: Date.now() }
|
|
282
|
+
}
|
|
283
|
+
})
|
|
272
284
|
|
|
273
|
-
//
|
|
274
|
-
|
|
285
|
+
// Events (fire-and-forget with guarantees)
|
|
286
|
+
server.event('emails.send')
|
|
287
|
+
.delivery('at-least-once')
|
|
288
|
+
.handler(async (payload, ctx, ack) => {
|
|
289
|
+
await sendEmail(payload)
|
|
290
|
+
ack()
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
await server.start()
|
|
275
294
|
```
|
|
276
295
|
|
|
277
|
-
###
|
|
296
|
+
### The Envelope Model
|
|
278
297
|
|
|
279
|
-
|
|
298
|
+
Every request becomes an Envelope:
|
|
280
299
|
|
|
281
300
|
```typescript
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
//
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
// WebSocket → { type: 'error', code: 'NOT_FOUND' }
|
|
301
|
+
interface Envelope {
|
|
302
|
+
id: string // Correlation ID
|
|
303
|
+
procedure: string // Handler name (e.g., "users.create")
|
|
304
|
+
type: 'request' | 'response' | 'stream:data' | 'event'
|
|
305
|
+
payload: unknown // Your data
|
|
306
|
+
context: Context // Auth, tracing, deadline, metadata
|
|
307
|
+
}
|
|
290
308
|
```
|
|
291
309
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
## HTTP Module
|
|
310
|
+
This abstraction is what enables protocol-agnostic handlers. You write business logic once, Raffel handles the protocol translation.
|
|
295
311
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
```typescript
|
|
299
|
-
import {
|
|
300
|
-
// Server
|
|
301
|
-
HttpApp, serve,
|
|
312
|
+
---
|
|
302
313
|
|
|
303
|
-
|
|
304
|
-
cors, compress, secureHeaders, bodyLimit,
|
|
305
|
-
basicAuth, bearerAuth, cookieSession, oauth2, oidc,
|
|
306
|
-
rateLimitMiddleware, validate,
|
|
314
|
+
## Features at a Glance
|
|
307
315
|
|
|
308
|
-
|
|
309
|
-
|
|
316
|
+
| Category | Features |
|
|
317
|
+
|----------|----------|
|
|
318
|
+
| **HTTP** | GET/POST/PUT/PATCH/DELETE, path params, query params, headers |
|
|
319
|
+
| **Validation** | Zod, Yup, Joi, Ajv, fastest-validator |
|
|
320
|
+
| **Auth** | JWT, API Key, OAuth2, OIDC, Basic, Session |
|
|
321
|
+
| **Resilience** | Rate Limit, Circuit Breaker, Retry, Timeout, Bulkhead |
|
|
322
|
+
| **Observability** | Prometheus Metrics, OpenTelemetry Tracing |
|
|
323
|
+
| **Caching** | Memory, Redis, S3, Read-through, Write-through |
|
|
324
|
+
| **Real-time** | WebSocket Channels, Presence, Broadcasting |
|
|
325
|
+
| **Documentation** | Auto-generated OpenAPI/Swagger from schemas |
|
|
326
|
+
| **DX** | Hot Reload, File-based Routing, TypeScript-first |
|
|
310
327
|
|
|
311
|
-
|
|
312
|
-
success, error, list, created, notFound, validationError,
|
|
328
|
+
---
|
|
313
329
|
|
|
314
|
-
|
|
315
|
-
createSessionTracker, createRedisSessionStore,
|
|
330
|
+
## Migration from Express
|
|
316
331
|
|
|
317
|
-
|
|
318
|
-
getCookie, setCookie, healthCheck,
|
|
319
|
-
} from 'raffel/http'
|
|
320
|
-
```
|
|
332
|
+
Already have an Express app? Migration is straightforward:
|
|
321
333
|
|
|
322
|
-
|
|
334
|
+
<table>
|
|
335
|
+
<tr>
|
|
336
|
+
<th>Express</th>
|
|
337
|
+
<th>Raffel</th>
|
|
338
|
+
</tr>
|
|
339
|
+
<tr>
|
|
340
|
+
<td>
|
|
323
341
|
|
|
324
|
-
|
|
342
|
+
```javascript
|
|
343
|
+
const express = require('express')
|
|
344
|
+
const app = express()
|
|
325
345
|
|
|
326
|
-
|
|
346
|
+
app.get('/users/:id', (req, res) => {
|
|
347
|
+
const user = getUser(req.params.id)
|
|
348
|
+
res.json(user)
|
|
349
|
+
})
|
|
327
350
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
351
|
+
app.post('/users', (req, res) => {
|
|
352
|
+
const user = createUser(req.body)
|
|
353
|
+
res.status(201).json(user)
|
|
354
|
+
})
|
|
331
355
|
|
|
332
|
-
|
|
333
|
-
npx raffel-mcp --category minimal
|
|
334
|
-
npx raffel-mcp --category docs,codegen
|
|
335
|
-
npx raffel-mcp --transport http --port 3200
|
|
356
|
+
app.listen(3000)
|
|
336
357
|
```
|
|
337
358
|
|
|
338
|
-
|
|
339
|
-
<
|
|
340
|
-
|
|
341
|
-
| Category | Tokens | Tools |
|
|
342
|
-
|----------|--------|-------|
|
|
343
|
-
| `minimal` | ~2.5K | Essential docs & patterns |
|
|
344
|
-
| `docs` | ~3K | Documentation search |
|
|
345
|
-
| `codegen` | ~4K | Code generation |
|
|
346
|
-
| `full` | ~8K | All 16 tools |
|
|
347
|
-
|
|
348
|
-
</details>
|
|
349
|
-
|
|
350
|
-
### MCP Tools
|
|
351
|
-
|
|
352
|
-
**Docs & Reference**
|
|
353
|
-
- `raffel_getting_started` - Quick start guide
|
|
354
|
-
- `raffel_search` - Search all documentation
|
|
355
|
-
- `raffel_list_interceptors` - List interceptors by category
|
|
356
|
-
- `raffel_get_interceptor` - Interceptor details + examples
|
|
357
|
-
- `raffel_list_adapters` - List protocol adapters
|
|
358
|
-
- `raffel_get_adapter` - Adapter details + protocol mapping
|
|
359
|
-
- `raffel_api_patterns` - **Critical** - Correct code patterns
|
|
360
|
-
- `raffel_explain_error` - Error code explanations
|
|
361
|
-
|
|
362
|
-
**Codegen**
|
|
363
|
-
- `raffel_create_server` - Generate server boilerplate
|
|
364
|
-
- `raffel_create_procedure` - Generate RPC endpoints
|
|
365
|
-
- `raffel_create_stream` - Generate streaming handlers
|
|
366
|
-
- `raffel_create_event` - Generate event handlers
|
|
367
|
-
- `raffel_add_middleware` - Add interceptors
|
|
368
|
-
- `raffel_create_module` - Generate router modules
|
|
369
|
-
- `raffel_boilerplate` - Multi-file project templates
|
|
370
|
-
|
|
371
|
-
**Meta**
|
|
372
|
-
- `raffel_version` - Version + compatibility info
|
|
373
|
-
|
|
374
|
-
### MCP Prompts
|
|
375
|
-
|
|
376
|
-
- `create_rest_api` - Build complete REST API
|
|
377
|
-
- `create_realtime_server` - WebSocket + channels
|
|
378
|
-
- `create_grpc_service` - gRPC services (unary + streaming)
|
|
379
|
-
- `create_microservice` - Production-ready service
|
|
380
|
-
- `add_authentication` - Add JWT/API key auth
|
|
381
|
-
- `add_caching` - Add caching drivers
|
|
382
|
-
- `add_rate_limiting` - Add per-route or global limits
|
|
383
|
-
- `add_observability` - Metrics + tracing
|
|
384
|
-
- `migrate_from_express` - Convert from Express
|
|
385
|
-
- `migrate_from_fastify` - Convert from Fastify
|
|
386
|
-
- `migrate_from_trpc` - Convert from tRPC
|
|
387
|
-
- `debug_middleware` - Diagnose interceptor order/issues
|
|
388
|
-
- `optimize_performance` - Perf review + tuning ideas
|
|
389
|
-
|
|
390
|
-
### MCP Resources
|
|
391
|
-
|
|
392
|
-
The MCP server also exposes documentation and boilerplates as resources:
|
|
393
|
-
|
|
394
|
-
- `raffel://guide/quickstart`
|
|
395
|
-
- `raffel://interceptor/{name}`
|
|
396
|
-
- `raffel://adapter/{name}`
|
|
397
|
-
- `raffel://pattern/{name}`
|
|
398
|
-
- `raffel://error/{code}`
|
|
399
|
-
- `raffel://boilerplate/{template}`
|
|
359
|
+
</td>
|
|
360
|
+
<td>
|
|
400
361
|
|
|
401
|
-
|
|
362
|
+
```typescript
|
|
363
|
+
import { createServer } from 'raffel'
|
|
402
364
|
|
|
403
|
-
|
|
365
|
+
const app = createServer({ port: 3000 })
|
|
404
366
|
|
|
405
|
-
|
|
367
|
+
app.get('/users/:id', async ({ id }) => {
|
|
368
|
+
return getUser(id)
|
|
369
|
+
})
|
|
406
370
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
| [Core Model](https://forattini-dev.github.io/raffel/#/core-model) | Envelope + Context deep dive |
|
|
411
|
-
| [Handlers](https://forattini-dev.github.io/raffel/#/handlers/procedures) | Procedures, Streams, Events |
|
|
412
|
-
| [Interceptors](https://forattini-dev.github.io/raffel/#/interceptors) | Retry, timeout, bulkhead, cache |
|
|
413
|
-
| [Auth](https://forattini-dev.github.io/raffel/#/auth/overview) | Bearer/API key/OAuth2/OIDC |
|
|
414
|
-
| [Routing](https://forattini-dev.github.io/raffel/#/route-discovery) | Modules, discovery, REST Auto-CRUD |
|
|
415
|
-
| [USD + OpenAPI](https://forattini-dev.github.io/raffel/#/usd) | Specs from schemas |
|
|
416
|
-
| [MCP Server](https://forattini-dev.github.io/raffel/#/mcp) | Tools, resources, prompts |
|
|
371
|
+
app.post('/users', async (body) => {
|
|
372
|
+
return createUser(body)
|
|
373
|
+
})
|
|
417
374
|
|
|
418
|
-
|
|
375
|
+
await app.start()
|
|
376
|
+
```
|
|
419
377
|
|
|
420
|
-
|
|
378
|
+
</td>
|
|
379
|
+
</tr>
|
|
380
|
+
</table>
|
|
421
381
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
| **MCP Prompts** | 13 |
|
|
382
|
+
**Key differences:**
|
|
383
|
+
- Return values instead of `res.json()`
|
|
384
|
+
- Path params and body merged into handler argument
|
|
385
|
+
- `async/await` native (no callback hell)
|
|
386
|
+
- Errors thrown, not manually handled
|
|
387
|
+
|
|
388
|
+
See [full migration guide](./docs/migration.md) for middleware, error handling, and advanced patterns.
|
|
430
389
|
|
|
431
390
|
---
|
|
432
391
|
|
|
433
392
|
## Examples
|
|
434
393
|
|
|
435
394
|
```bash
|
|
436
|
-
# Clone and run
|
|
395
|
+
# Clone and run
|
|
396
|
+
git clone https://github.com/tetis-io/raffel
|
|
397
|
+
cd raffel
|
|
398
|
+
|
|
399
|
+
# Basic examples
|
|
437
400
|
pnpm tsx examples/00-hello-world.ts
|
|
438
401
|
pnpm tsx examples/01-rest-api.ts
|
|
439
402
|
pnpm tsx examples/02-websocket-server.ts
|
|
440
403
|
pnpm tsx examples/03-rpc-server.ts
|
|
404
|
+
|
|
405
|
+
# Advanced
|
|
406
|
+
pnpm tsx examples/07-resource-builder.ts
|
|
407
|
+
pnpm tsx examples/08-declarative-api.ts
|
|
441
408
|
```
|
|
442
409
|
|
|
443
410
|
---
|
|
444
411
|
|
|
445
|
-
##
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
|
450
|
-
|
|
451
|
-
|
|
|
452
|
-
|
|
|
453
|
-
|
|
|
454
|
-
|
|
|
455
|
-
|
|
|
456
|
-
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
412
|
+
## Documentation
|
|
413
|
+
|
|
414
|
+
| Topic | Description |
|
|
415
|
+
|-------|-------------|
|
|
416
|
+
| [Quickstart](https://forattini-dev.github.io/raffel/#/quickstart) | 5-minute guide |
|
|
417
|
+
| [HTTP Deep Dive](https://forattini-dev.github.io/raffel/#/protocols/http) | REST, middleware, routing |
|
|
418
|
+
| [Authentication](https://forattini-dev.github.io/raffel/#/auth/overview) | JWT, API Key, OAuth2, OIDC |
|
|
419
|
+
| [Validation](https://forattini-dev.github.io/raffel/#/validation) | Zod, Yup, Joi integration |
|
|
420
|
+
| [WebSocket](https://forattini-dev.github.io/raffel/#/protocols/websocket) | Real-time, channels, presence |
|
|
421
|
+
| [Interceptors](https://forattini-dev.github.io/raffel/#/interceptors) | Rate limit, retry, cache |
|
|
422
|
+
| [Core Model](https://forattini-dev.github.io/raffel/#/core-model) | Envelope, Context, architecture |
|
|
423
|
+
| [File-based Routing](https://forattini-dev.github.io/raffel/#/file-system-discovery) | Zero-config discovery |
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## MCP Server (AI Integration)
|
|
428
|
+
|
|
429
|
+
Raffel includes an MCP server for AI-powered development:
|
|
430
|
+
|
|
431
|
+
```bash
|
|
432
|
+
# Add to Claude Code
|
|
433
|
+
claude mcp add raffel npx raffel-mcp
|
|
434
|
+
|
|
435
|
+
# Or run directly
|
|
436
|
+
npx raffel-mcp --category minimal
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Tools: `raffel_create_server`, `raffel_create_procedure`, `raffel_add_middleware`, `raffel_api_patterns`
|
|
472
440
|
|
|
473
441
|
---
|
|
474
442
|
|
|
475
443
|
## License
|
|
476
444
|
|
|
477
|
-
|
|
445
|
+
ISC
|
|
478
446
|
|
|
479
447
|
---
|
|
480
448
|
|
|
481
449
|
<div align="center">
|
|
482
450
|
|
|
483
|
-
**[Documentation](https://forattini-dev.github.io/raffel)** · **[GitHub](https://github.com/
|
|
451
|
+
**[Documentation](https://forattini-dev.github.io/raffel)** · **[GitHub](https://github.com/tetis-io/raffel)** · **[npm](https://www.npmjs.com/package/raffel)**
|
|
484
452
|
|
|
485
453
|
</div>
|