Context Object
The Context object wraps the native Request and provides convenient methods for accessing request data, setting response headers, and sending responses.
What is Context?
Context is a wrapper around Deno's native Request object. Every incoming request is wrapped in one Context that flows from middleware to route handler. Instead of working with raw Request directly, you use Context which gives you:
- Lazy parsing - Data is parsed only when you access it
- Convenient methods - Simple APIs for common operations
- Response utilities - Built-in methods for sending responses
- Header management - Easy response header manipulation
Why Using Context?
Context avoids multiple parsing and repeated processing during the request lifecycle. The handler receives one Context object that persists through the entire lifecycle — from middleware to route handler.
Creating Context
Deserve creates Context automatically when requests arrive:
// 1. Import Context type
import type { Context } from '@neabyte/deserve'
// 2. Handler receives ctx (Deserve creates it per request)
export function GET(ctx: Context): Response {
return ctx.send.json({ message: 'Hello' })
}Context Structure
Context wraps several key pieces:
- Original Request - Access via
ctx.request - Parsed URL - Used internally for query params
- Route Parameters - Extracted from dynamic routes
- Response Headers - Set before sending response
Lazy Parsing
Context uses lazy parsing for performance: data (query, body, cookie, header) is parsed only when you call the method that uses it, then the result is cached for later calls.
export function GET(ctx: Context): Response {
// 1. Query not parsed until ctx.query() is called
const query = ctx.query()
// 2. Result cached; next call uses cache
// 3. Body parsed on first access (based on Content-Type)
const body = await ctx.body()
// 4. Send combined query + body
return ctx.send.json({ query, body })
}Request Data Access
Access request data through Context methods:
- Query Parameters -
ctx.query(),ctx.queries() - Route Parameters -
ctx.param(),ctx.params() - Headers -
ctx.header(),ctx.headers - Cookies -
ctx.cookie() - Body -
ctx.body(),ctx.json(),ctx.formData(),ctx.text(),ctx.arrayBuffer(),ctx.blob() - URL Information -
ctx.url,ctx.pathname
Response Utilities
Send responses using ctx.send:
ctx.send.json()- JSON responsectx.send.text()- Plain textctx.send.html()- HTML contentctx.send.file()- File downloadctx.send.data()- In-memory data downloadctx.send.stream()- Stream response (ReadableStream)ctx.send.redirect()- Redirectctx.send.custom()- Custom responsectx.handleError()- Error handling
You can also use ctx.redirect() directly as a convenience method:
export function GET(ctx: Context): Response {
// 1. Redirect 301 to new path (shorthand for ctx.send.redirect)
return ctx.redirect('/new-location', 301)
}Response Headers
Set response headers before sending:
export function GET(ctx: Context): Response {
ctx.setHeader('X-Custom', 'value')
ctx.setHeader('Cache-Control', 'no-cache')
return ctx.send.json({ data: 'test' })
}Setting Multiple Headers
Use setHeaders() to set multiple headers at once:
export function GET(ctx: Context): Response {
ctx.setHeaders({
'X-Custom': 'value',
'Cache-Control': 'no-cache',
'X-Request-ID': 'abc123'
})
return ctx.send.json({ data: 'test' })
}Reading Response Headers
Access all response headers that have been set:
export function GET(ctx: Context): Response {
ctx.setHeader('X-Custom', 'value')
ctx.setHeader('Cache-Control', 'no-cache')
const headers = ctx.responseHeadersMap // { 'X-Custom': 'value', 'Cache-Control': 'no-cache' }
return ctx.send.json({ data: 'test' })
}URL and Pathname
Get URL information directly:
ctx.url- Full URL stringctx.pathname- Pathname portion of URL (e.g.,/api/users/123)
export function GET(ctx: Context): Response {
const fullUrl = ctx.url // 'http://localhost:8000/api/users/123?sort=name'
const path = ctx.pathname // '/api/users/123'
return ctx.send.json({ path, fullUrl })
}Error Handling
Handle errors consistently using ctx.handleError():
export function GET(ctx: Context): Response {
try {
if (!isAuthorized) {
return ctx.handleError(401, new Error('Unauthorized'))
}
return ctx.send.json({ data: 'success' })
} catch (error) {
return ctx.handleError(500, error as Error)
}
}How It Works
ctx.handleError() respects your global error handler set with router.catch():
- If
router.catch()is defined - Uses your custom error handler - If no error handler - Returns a simple response with the status code
Use in Middleware
Middleware can use ctx.handleError() to trigger error handling:
router.use(async (ctx, next) => {
if (!isValid) {
return ctx.handleError(401, new Error('Unauthorized'))
// This will use router.catch() if defined
}
return await next()
})Context Lifecycle
- Request arrives - Deserve creates Context with Request and URL
- Route matching - Route parameters extracted and added to Context
- Middleware execution - Context passed through middleware chain
- Route handler - Your handler receives Context
- Response sent - Context methods used to build Response