Cookies
OPRA parses incoming cookies automatically and makes them available on ctx.cookies. Outgoing cookies are set via ctx.response.cookie(). Expected request cookies can be declared on the operation so they appear in the API schema.
Reading request cookies
ctx.cookies
The parsed cookie map for the current request. Values are strings unless the cookie was serialized as JSON (in which case the adapter deserializes it automatically).
const sessionId = ctx.cookies['session-id'];
const lang = ctx.cookies['preferred-lang'] ?? 'en';
ctx.cookies is a convenience alias for ctx.request.cookies.
Declaring expected request cookies
Use .Cookie() in the operation decorator chain to declare a cookie parameter. Declared cookies appear in the API schema and support the same options as other parameter types.
@(HttpOperation.GET()
.Cookie('session-id', { type: 'string', required: true })
.Cookie('preferred-lang', 'string')
.Response(200, { type: Customer }))
async get(ctx: HttpContext) {
const sessionId = ctx.cookies['session-id'];
const lang = ctx.cookies['preferred-lang'] ?? 'en';
// ...
}
.Cookie() options:
| Option | Type | Description |
|---|---|---|
type | Type | string | OPRA type for the cookie value. |
required | boolean | Whether the cookie must be present. |
default | any | Default value when the cookie is absent. |
deprecated | boolean | string | Mark the cookie as deprecated. |
description | string | Human-readable description for the schema. |
Setting response cookies
ctx.response.cookie(name, value, options?)
cookie(name: string, value: any, options?: CookieOptions): this
Appends a Set-Cookie header to the response. Returns this for chaining.
ctx.response.cookie('session-id', token, {
httpOnly: true,
secure: true,
maxAge: 3_600_000, // 1 hour in ms
sameSite: 'strict',
path: '/',
});
Object values are serialized to JSON automatically. Signed cookies require options.signed: true and a secret.
ctx.response.clearCookie(name, options?)
clearCookie(name: string, options?: CookieOptions): this
Expires the cookie immediately. The path defaults to / when omitted. Pass the same domain and path used when the cookie was set to ensure the browser deletes the right cookie:
ctx.response.clearCookie('session-id', { path: '/' });
Cookie options
| Option | Type | Description |
|---|---|---|
maxAge | number | Expiry in milliseconds from now. Converted to an Expires date and a Max-Age value (in seconds) on the header. |
expires | Date | Explicit expiry date. Takes precedence over maxAge. |
httpOnly | boolean | When true, the cookie is inaccessible to client-side JavaScript. |
secure | boolean | When true, the cookie is sent only over HTTPS. |
sameSite | 'strict' | 'lax' | 'none' | boolean | Controls cross-site sending. 'none' requires secure: true. |
path | string | Cookie path scope. Defaults to '/'. |
domain | string | Cookie domain scope. |
signed | boolean | Sign the value with secret. The adapter prefixes the value with s:. |
secret | string | Secret used for signing. Falls back to req.secret if not provided here. |
encode | (val: string) => string | Custom encoding function for the cookie value. |
Complete example
import { HttpController, HttpOperation } from '@opra/common';
import { HttpContext } from '@opra/http';
@HttpController({ path: 'auth' })
export class AuthController {
@(HttpOperation.POST({ path: 'login' })
.RequestContent({ type: LoginDto })
.Response(200))
async login(ctx: HttpContext) {
const { email, password } = await ctx.getBody<LoginDto>();
const token = await this.authService.createSession(email, password);
ctx.response.cookie('session', token, {
httpOnly: true,
secure: true,
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
sameSite: 'strict',
path: '/',
});
}
@(HttpOperation.POST({ path: 'logout' })
.Cookie('session', { type: 'string', required: true })
.Response(204))
async logout(ctx: HttpContext) {
const token = ctx.cookies['session'];
await this.authService.revokeSession(token);
ctx.response.clearCookie('session', { path: '/' });
}
@(HttpOperation.GET({ path: 'me' })
.Cookie('session', { type: 'string', required: true })
.Response(200, { type: User }))
async me(ctx: HttpContext) {
return this.authService.getUserBySession(ctx.cookies['session']);
}
}