<critical_patterns>
CORRECT (Payload v3 pattern):
handler: async (req) => {
const data = await req.json()
return Response.json({ success: true }, { status: 200 })
}
Alternative using helper:
import { addDataAndFileToRequest } from 'payload'
handler: async (req) => {
await addDataAndFileToRequest(req)
const data = req.data // now available
return Response.json({ success: true })
}
CORRECT:
return Response.json({ message: 'success' }, { status: 200 })
return Response.json({ message: 'success' }) // defaults to 200
Error responses:
return Response.json({ error: 'Not found' }, { status: 404 })
return Response.json({ error: 'Unauthorized' }, { status: 401 })
CORRECT:
const cookieHeader = req.headers.get('cookie')
const cookies = parseCookies(cookieHeader)
const token = cookies.token
CORRECT:
handler: async (req) => { ... }
// or with PayloadRequest type
handler: async (req: PayloadRequest) => { ... }
WRONG:
req.payload.logger.info('User created', { userId: 123 })
req.payload.logger.error('Failed to save:', error)
CORRECT:
req.payload.logger.info({ userId: 123 }, 'User created')
req.payload.logger.error({ error: String(error) }, 'Failed to save')
For simple messages (no object):
req.payload.logger.info('Operation completed') // This is fine
export const myEndpoint: Endpoint = { path: '/my-endpoint', method: 'post', handler: async (req: PayloadRequest) => { try { const data = await req.json()
// Access payload
const result = await req.payload.find({
collection: 'users',
where: { email: { equals: data.email } }
})
req.payload.logger.info({ count: result.totalDocs }, 'Found users')
return Response.json({
success: true,
data: result
})
} catch (error) {
req.payload.logger.error({ error: String(error) }, 'Endpoint failed')
return Response.json({ error: 'Internal error' }, { status: 500 })
}
} }
</pattern>
</critical_patterns>
<common_errors>
| Error | Cause | Fix |
|-------|-------|-----|
| `req.data is undefined` | Using v2 pattern | Use `await req.json()` |
| `req.cookies is undefined` | Using v2 pattern | Parse from `req.headers.get('cookie')` |
| `res.json is not a function` | Handler has wrong signature | Return `Response.json()` |
| `TS2769: No overload matches` on logger | Wrong argument order | Put object first, message second |
| `Type 'string \| string[]' not assignable` | relationTo can be array | Use `Array.isArray(x) ? x[0] : x` |
</common_errors>
<quick_reference>
```typescript
// Request body
const data = await req.json()
// Response
return Response.json({ data }, { status: 200 })
// Logger
req.payload.logger.info({ key: value }, 'Message')
req.payload.logger.error({ error: String(e) }, 'Error message')
// Handler signature
handler: async (req: PayloadRequest) => { ... }
</quick_reference>
<success_criteria>
- All endpoints use
await req.json()for body access - All responses use
Response.json() - All logger calls have object first, message second
- Handler signature is
(req)not(req, res) - TypeScript compiles without errors </success_criteria>
