The stylish Node.js middleware engine for AWS Lambda (core package)
npx @tessl/cli install tessl/npm-middy--core@6.4.00
# Middy Core
1
2
Middy Core is the stylish Node.js middleware engine for AWS Lambda. It provides a fluent, plugin-based architecture that enables developers to create composable and reusable middleware patterns for serverless applications. The core package handles before, after, and error middleware execution, supports both traditional and streaming response patterns, includes built-in timeout handling, and provides a clean API for creating maintainable Lambda functions.
3
4
## Package Information
5
6
- **Package Name**: @middy/core
7
- **Package Type**: npm
8
- **Language**: JavaScript (ES Module)
9
- **Installation**: `npm install @middy/core`
10
- **Node.js**: >=20
11
- **Types**: Full TypeScript support included
12
13
## Core Imports
14
15
```javascript
16
import middy from "@middy/core";
17
```
18
19
For CommonJS (also supported):
20
21
```javascript
22
const middy = require("@middy/core");
23
```
24
25
## Basic Usage
26
27
```javascript
28
import middy from "@middy/core";
29
30
// Basic Lambda handler
31
const lambdaHandler = async (event, context) => {
32
return { statusCode: 200, body: "Hello World" };
33
};
34
35
// Create middlewared handler
36
const handler = middy(lambdaHandler)
37
.use(someMiddleware())
38
.before(async (request) => {
39
console.log("Before handler", request.event);
40
})
41
.after(async (request) => {
42
console.log("After handler", request.response);
43
})
44
.onError(async (request) => {
45
console.log("Error occurred", request.error);
46
});
47
48
export { handler };
49
```
50
51
## Architecture
52
53
Middy Core is built around several key components:
54
55
- **Factory Function**: The main `middy()` function creates middlewared Lambda handlers
56
- **Middleware Chain**: Before, after, and error middleware executed in sequence
57
- **Request Object**: Shared state object passed through the middleware chain
58
- **Plugin System**: Lifecycle hooks for extending functionality and performance monitoring
59
- **Streaming Support**: Native AWS Lambda streaming response support
60
- **Timeout Handling**: Early timeout detection with configurable responses
61
- **Abort Signals**: Built-in AbortSignal support for cancellation
62
63
## Capabilities
64
65
### Factory Function
66
67
Creates a middlewared Lambda handler with fluent API for attaching middleware.
68
69
```typescript { .api }
70
/**
71
* Middy factory function. Use it to wrap your existing handler to enable middlewares on it.
72
* @param handler - Your original AWS Lambda function or PluginObject
73
* @param plugin - Plugin configuration for lifecycle hooks
74
* @returns MiddyfiedHandler with middleware methods
75
*/
76
function middy<TEvent = unknown, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}>(
77
handler?: LambdaHandler<TEvent, TResult> | MiddlewareHandler<LambdaHandler<TEvent, TResult>, TContext, TResult, TEvent> | PluginObject,
78
plugin?: PluginObject
79
): MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;
80
```
81
82
### Middlewared Handler Interface
83
84
The enhanced Lambda handler returned by `middy()` with middleware attachment methods. It extends both `MiddyInputHandler` and `MiddyInputPromiseHandler` to support both callback and Promise-based execution patterns.
85
86
```typescript { .api }
87
interface MiddyfiedHandler<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}> extends MiddyInputHandler<TEvent, TResult, TContext>, MiddyInputPromiseHandler<TEvent, TResult, TContext> {
88
/** Attach middleware objects or arrays of middleware objects */
89
use: (middlewares: MiddlewareObj | MiddlewareObj[]) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;
90
/** Attach before middleware function */
91
before: (middleware: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;
92
/** Attach after middleware function */
93
after: (middleware: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;
94
/** Attach error middleware function */
95
onError: (middleware: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;
96
/** Replace the Lambda handler function */
97
handler: <TInputHandlerEventProps = TEvent, TInputHandlerResultProps = TResult>(
98
handler: MiddyInputHandler<TInputHandlerEventProps, TInputHandlerResultProps, TContext>
99
) => MiddyfiedHandler<TInputHandlerEventProps, TInputHandlerResultProps, TErr, TContext, TInternal>;
100
}
101
```
102
103
### Request Object
104
105
The request object passed to all middleware functions containing event, context, response, and internal state.
106
107
```typescript { .api }
108
interface Request<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}> {
109
/** The Lambda event object */
110
event: TEvent;
111
/** The Lambda context object */
112
context: TContext;
113
/** Handler response (null initially, set by handler or middleware) */
114
response: TResult | null;
115
/** Optional early response value for short-circuiting */
116
earlyResponse?: TResult | null | undefined;
117
/** Error object (null initially, set if error occurs) */
118
error: TErr | null;
119
/** Internal state object for sharing data between middleware */
120
internal: TInternal;
121
}
122
```
123
124
### Middleware Function
125
126
Function signature for individual middleware functions.
127
128
```typescript { .api }
129
/**
130
* Middleware function signature
131
* @param request - The request object containing event, context, response, and internal state
132
* @returns Any value (can be Promise)
133
*/
134
type MiddlewareFn<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}> = (
135
request: Request<TEvent, TResult, TErr, TContext, TInternal>
136
) => any;
137
```
138
139
### Middleware Object
140
141
Object structure for defining middleware with before, after, and error hooks.
142
143
```typescript { .api }
144
interface MiddlewareObj<TEvent = unknown, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}> {
145
/** Optional before middleware function */
146
before?: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>;
147
/** Optional after middleware function */
148
after?: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>;
149
/** Optional error middleware function */
150
onError?: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>;
151
/** Optional middleware name for debugging */
152
name?: string;
153
}
154
```
155
156
### Plugin Configuration
157
158
Configuration object for lifecycle hooks and advanced functionality.
159
160
```typescript { .api }
161
interface PluginObject {
162
/** Internal state object shared across middleware */
163
internal?: any;
164
/** Hook called before prefetch operations */
165
beforePrefetch?: PluginHook;
166
/** Hook called at request start */
167
requestStart?: PluginHook;
168
/** Hook called before each middleware */
169
beforeMiddleware?: PluginHookWithMiddlewareName;
170
/** Hook called after each middleware */
171
afterMiddleware?: PluginHookWithMiddlewareName;
172
/** Hook called before handler execution */
173
beforeHandler?: PluginHook;
174
/** Timeout in milliseconds before Lambda timeout for early response */
175
timeoutEarlyInMillis?: number;
176
/** Function to call when timeout occurs */
177
timeoutEarlyResponse?: PluginHook;
178
/** Hook called after handler execution */
179
afterHandler?: PluginHook;
180
/** Hook called at request end */
181
requestEnd?: PluginHookPromise;
182
/** Enable AWS Lambda streaming response support */
183
streamifyResponse?: boolean;
184
}
185
```
186
187
### Handler Object
188
189
Object passed to Lambda handlers containing abort signal for timeout handling.
190
191
```typescript { .api }
192
interface MiddyHandlerObject {
193
/**
194
* An abort signal that will be canceled just before the lambda times out.
195
* Use this to cancel long-running operations gracefully.
196
*/
197
signal: AbortSignal;
198
}
199
```
200
201
## Usage Examples
202
203
### Multiple Middleware Objects
204
205
```javascript
206
import middy from "@middy/core";
207
208
const middleware1 = {
209
before: async (request) => {
210
console.log("Middleware 1 before");
211
},
212
after: async (request) => {
213
console.log("Middleware 1 after");
214
},
215
name: "middleware1"
216
};
217
218
const middleware2 = {
219
before: async (request) => {
220
console.log("Middleware 2 before");
221
},
222
onError: async (request) => {
223
console.log("Middleware 2 error handler");
224
},
225
name: "middleware2"
226
};
227
228
const handler = middy(lambdaHandler)
229
.use([middleware1, middleware2]);
230
```
231
232
### Plugin Configuration
233
234
```javascript
235
import middy from "@middy/core";
236
237
const pluginConfig = {
238
timeoutEarlyInMillis: 1000, // Timeout 1 second before Lambda timeout
239
timeoutEarlyResponse: () => {
240
throw new Error("Function timed out");
241
},
242
beforeMiddleware: (name) => console.log(`Starting ${name}`),
243
afterMiddleware: (name) => console.log(`Completed ${name}`),
244
internal: { startTime: Date.now() }
245
};
246
247
const handler = middy(lambdaHandler, pluginConfig);
248
```
249
250
### Handler Replacement
251
252
```javascript
253
import middy from "@middy/core";
254
255
// Create handler without initial function
256
const handler = middy()
257
.use(someMiddleware())
258
.handler(async (event, context, { signal }) => {
259
// Use abort signal for cancellation
260
const controller = new AbortController();
261
signal.addEventListener('abort', () => controller.abort());
262
263
return await fetch('https://api.example.com', {
264
signal: controller.signal
265
});
266
});
267
268
// Or replace an existing handler
269
const existingHandler = middy(lambdaHandler);
270
existingHandler.handler(async (event, context, { signal }) => {
271
// Completely replace the original handler logic
272
return { statusCode: 200, body: "New handler implementation" };
273
});
274
```
275
276
### Streaming Response
277
278
```javascript
279
import middy from "@middy/core";
280
281
const pluginConfig = {
282
streamifyResponse: true
283
};
284
285
const handler = middy(async (event, responseStream, context) => {
286
responseStream.write("Streaming data chunk 1\n");
287
responseStream.write("Streaming data chunk 2\n");
288
responseStream.end();
289
}, pluginConfig);
290
```
291
292
### Early Response
293
294
```javascript
295
import middy from "@middy/core";
296
297
const authMiddleware = {
298
before: async (request) => {
299
if (!request.event.headers.authorization) {
300
// Short-circuit the execution chain
301
request.earlyResponse = {
302
statusCode: 401,
303
body: JSON.stringify({ error: "Unauthorized" })
304
};
305
return;
306
}
307
}
308
};
309
310
const handler = middy(lambdaHandler)
311
.use(authMiddleware);
312
```
313
314
### Error Handling
315
316
```javascript
317
import middy from "@middy/core";
318
319
const errorHandlerMiddleware = {
320
onError: async (request) => {
321
console.error("Error occurred:", request.error);
322
323
// Modify response based on error
324
request.response = {
325
statusCode: 500,
326
body: JSON.stringify({
327
error: "Internal Server Error",
328
requestId: request.context.awsRequestId
329
})
330
};
331
}
332
};
333
334
const handler = middy(lambdaHandler)
335
.use(errorHandlerMiddleware);
336
```
337
338
## Constants
339
340
Package constants available for reference:
341
342
```typescript { .api }
343
/** Default no-op Lambda handler */
344
declare const defaultLambdaHandler: () => void;
345
346
/** Default plugin configuration object */
347
declare const defaultPluginConfig: PluginObject;
348
349
/** Chunk size for string iteration in streaming responses */
350
declare const stringIteratorSize: number; // 16384
351
```
352
353
The `defaultPluginConfig` object contains these default values:
354
355
```typescript { .api }
356
const defaultPluginConfig = {
357
timeoutEarlyInMillis: 5,
358
timeoutEarlyResponse: () => {
359
const err = new Error("[AbortError]: The operation was aborted.", {
360
cause: { package: "@middy/core" }
361
});
362
err.name = "TimeoutError";
363
throw err;
364
},
365
streamifyResponse: false
366
};
367
```
368
369
## Types
370
371
All TypeScript types and interfaces are exported in the `middy` namespace:
372
373
```typescript { .api }
374
declare namespace middy {
375
export type {
376
Request,
377
PluginHook,
378
PluginHookWithMiddlewareName,
379
PluginObject,
380
MiddlewareFn,
381
MiddlewareObj,
382
MiddyfiedHandler,
383
};
384
}
385
386
/** Plugin hook function signature */
387
type PluginHook = () => void;
388
389
/** Plugin hook function with middleware name parameter */
390
type PluginHookWithMiddlewareName = (middlewareName: string) => void;
391
392
/** Plugin hook function that can return a promise */
393
type PluginHookPromise = (request: Request) => Promise<unknown> | unknown;
394
395
/** Handler attachment function signature */
396
type AttachMiddlewareFn<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}> = (
397
middleware: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>
398
) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;
399
400
/** Middleware use function signature */
401
type UseFn<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}> = <TMiddleware extends MiddlewareObj<any, any, Error, any, any>>(
402
middlewares: TMiddleware | TMiddleware[]
403
) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;
404
405
/** Handler function with optional MiddyHandlerObject parameter */
406
type MiddyInputHandler<TEvent, TResult, TContext extends LambdaContext = LambdaContext> = (
407
event: TEvent,
408
context: TContext,
409
opts: MiddyHandlerObject
410
) => undefined | Promise<TResult> | TResult;
411
412
/** Standard Promise-based handler function */
413
type MiddyInputPromiseHandler<TEvent, TResult, TContext extends LambdaContext = LambdaContext> = (
414
event: TEvent,
415
context: TContext
416
) => Promise<TResult>;
417
```
418
419
## Key Features
420
421
1. **Fluent API**: Chainable methods for attaching middleware
422
2. **Middleware Support**: Before, after, and error middleware execution
423
3. **Plugin System**: Lifecycle hooks for custom behaviors and monitoring
424
4. **Streaming Response**: Native AWS Lambda streaming response support
425
5. **Timeout Handling**: Early timeout detection with configurable responses
426
6. **Abort Signal**: Built-in AbortSignal support for graceful cancellation
427
7. **Early Response**: Middleware can short-circuit execution
428
8. **Type Safety**: Full TypeScript support with generic types
429
9. **ES Module**: Modern JavaScript module format with CommonJS compatibility
430
10. **Minimal Dependencies**: Lightweight core with no external dependencies