@moostjs/event-http 0.5.32 → 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.
- package/README.md +67 -3
- package/dist/index.cjs +261 -83
- package/dist/index.d.ts +123 -22
- package/dist/index.mjs +248 -86
- package/package.json +40 -34
- package/scripts/setup-skills.js +76 -0
- package/skills/moostjs-event-http/SKILL.md +32 -0
- package/skills/moostjs-event-http/auth.md +275 -0
- package/skills/moostjs-event-http/core.md +193 -0
- package/skills/moostjs-event-http/request.md +230 -0
- package/skills/moostjs-event-http/response.md +287 -0
- package/skills/moostjs-event-http/routing.md +210 -0
|
@@ -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
|