@moostjs/event-http 0.5.33 → 0.6.0

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.
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: moostjs-event-http
3
+ description: Use this skill when working with @moostjs/event-http — to create an HTTP server with MoostHttp adapter, register route handlers with @Get()/@Post()/@Put()/@Delete()/@Patch()/@All(), extract request data with @Query(), @Header(), @Cookie(), @Body(), @RawBody(), @Authorization(), @Url(), @Method(), @Req(), @Res(), @ReqId(), @Ip(), @IpList(), control responses with @SetHeader(), @SetCookie(), @SetStatus(), @StatusHook(), @HeaderHook(), @CookieHook(), @CookieAttrsHook(), throw HTTP errors with HttpError, enforce body limits with @BodySizeLimit()/@CompressedBodySizeLimit()/@BodyReadTimeoutMs(), define auth guards with defineAuthGuard()/AuthGuard/Authenticate, or handle WebSocket upgrades with @Upgrade().
4
+ ---
5
+
6
+ # @moostjs/event-http
7
+
8
+ Moost HTTP adapter — decorator-driven HTTP server built on `@wooksjs/event-http`. Provides route decorators, request data extractors, response control, auth guards, and body limits for Moost applications.
9
+
10
+ ## How to use this skill
11
+
12
+ Read the domain file that matches the task. Do not load all files — only what you need.
13
+
14
+ | Domain | File | Load when... |
15
+ |--------|------|------------|
16
+ | Core concepts & setup | [core.md](core.md) | Starting a new project, understanding the mental model, configuring MoostHttp adapter |
17
+ | Routing & handlers | [routing.md](routing.md) | Defining routes with @Get/@Post/etc, route parameters, wildcards, path patterns |
18
+ | Request data | [request.md](request.md) | Extracting query params, headers, cookies, body, auth, IP, URL from requests |
19
+ | Response control | [response.md](response.md) | Setting status codes, headers, cookies, error handling, raw response access |
20
+ | Authentication | [auth.md](auth.md) | Auth guards, credential extraction, bearer/basic/apiKey/cookie transports, @Authenticate |
21
+
22
+ ## Quick reference
23
+
24
+ ```ts
25
+ // Imports
26
+ import { MoostHttp, Get, Post, Put, Delete, Patch, All, HttpMethod, Upgrade } from '@moostjs/event-http'
27
+ import { Query, Header, Cookie, Body, RawBody, Authorization, Url, Method, Req, Res, ReqId, Ip, IpList } from '@moostjs/event-http'
28
+ import { SetHeader, SetCookie, SetStatus, StatusHook, HeaderHook, CookieHook, CookieAttrsHook } from '@moostjs/event-http'
29
+ import { BodySizeLimit, CompressedBodySizeLimit, BodyReadTimeoutMs } from '@moostjs/event-http'
30
+ import { Authenticate, AuthGuard, defineAuthGuard, HttpError } from '@moostjs/event-http'
31
+ import { Controller, Param, Params } from 'moost'
32
+ ```
@@ -0,0 +1,275 @@
1
+ # Authentication — @moostjs/event-http
2
+
3
+ > Declarative auth guards with automatic credential extraction and Swagger integration.
4
+
5
+ ## Concepts
6
+
7
+ The auth guard system has three components:
8
+
9
+ 1. **Transport declaration** — describes *where* credentials come from (bearer token, basic auth, API key, cookie)
10
+ 2. **Guard handler** — your verification logic, receives the extracted credentials
11
+ 3. **`@Authenticate` decorator** — applies the guard to a controller or handler
12
+
13
+ Two APIs are provided:
14
+ - **Functional** (`defineAuthGuard`) — stateless, simple guards
15
+ - **Class-based** (`AuthGuard`) — when you need dependency injection
16
+
17
+ Auth guard transport declarations are stored in metadata, enabling automatic Swagger/OpenAPI security scheme discovery.
18
+
19
+ ## API Reference
20
+
21
+ ### `defineAuthGuard(transports, handler)`
22
+
23
+ Create a functional auth guard.
24
+
25
+ ```ts
26
+ import { defineAuthGuard, HttpError } from '@moostjs/event-http'
27
+
28
+ const jwtGuard = defineAuthGuard(
29
+ { bearer: { format: 'JWT' } },
30
+ (transports) => {
31
+ const user = verifyJwt(transports.bearer)
32
+ if (!user) throw new HttpError(401, 'Invalid token')
33
+ // return value is optional — if returned, it becomes the handler's response (short-circuit)
34
+ },
35
+ )
36
+ ```
37
+
38
+ **Parameters:**
39
+ - `transports: TAuthTransportDeclaration` — which credentials to extract
40
+ - `handler: (transports: TAuthTransportValues<T>) => unknown | Promise<unknown>` — verification logic
41
+
42
+ **Returns:** `TAuthGuardDef` — an interceptor def with transport metadata attached.
43
+
44
+ ### `AuthGuard<T>`
45
+
46
+ Abstract base class for class-based auth guards. Use when you need DI.
47
+
48
+ ```ts
49
+ import { AuthGuard, HttpError } from '@moostjs/event-http'
50
+ import { Injectable } from 'moost'
51
+
52
+ @Injectable()
53
+ class JwtGuard extends AuthGuard<{ bearer: { format: 'JWT' } }> {
54
+ static transports = { bearer: { format: 'JWT' } } as const
55
+
56
+ constructor(private userService: UserService) {}
57
+
58
+ handle(transports: { bearer: string }) {
59
+ const user = this.userService.verifyToken(transports.bearer)
60
+ if (!user) throw new HttpError(401, 'Invalid token')
61
+ }
62
+ }
63
+ ```
64
+
65
+ Requirements:
66
+ - Extend `AuthGuard<T>` with transport declaration as generic parameter
67
+ - Set `static transports` matching the generic (read at runtime)
68
+ - Implement `handle(transports)` with verification logic
69
+ - Use `@Injectable()` for constructor injection
70
+
71
+ ### `@Authenticate(handler)`
72
+
73
+ Apply an auth guard to a controller or handler method.
74
+
75
+ ```ts
76
+ import { Authenticate, Get } from '@moostjs/event-http'
77
+ import { Controller } from 'moost'
78
+
79
+ // Controller-level — all handlers require auth
80
+ @Authenticate(jwtGuard)
81
+ @Controller('users')
82
+ class UsersController {
83
+ @Get('')
84
+ list() {}
85
+
86
+ @Get(':id')
87
+ find() {}
88
+ }
89
+
90
+ // Handler-level — specific endpoints only
91
+ @Controller('products')
92
+ class ProductsController {
93
+ @Get('')
94
+ list() { /* public */ }
95
+
96
+ @Authenticate(jwtGuard)
97
+ @Post('')
98
+ create() { /* requires auth */ }
99
+ }
100
+ ```
101
+
102
+ Accepts both functional (`TAuthGuardDef`) and class-based (`TAuthGuardClass`) guards.
103
+
104
+ ### `extractTransports(declaration)`
105
+
106
+ Low-level function that extracts credentials from the current request context. Called internally by auth guards — rarely needed directly.
107
+
108
+ ```ts
109
+ import { extractTransports } from '@moostjs/event-http'
110
+
111
+ const values = extractTransports({ bearer: { format: 'JWT' } })
112
+ // values.bearer = 'eyJ...' (the raw token)
113
+ ```
114
+
115
+ Throws `HttpError(401, 'No authentication credentials provided')` if none of the declared transports are present.
116
+
117
+ ## Transport types
118
+
119
+ ### Bearer token
120
+
121
+ Extracts from `Authorization: Bearer <token>`:
122
+
123
+ ```ts
124
+ { bearer: { format?: string, description?: string } }
125
+ // Extracted value: string (raw token without "Bearer " prefix)
126
+ ```
127
+
128
+ ### Basic authentication
129
+
130
+ Extracts from `Authorization: Basic <base64>`:
131
+
132
+ ```ts
133
+ { basic: { description?: string } }
134
+ // Extracted value: { username: string, password: string }
135
+ ```
136
+
137
+ ### API key
138
+
139
+ Extracts from a header, query parameter, or cookie:
140
+
141
+ ```ts
142
+ { apiKey: { name: string, in: 'header' | 'query' | 'cookie', description?: string } }
143
+ // Extracted value: string
144
+ ```
145
+
146
+ ```ts
147
+ // From header
148
+ defineAuthGuard({ apiKey: { name: 'X-API-Key', in: 'header' } }, (t) => { t.apiKey /* string */ })
149
+
150
+ // From query param
151
+ defineAuthGuard({ apiKey: { name: 'api_key', in: 'query' } }, (t) => { t.apiKey /* string */ })
152
+
153
+ // From cookie
154
+ defineAuthGuard({ apiKey: { name: 'api_key', in: 'cookie' } }, (t) => { t.apiKey /* string */ })
155
+ ```
156
+
157
+ ### Cookie
158
+
159
+ Extracts a value from a named cookie:
160
+
161
+ ```ts
162
+ { cookie: { name: string, description?: string } }
163
+ // Extracted value: string
164
+ ```
165
+
166
+ ## Common Patterns
167
+
168
+ ### Pattern: JWT bearer guard
169
+
170
+ ```ts
171
+ const jwtGuard = defineAuthGuard(
172
+ { bearer: { format: 'JWT', description: 'JWT access token' } },
173
+ (transports) => {
174
+ const payload = verifyJwt(transports.bearer)
175
+ if (!payload) throw new HttpError(401, 'Invalid or expired token')
176
+ },
177
+ )
178
+ ```
179
+
180
+ ### Pattern: API key guard
181
+
182
+ ```ts
183
+ const apiKeyGuard = defineAuthGuard(
184
+ { apiKey: { name: 'X-API-Key', in: 'header' } },
185
+ (transports) => {
186
+ if (!isValidApiKey(transports.apiKey)) {
187
+ throw new HttpError(401, 'Invalid API key')
188
+ }
189
+ },
190
+ )
191
+ ```
192
+
193
+ ### Pattern: Auth + authorization stacking
194
+
195
+ ```ts
196
+ @Authenticate(jwtGuard) // step 1: verify credentials
197
+ @RequireRole('admin') // step 2: check authorization
198
+ @Controller('admin')
199
+ class AdminController {
200
+ @Get('dashboard')
201
+ dashboard() { /* authenticated + admin */ }
202
+ }
203
+ ```
204
+
205
+ ### Pattern: Class-based guard with DI
206
+
207
+ ```ts
208
+ @Injectable()
209
+ class SessionGuard extends AuthGuard<{ cookie: { name: 'session' } }> {
210
+ static transports = { cookie: { name: 'session' } } as const
211
+
212
+ constructor(private sessionService: SessionService) {}
213
+
214
+ handle(transports: { cookie: string }) {
215
+ const session = this.sessionService.validate(transports.cookie)
216
+ if (!session) throw new HttpError(401, 'Invalid session')
217
+ }
218
+ }
219
+
220
+ @Authenticate(SessionGuard)
221
+ @Controller('dashboard')
222
+ class DashboardController {}
223
+ ```
224
+
225
+ ### Pattern: Handler-level override
226
+
227
+ ```ts
228
+ @Authenticate(apiKeyGuard)
229
+ @Controller('products')
230
+ class ProductsController {
231
+ @Get('')
232
+ list() { /* uses apiKeyGuard */ }
233
+
234
+ @Authenticate(basicGuard)
235
+ @Post('')
236
+ create() { /* uses basicGuard instead */ }
237
+ }
238
+ ```
239
+
240
+ ## Integration
241
+
242
+ - **Swagger**: Transport declarations map directly to OpenAPI security schemes. The `@moostjs/swagger` package auto-discovers `@Authenticate` metadata.
243
+ - **Guards**: Auth guards run at `GUARD` priority. Combine with custom authorization guards (also at `GUARD` priority) — they execute in decorator declaration order.
244
+
245
+ ## Types
246
+
247
+ ```ts
248
+ interface TAuthTransportDeclaration {
249
+ bearer?: { format?: string; description?: string }
250
+ basic?: { description?: string }
251
+ apiKey?: { name: string; in: 'header' | 'query' | 'cookie'; description?: string }
252
+ cookie?: { name: string; description?: string }
253
+ }
254
+
255
+ type TAuthTransportValues<T> = {
256
+ [K in keyof T]: K extends 'basic' ? { username: string; password: string } : string
257
+ }
258
+
259
+ type TAuthGuardHandler = TAuthGuardDef | TAuthGuardClass
260
+ ```
261
+
262
+ ## Best Practices
263
+
264
+ - Use functional guards (`defineAuthGuard`) for simple, stateless checks
265
+ - Use class-based guards (`AuthGuard`) when you need DI services (e.g., database, token service)
266
+ - Apply `@Authenticate` at the controller level for protected resources, handler level for mixed access
267
+ - Always throw `HttpError` with meaningful messages for auth failures
268
+ - Combine with `@moostjs/swagger` for automatic OpenAPI security documentation
269
+
270
+ ## Gotchas
271
+
272
+ - If none of the declared transports are present in the request, `extractTransports` throws `HttpError(401)` automatically — your handler won't be called
273
+ - Class-based guards must set `static transports` — it's read at runtime, not from the generic parameter
274
+ - Auth guards run at `GUARD` priority — they execute before `INTERCEPTOR`-priority interceptors
275
+ - The handler's return value (if any) short-circuits the response — use this for redirects or custom auth responses
@@ -0,0 +1,193 @@
1
+ # Core concepts & setup — @moostjs/event-http
2
+
3
+ > How to create and configure an HTTP server with the Moost HTTP adapter.
4
+
5
+ ## Concepts
6
+
7
+ `@moostjs/event-http` is the HTTP adapter for the Moost framework. It bridges Moost's decorator-driven controller system with `@wooksjs/event-http` (the underlying HTTP engine). The adapter handles:
8
+
9
+ - Binding decorated handler methods to HTTP routes
10
+ - Managing request scoping and cleanup
11
+ - Providing dependency injection for the underlying WooksHttp instance
12
+
13
+ **Key classes:**
14
+ - `MoostHttp` — the adapter class you instantiate and attach to your Moost app
15
+ - `WooksHttp` — the underlying Wooks HTTP engine (available via DI if needed)
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @moostjs/event-http moost
21
+ # or
22
+ pnpm add @moostjs/event-http moost
23
+ ```
24
+
25
+ Peer dependencies (installed automatically with moost):
26
+ - `@wooksjs/event-core` — async event context
27
+ - `@prostojs/infact` — dependency injection
28
+ - `@prostojs/router` — route matching
29
+
30
+ ## Setup
31
+
32
+ ### Minimal HTTP server
33
+
34
+ ```ts
35
+ import { MoostHttp, Get } from '@moostjs/event-http'
36
+ import { Moost, Controller, Param } from 'moost'
37
+
38
+ @Controller()
39
+ class AppController {
40
+ @Get('hello/:name')
41
+ greet(@Param('name') name: string) {
42
+ return `Hello, ${name}!`
43
+ }
44
+ }
45
+
46
+ const app = new Moost()
47
+ void app.adapter(new MoostHttp()).listen(3000, () => {
48
+ app.getLogger('app').info('Up on port 3000')
49
+ })
50
+ void app.registerControllers(AppController).init()
51
+ ```
52
+
53
+ ### Scaffold a project
54
+
55
+ ```bash
56
+ npm create moost -- --http
57
+ # or with a name:
58
+ npm create moost my-web-app -- --http
59
+ ```
60
+
61
+ ## API Reference
62
+
63
+ ### `MoostHttp`
64
+
65
+ The HTTP adapter class implementing `TMoostAdapter`.
66
+
67
+ ```ts
68
+ import { MoostHttp } from '@moostjs/event-http'
69
+
70
+ // Default — creates a new WooksHttp internally
71
+ const http = new MoostHttp()
72
+
73
+ // With options passed to WooksHttp
74
+ const http = new MoostHttp({ onNotFound: customHandler })
75
+
76
+ // With an existing WooksHttp instance
77
+ const http = new MoostHttp(existingWooksHttp)
78
+ ```
79
+
80
+ **Methods:**
81
+
82
+ | Method | Returns | Description |
83
+ |--------|---------|-------------|
84
+ | `listen(port?, ...)` | `Promise<void>` | Start the HTTP server (same overloads as `net.Server.listen`) |
85
+ | `getHttpApp()` | `WooksHttp` | Access the underlying Wooks HTTP engine |
86
+ | `getServerCb()` | `RequestListener` | Get the request handler callback for use with existing Node.js servers |
87
+
88
+ ### `getServerCb()` — Custom server integration
89
+
90
+ Use `getServerCb()` to integrate with an existing Node.js HTTP/HTTPS server:
91
+
92
+ ```ts
93
+ import { createServer } from 'https'
94
+ import { MoostHttp } from '@moostjs/event-http'
95
+ import { Moost } from 'moost'
96
+
97
+ const http = new MoostHttp()
98
+ const app = new Moost()
99
+ app.adapter(http)
100
+
101
+ const server = createServer(tlsOptions, http.getServerCb())
102
+ server.listen(443)
103
+ await app.init()
104
+ ```
105
+
106
+ ### `HttpError`
107
+
108
+ Re-exported from `@wooksjs/event-http`. Throw to produce an HTTP error response with a specific status code.
109
+
110
+ ```ts
111
+ import { HttpError } from '@moostjs/event-http'
112
+
113
+ throw new HttpError(404, 'Not Found')
114
+ throw new HttpError(422, { message: 'Validation failed', errors: [...] })
115
+ ```
116
+
117
+ ### `httpKind`
118
+
119
+ Re-exported from `@wooksjs/event-http`. The event kind identifier for HTTP events.
120
+
121
+ ### `useHttpContext()`
122
+
123
+ Re-exported from `@wooksjs/event-http`. Access the raw Wooks HTTP context from within a handler (advanced use only — prefer decorators).
124
+
125
+ ## Common Patterns
126
+
127
+ ### Pattern: Class extending Moost
128
+
129
+ Instead of creating a separate `Moost` instance and registering controllers, extend `Moost` directly:
130
+
131
+ ```ts
132
+ import { MoostHttp, Get } from '@moostjs/event-http'
133
+ import { Moost, Param } from 'moost'
134
+
135
+ class MyServer extends Moost {
136
+ @Get('test/:name')
137
+ test(@Param('name') name: string) {
138
+ return { message: `Hello ${name}!` }
139
+ }
140
+ }
141
+
142
+ const app = new MyServer()
143
+ app.adapter(new MoostHttp()).listen(3000)
144
+ void app.init()
145
+ ```
146
+
147
+ ### Pattern: Multiple controllers
148
+
149
+ ```ts
150
+ import { Moost } from 'moost'
151
+ import { MoostHttp } from '@moostjs/event-http'
152
+
153
+ const app = new Moost()
154
+ app.adapter(new MoostHttp()).listen(3000)
155
+ void app
156
+ .registerControllers(UserController, ProductController, OrderController)
157
+ .init()
158
+ ```
159
+
160
+ ## DI-available services
161
+
162
+ When MoostHttp is attached, these are available via constructor injection:
163
+
164
+ | Token | Type | Description |
165
+ |-------|------|-------------|
166
+ | `WooksHttp` | class | The underlying Wooks HTTP app |
167
+ | `'WooksHttp'` | string | Same, by string token |
168
+ | `HttpServer` | `http.Server` | The Node.js HTTP server instance |
169
+ | `HttpsServer` | `https.Server` | The Node.js HTTPS server instance |
170
+
171
+ ```ts
172
+ import { Injectable } from 'moost'
173
+ import { WooksHttp } from '@wooksjs/event-http'
174
+
175
+ @Injectable()
176
+ class MyService {
177
+ constructor(private wooks: WooksHttp) {}
178
+ }
179
+ ```
180
+
181
+ ## Best Practices
182
+
183
+ - Call `app.adapter(http).listen(port)` before `app.init()` — the adapter must be attached before initialization
184
+ - Use `registerControllers()` to add controller classes, not instances
185
+ - Prefer the decorator-based API (`@Get`, `@Body`, etc.) over accessing wooks composables directly
186
+ - Use `getServerCb()` when integrating with existing HTTP/HTTPS servers instead of calling `listen()`
187
+
188
+ ## Gotchas
189
+
190
+ - The `path` argument in route decorators is optional — when omitted, the method name is used as the path segment
191
+ - `@Get('')` (empty string) maps to the controller root, while `@Get()` (no argument) uses the method name
192
+ - `app.init()` must be called after `registerControllers()` — routes are bound during initialization
193
+ - MoostHttp registers a default 404 handler that runs through the global interceptor chain