<critical_patterns>
<pattern name="request-body-access"> **WRONG (Payload v2 pattern):** ```typescript handler: async (req, res) => { const data = req.data // undefined in v3! res.status(200).json({ success: true }) } ```CORRECT (Payload v3 pattern):
typescript1handler: async (req) => { 2 const data = await req.json() 3 return Response.json({ success: true }, { status: 200 }) 4}
Alternative using helper:
</pattern> <pattern name="response-format"> **WRONG:** ```typescript res.status(200).json({ message: 'success' }) res.json({ message: 'success' }) ```typescript1import { addDataAndFileToRequest } from 'payload' 2 3handler: async (req) => { 4 await addDataAndFileToRequest(req) 5 const data = req.data // now available 6 return Response.json({ success: true }) 7}
CORRECT:
typescript1return Response.json({ message: 'success' }, { status: 200 }) 2return Response.json({ message: 'success' }) // defaults to 200
Error responses:
</pattern> <pattern name="cookie-access"> **WRONG:** ```typescript const token = req.cookies.token // undefined in v3! ```typescript1return Response.json({ error: 'Not found' }, { status: 404 }) 2return Response.json({ error: 'Unauthorized' }, { status: 401 })
CORRECT:
</pattern> <pattern name="handler-signature"> **WRONG:** ```typescript handler: async (req, res) => { ... } ```typescript1const cookieHeader = req.headers.get('cookie') 2const cookies = parseCookies(cookieHeader) 3const token = cookies.token
CORRECT:
</pattern> <pattern name="logger-api"> Payload uses pino logger. The signature is: `logger.info(object, message)` NOT `logger.info(message, object)`typescript1handler: async (req) => { ... } 2// or with PayloadRequest type 3handler: async (req: PayloadRequest) => { ... }
WRONG:
typescript1req.payload.logger.info('User created', { userId: 123 }) 2req.payload.logger.error('Failed to save:', error)
CORRECT:
typescript1req.payload.logger.info({ userId: 123 }, 'User created') 2req.payload.logger.error({ error: String(error) }, 'Failed to save')
For simple messages (no object):
</pattern> <pattern name="endpoint-definition"> Complete endpoint example: ```typescript import type { Endpoint, PayloadRequest } from 'payload'typescript1req.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>