122 lines
2.9 KiB
Plaintext
122 lines
2.9 KiB
Plaintext
---
|
|
description: How to handle errors in a Motia project
|
|
globs:
|
|
alwaysApply: true
|
|
---
|
|
|
|
# Error Handling Guide
|
|
|
|
Errors happen, but we need to handle them gracefully. Make sure you create a custom error class for your project, underneath `/src/errors/` folder.
|
|
|
|
## Good practices
|
|
|
|
- Use Custom error to return errors to the client.
|
|
- Anything that is not the error class, should be logged with `logger.error`. And root cause should be omitted to the client.
|
|
|
|
## Create a custom Error class
|
|
|
|
Name: `/src/errors/base.error.ts`
|
|
|
|
```typescript
|
|
export class BaseError extends Error {
|
|
public readonly status: number
|
|
public readonly code: string
|
|
public readonly metadata: Record<string, any>
|
|
|
|
constructor(
|
|
message: string,
|
|
status: number = 500,
|
|
code: string = 'INTERNAL_SERVER_ERROR',
|
|
metadata: Record<string, any> = {}
|
|
) {
|
|
super(message)
|
|
this.name = this.constructor.name
|
|
this.status = status
|
|
this.code = code
|
|
this.metadata = metadata
|
|
|
|
// Maintains proper stack trace for where our error was thrown
|
|
Error.captureStackTrace(this, this.constructor)
|
|
}
|
|
|
|
toJSON() {
|
|
return {
|
|
error: {
|
|
name: this.name,
|
|
message: this.message,
|
|
code: this.code,
|
|
status: this.status,
|
|
...(Object.keys(this.metadata).length > 0 && { metadata: this.metadata }),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Then create sub class for specific errors that are commonly thrown in your project.
|
|
|
|
Name: `/src/errors/not-found.error.ts`
|
|
|
|
```typescript
|
|
import { BaseError } from './base.error'
|
|
|
|
export class NotFoundError extends BaseError {
|
|
constructor(message: string = 'Not Found', metadata: Record<string, any> = {}) {
|
|
super(message, 404, 'NOT_FOUND', metadata)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Core Middleware
|
|
|
|
Make sure you create a core middleware that will be added to ALL API Steps.
|
|
|
|
File: `/src/middlewares/core.middleware.ts`
|
|
|
|
```typescript
|
|
import { ApiMiddleware } from 'motia'
|
|
import { ZodError } from 'zod'
|
|
import { BaseError } from '../errors/base.error'
|
|
|
|
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,
|
|
},
|
|
}
|
|
} else if (error instanceof BaseError) {
|
|
logger.error('BaseError', {
|
|
status: error.status,
|
|
code: error.code,
|
|
metadata: error.metadata,
|
|
name: error.name,
|
|
message: error.message,
|
|
})
|
|
|
|
return { status: error.status, body: error.toJSON() }
|
|
}
|
|
|
|
logger.error('Error while performing request', {
|
|
error,
|
|
body: req.body,
|
|
stack: error.stack,
|
|
})
|
|
|
|
return { status: 500, body: { error: 'Internal Server Error' } }
|
|
}
|
|
}
|
|
``` |