Files
motia/bitbylaw/.cursor/rules/motia/middlewares.mdc
2025-10-19 14:57:07 +00:00

218 lines
6.0 KiB
Plaintext

---
description: Middlewares are used to execute code before and after the handler is called
globs: steps/**/*.step.ts,steps/**/*.step.js,steps/**/*_step.py,middlewares/**/*.middleware.ts,middlewares/**/*.middleware.js,middlewares/**/*_middleware.py
alwaysApply: false
---
# Middlewares Guide
Middlewares are used to execute code before and after the handler is called in API Steps.
The middleware is a handler that receives three arguments:
- **Request**: this is the same request object received by API Step handlers, if modified by the middleware, it will be the same object passed to the handler and any subsequent middleware. Be careful to not cause any side effects to the request object.
- **Context**: this is the same context object received by API Step handlers, if modified by the middleware, it will be the same object passed to the handler and any subsequent middleware. Be careful to not cause any side effects to the context object.
- **Next**: this is a function that you need to call to invoke the next middleware in the stack. If you don't call it, the request will be halted—the handler and any subsequent middlewares will not be called.
## Next function
Next function is a way to either continue the execution flow or stop it. For example, in authentication middlewares, if the user is not authenticated, you can return a 401 response and not call `next()`.
It can also be used to enrich data returned back to the HTTP response. Like adding a header parameter or so after calling `next()`.
## Adding middlewares to a step
### TypeScript/JavaScript Example
```typescript
import { ApiRouteConfig } from 'motia'
import { coreMiddleware } from '../middlewares/core.middleware'
export const config: ApiRouteConfig = {
type: 'api',
name: 'SampleRoute',
description: 'Sample route',
path: '/sample',
method: 'GET',
emits: [],
flows: [],
middleware: [coreMiddleware],
}
```
### Python Example
```python
async def enrich_data_middleware(req, context, next_fn):
context.logger.info("enriching data")
req["enriched"] = "yes"
return await next_fn()
config = {
"type": "api",
"name": "SampleRoute",
"description": "Sample route",
"path": "/sample",
"method": "GET",
"emits": [],
"flows": [],
"middleware": [enrich_data_middleware],
}
```
## Middleware examples
### Handling errors
#### TypeScript/JavaScript
```typescript
import { ApiMiddleware } from 'motia'
export const coreMiddleware: ApiMiddleware = async (req, ctx, next) => {
const { logger } = ctx
try {
/**
* Calling next() will invoke the next item in the stack.
*
* It will depend on the order of middlewares configured in the step,
* first one in the list is called first and so on.
*/
return await next()
} catch (error: any) {
logger.error('Error while performing request', {
error,
body: req.body, // make sure you don't include sensitive data in the logs
stack: error.stack,
})
return {
status: 500,
body: { error: 'Internal Server Error' },
}
}
}
```
#### Python
```python
async def error_handling_middleware(req, context, next_fn):
try:
# Calling next_fn() will invoke the next item in the stack.
# It will depend on the order of middlewares configured in the step,
# first one in the list is called first and so on.
return await next_fn()
except Exception as error:
context.logger.error('Error while performing request', {
'error': str(error),
'body': req.get('body'), # make sure you don't include sensitive data in the logs
})
return {
'status': 500,
'body': {'error': 'Internal Server Error'},
}
```
### Enriching response
#### TypeScript/JavaScript
```typescript
export const enrichResponseMiddleware: ApiMiddleware = async (req, ctx, next) => {
const response = await next()
response.headers['X-Custom-Header'] = 'Custom Value'
return response
}
```
#### Python
```python
async def enrich_response_middleware(req, context, next_fn):
response = await next_fn()
if not response.get('headers'):
response['headers'] = {}
response['headers']['X-Custom-Header'] = 'Custom Value'
return response
```
### Handling validation errors
#### TypeScript/JavaScript - Handling Zod Validation errors
```typescript
import { ApiMiddleware } from 'motia'
import { ZodError } from 'zod'
export const coreMiddleware: ApiMiddleware = async (req, ctx, next) => {
const logger = ctx.logger
try {
return await next()
} catch (error: any) {
if (error instanceof ZodError) {
logger.error('Validation error', {
error,
stack: error.stack,
errors: error.errors,
})
return {
status: 400,
body: {
error: 'Invalid request body',
data: error.errors,
},
}
}
logger.error('Error while performing request', {
error,
body: req.body, // make sure you don't include sensitive data in the logs
stack: error.stack,
})
return { status: 500, body: { error: 'Internal Server Error' } }
}
}
```
#### Python - Handling Pydantic Validation errors
```python
from pydantic import ValidationError
async def validation_middleware(req, context, next_fn):
try:
return await next_fn()
except ValidationError as error:
context.logger.error('Validation error', {
'error': str(error),
'errors': error.errors(),
})
return {
'status': 400,
'body': {
'error': 'Invalid request body',
'data': error.errors(),
},
}
except Exception as error:
context.logger.error('Error while performing request', {
'error': str(error),
'body': req.get('body'), # make sure you don't include sensitive data in the logs
})
return {
'status': 500,
'body': {'error': 'Internal Server Error'}
}
```