Skip to content

Commit aaeb0cc

Browse files
authored
Merge pull request #108 from bufferings/work
Remove response abort functionality and simplify hook return mechanism
2 parents f2d81de + d7b3394 commit aaeb0cc

24 files changed

+510
-510
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@korix/kori': patch
3+
'@korix/body-limit-plugin': patch
4+
'@korix/cors-plugin': patch
5+
---
6+
7+
Remove response abort functionality and simplify hook return mechanism
8+
9+
This change eliminates the complex abort pattern in favor of direct response returns from hooks, making the API more intuitive and reducing cognitive overhead for developers. Hooks can now return KoriResponse directly for early termination instead of using the abort mechanism.

docs/en/guide/context-evolution.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Use method chaining to ensure type extensions are properly inherited:
99
```typescript
1010
// ✅ Good: Chained hooks preserve type extensions
1111
const app = createKori()
12-
.onInit(async (ctx) => {
12+
.onStart(async (ctx) => {
1313
const db = await connectDatabase();
1414
return ctx.withEnv({ db });
1515
})
@@ -20,13 +20,13 @@ const app = createKori()
2020
})
2121
.onRequest((ctx) => {
2222
// Both ctx.env.db and ctx.req.user are typed
23-
ctx.req.log().info('Request', { userId: ctx.req.user?.id });
23+
ctx.log().info('Request', { userId: ctx.req.user?.id });
2424
});
2525

2626
// ❌ Avoid: Separate calls lose type information
2727
const app = createKori();
2828

29-
app.onInit(async (ctx) => {
29+
app.onStart(async (ctx) => {
3030
const db = await connectDatabase();
3131
return ctx.withEnv({ db });
3232
}); // Type extension is lost
@@ -51,7 +51,7 @@ const app = createKori()
5151
// 2. Request ID tracking
5252
.onRequest((ctx) => {
5353
const requestId = crypto.randomUUID();
54-
ctx.req.log().info('Request started', { requestId });
54+
ctx.log().info('Request started', { requestId });
5555
return ctx.withReq({ requestId });
5656
})
5757
// 3. Timing
@@ -71,7 +71,7 @@ const app = createKori()
7171

7272
// All extensions are available
7373
app.get('/api/data', (ctx) => {
74-
ctx.req.log().info('Processing request', {
74+
ctx.log().info('Processing request', {
7575
requestId: ctx.req.requestId,
7676
user: ctx.req.user?.id,
7777
});

docs/en/guide/error-handling.md

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Error Handling
22

3-
Kori provides comprehensive error handling with built-in error responses, automatic content negotiation, and flexible error recovery patterns.
3+
Kori provides comprehensive error handling with built-in error responses and flexible error recovery patterns.
44

55
## Built-in Error Responses
66

7-
Kori includes ready-to-use error response methods that automatically format based on the client's `Accept` header:
7+
Kori includes ready-to-use error response methods that set the appropriate HTTP status code and return a JSON body:
88

99
```typescript
1010
app.get('/users/:id', async (ctx) => {
@@ -27,87 +27,93 @@ app.get('/users/:id', async (ctx) => {
2727

2828
```typescript
2929
// 400 Bad Request
30-
ctx.res.badRequest({ message: 'Invalid input data' });
30+
ctx.res.badRequest();
3131

3232
// 401 Unauthorized
33-
ctx.res.unauthorized({ message: 'Authentication required' });
33+
ctx.res.unauthorized();
3434

3535
// 403 Forbidden
36-
ctx.res.forbidden({ message: 'Insufficient permissions' });
36+
ctx.res.forbidden();
3737

3838
// 404 Not Found
39-
ctx.res.notFound({ message: 'Resource not found' });
39+
ctx.res.notFound();
4040

4141
// 405 Method Not Allowed
42-
ctx.res.methodNotAllowed({ message: 'Only GET and POST allowed' });
42+
ctx.res.methodNotAllowed();
43+
44+
// 415 Unsupported Media Type
45+
ctx.res.unsupportedMediaType();
46+
47+
// 408 Request Timeout
48+
ctx.res.timeout();
4349

4450
// 500 Internal Server Error
45-
ctx.res.internalError({ message: 'Something went wrong' });
51+
ctx.res.internalError();
4652
```
4753

4854
### Error Response Format
4955

50-
All error responses return JSON format with a consistent structure:
56+
All error responses return JSON format with a consistent structure.
57+
58+
#### Default Responses
59+
60+
Without custom options, each method returns a standard message:
61+
62+
```typescript
63+
// Default usage
64+
ctx.res.notFound();
65+
```
5166

5267
```json
5368
{
5469
"error": {
5570
"type": "NOT_FOUND",
56-
"message": "User with ID 123 not found",
57-
"code": "USER_NOT_FOUND"
71+
"message": "Not Found"
5872
}
5973
}
6074
```
6175

76+
#### Custom Responses
77+
6278
You can include additional fields in the error response:
6379

6480
```typescript
81+
// Custom usage
6582
ctx.res.notFound({
6683
message: 'User with ID 123 not found',
6784
code: 'USER_NOT_FOUND',
6885
details: { userId: 123, searchedAt: new Date().toISOString() },
6986
});
7087
```
7188

89+
```json
90+
{
91+
"error": {
92+
"type": "NOT_FOUND",
93+
"message": "User with ID 123 not found",
94+
"code": "USER_NOT_FOUND",
95+
"details": {
96+
"userId": 123,
97+
"searchedAt": "2024-01-15T10:30:00.000Z"
98+
}
99+
}
100+
}
101+
```
102+
72103
## Global Error Handling
73104

74105
Set up application-wide error handling with the `onError` hook:
75106

76107
```typescript
77108
const app = createKori().onError((ctx, error) => {
78109
// Log the error
79-
ctx.req.log().error('Request failed', {
110+
ctx.log().error('Request failed', {
80111
error: error.message,
81112
stack: error.stack,
82113
url: ctx.req.url().pathname,
83114
});
84115

85116
// Return appropriate response
86-
if (!ctx.res.isReady()) {
87-
ctx.res.internalError({ message: 'Internal Server Error' });
88-
}
89-
});
90-
```
91-
92-
### Error Recovery Patterns
93-
94-
Handle specific errors locally and let the global handler manage unexpected ones:
95-
96-
```typescript
97-
app.get('/api/data', async (ctx) => {
98-
try {
99-
const data = await fetchExternalAPI();
100-
return ctx.res.json(data);
101-
} catch (error) {
102-
if (error.code === 'NETWORK_ERROR') {
103-
return ctx.res.badRequest({
104-
message: 'External service unavailable',
105-
code: 'SERVICE_UNAVAILABLE',
106-
});
107-
}
108-
109-
// Re-throw for global handler
110-
throw error;
111-
}
117+
return ctx.res.internalError({ message: 'Internal Server Error' });
112118
});
113119
```

docs/en/guide/getting-started.md

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,29 +63,37 @@ Monitor your application with built-in structured logging:
6363
```typescript
6464
import { createKori } from '@korix/kori';
6565

66-
const app = createKori().onInit(async (ctx) => {
67-
// Application-level logging (for system events)
68-
app.log().info('Application initializing');
66+
const app = createKori().onStart((ctx) => {
67+
// Instance-level logging (within hooks)
68+
ctx.log().info('Application initializing');
6969
});
7070

7171
app.get('/hello', (ctx) => {
72-
// Request-level logging (includes request context automatically)
73-
ctx.req.log().info('Processing hello request');
72+
// Request-level logging (within handlers)
73+
ctx.log().info('Processing hello request');
7474

7575
return ctx.res.text('Hello, Kori!');
7676
});
7777

78+
// Application-level logging (outside of Kori context)
79+
app.log().info('Application ready');
80+
7881
export { app };
7982
```
8083

84+
- **`app.log()`** - Application-level logger (outside of Kori context)
85+
- **`ctx.log()`** - Context-aware logger (inside Kori context: hooks and handlers)
86+
8187
Sample log output:
8288

8389
```json
84-
{"level":"info","time":1704067200000,"name":"application","message":"Application initializing"}
85-
{"level":"info","time":1704067200100,"name":"request","message":"Processing hello request"}
90+
{"time":1754198335875,"level":"info","channel":"app","name":"instance","message":"Application ready","meta":{}}
91+
{"time":1754198335875,"level":"info","channel":"app","name":"instance","message":"Application initializing","meta":{}}
92+
{"time":1754198335879,"level":"info","channel":"sys","name":"instance","message":"Kori server started at http://127.0.0.1:3000","meta":{}}
93+
{"time":1754198349150,"level":"info","channel":"app","name":"request","message":"Processing hello request","meta":{}}
8694
```
8795

88-
Kori provides a simple console logger by default for quick development. For real applications, use high-performance loggers like Pino. We provide a Pino adapter for easy integration.
96+
Kori provides a simple console logger by default for quick development. For real applications, use high-performance loggers like Pino or LogTape. We provide the adapters for easy integration.
8997

9098
## Hooks
9199

@@ -95,17 +103,18 @@ Add initialization and request lifecycle processing:
95103
import { createKori } from '@korix/kori';
96104

97105
const app = createKori()
98-
.onInit(async (ctx) => {
106+
.onStart((ctx) => {
99107
// Runs once when application starts
100108
return ctx.withEnv({ applicationStartTime: new Date() });
101109
})
102110
.onRequest((ctx) => {
103111
// Runs before each request handler
104-
ctx.req.log().info(`${ctx.req.method()} ${ctx.req.url().pathname}`);
105-
})
106-
.onResponse((ctx) => {
107-
// Runs after each successful response
108-
ctx.req.log().info(`Response: ${ctx.res.status()}`);
112+
ctx.log().info(`${ctx.req.method()} ${ctx.req.url().pathname}`);
113+
114+
// Defer response logging until after handler completes
115+
ctx.defer(() => {
116+
ctx.log().info(`Response: ${ctx.res.getStatus()}`);
117+
});
109118
});
110119

111120
app.get('/hello', (ctx) => {

docs/en/guide/handler-context.md

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,19 @@ type KoriHandlerContext<Env, Req, Res> = {
1111
env: Env; // Application environment
1212
req: Req; // Request object
1313
res: Res; // Response builder
14+
1415
withReq<ReqExt>(reqExt: ReqExt): KoriHandlerContext<Env, Req & ReqExt, Res>;
1516
withRes<ResExt>(resExt: ResExt): KoriHandlerContext<Env, Req, Res & ResExt>;
17+
18+
defer(
19+
callback: (ctx: KoriHandlerContext<Env, Req, Res>) => Promise<void> | void,
20+
): void;
21+
22+
log(): KoriLogger;
1623
};
1724
```
1825

19-
**Perfect for:**
26+
**Used for:**
2027

2128
- Processing HTTP requests
2229
- Accessing request data
@@ -30,7 +37,7 @@ Handler context supports request and response extensions for adding custom funct
3037
Every route handler receives a `ctx` parameter containing environment, request, and response:
3138

3239
```typescript
33-
app.get('/api/users/:id', (ctx) => {
40+
app.get('/api/users/:id', async (ctx) => {
3441
// ctx.env - application environment
3542
// ctx.req - request data and methods
3643
// ctx.res - response building methods
@@ -63,7 +70,7 @@ app.get('/users', async (ctx) => {
6370
Access all incoming request data:
6471

6572
```typescript
66-
app.get('/users/:id/posts', (ctx) => {
73+
app.get('/users/:id/posts', async (ctx) => {
6774
// Path parameters
6875
const { id } = ctx.req.pathParams();
6976

@@ -204,3 +211,38 @@ app.get('/users', async (ctx) => {
204211
}
205212
});
206213
```
214+
215+
## Deferred Processing (`ctx.defer()`)
216+
217+
Schedule tasks to run after the handler completes but before the response is returned. Commonly used in `onRequest` hooks, but also available in handlers.
218+
219+
### In onRequest Hook (Recommended)
220+
221+
```typescript
222+
const app = createKori().onRequest((ctx) => {
223+
// Add request ID for tracking
224+
const requestId = crypto.randomUUID();
225+
226+
// Schedule post-response cleanup
227+
ctx.defer(() => {
228+
// Clean up request-specific resources
229+
// Update metrics, close connections, etc.
230+
});
231+
232+
return ctx.withReq({ requestId });
233+
});
234+
```
235+
236+
### In Handler (When Needed)
237+
238+
```typescript
239+
app.post('/api/process', async (ctx) => {
240+
// Schedule cleanup for after response
241+
ctx.defer(() => {
242+
// Clean up temporary resources, update metrics, etc.
243+
});
244+
245+
const result = await processData();
246+
return ctx.res.json({ result });
247+
});
248+
```

0 commit comments

Comments
 (0)