Documentation
Cookies & Headers
Kynetra FX provides ctx.cookie(name) to read request cookies and ctx.setCookie(name, value, opts?) to write response cookies. Request headers are read with ctx.header(name) and response headers are written with ctx.set(name, value).
Reading request cookies#
ctx.cookie(name) parses the incoming Cookie request header and returns the value of the named cookie as a string, or null if absent.
import { createFX } from '@kynetra/fx' const app = createFX() app.get('/me', (ctx) => { const sessionId = ctx.cookie('session') // string | null if (!sessionId) { return ctx.json({ error: 'not logged in' }, { status: 401 }) } return ctx.json({ sessionId })})Setting response cookies#
ctx.setCookie(name, value, opts?) adds a Set-Cookie header to the outgoing response. Call it before returning a response from the handler.
app.post('/login', async (ctx) => { const { username, password } = await ctx.jsonBody() // Authenticate (pseudo-code) const sessionId = await createSession(username, password) ctx.setCookie('session', sessionId, { httpOnly: true, secure: true, sameSite: 'Lax', path: '/', maxAge: 60 * 60 * 24 * 7, // 1 week in seconds }) return ctx.json({ ok: true })})Cookie options#
| Name | Type | Description |
|---|---|---|
| httpOnly | boolean | Prevents JavaScript access via document.cookie. Recommended for session cookies. |
| secure | boolean | Send only over HTTPS. Always set in production. |
| sameSite | 'Strict' | 'Lax' | 'None' | Cross-site request policy. Use Lax for most session cookies; None requires Secure. |
| path | string | Cookie path scope. Defaults to '/'. |
| domain | string | Cookie domain scope. Omit to scope to the exact host. |
| maxAge | number | Lifetime in seconds from now. Takes precedence over expires. |
| expires | Date | Absolute expiry date. Use maxAge instead for most cases. |
Deleting cookies#
To delete a cookie, set it with maxAge: 0 (or a past expiry). The browser will immediately discard it.
app.post('/logout', (ctx) => { ctx.setCookie('session', '', { httpOnly: true, secure: true, path: '/', maxAge: 0, }) return ctx.json({ ok: true })})Multiple cookies#
Call ctx.setCookie() multiple times to set multiple cookies. Each call appends a new Set-Cookie header.
app.post('/setup', (ctx) => { ctx.setCookie('theme', 'dark', { path: '/', maxAge: 86400 * 365 }) ctx.setCookie('lang', 'en', { path: '/', maxAge: 86400 * 365 }) return ctx.json({ ok: true })})Request headers#
ctx.header(name) returns a single request header value (case-insensitive) or null if absent. For multiple values, access ctx.req.headers directly — it is a standard Headers object.
app.get('/info', (ctx) => { const auth = ctx.header('authorization') const ct = ctx.header('content-type') const tenant = ctx.header('x-tenant-id') // Standard Headers API for multi-value or iteration const all: Record<string, string> = {} ctx.req.headers.forEach((v, k) => { all[k] = v }) return ctx.json({ auth, ct, tenant, all })})Response headers#
Use ctx.set(name, value) to set a single response header and ctx.append(name, value) to append to an existing one. You can also pass headers in the init object of any response helper.
// Via ctx.set()app.get('/data', (ctx) => { ctx.set('Cache-Control', 'public, max-age=300') ctx.set('X-Powered-By', 'Kynetra FX') return ctx.json({ value: 42 })}) // Via init objectapp.get('/nocache', (ctx) => ctx.json({ ts: Date.now() }, { headers: { 'Cache-Control': 'no-store' }, })) // Append Vary header in middlewareapp.use(async (ctx, next) => { const res = await next() ctx.append('Vary', 'Accept-Encoding') return res})Session auth with cookies#
For full session-based authentication including signed cookie JWT sessions, see the Auth page. The sessionAuth() strategy from @kynetra/fx-auth handles signing and verification automatically.
import { createFX } from '@kynetra/fx'import { sessionAuth, signSession, requireAuth } from '@kynetra/fx-auth' const app = createFX()const SESSION_SECRET = 'my-32-char-or-longer-secret-key!' app.post('/login', async (ctx) => { // Verify credentials (pseudo-code)... const token = await signSession({ sub: 'user-42', roles: ['member'] }, SESSION_SECRET) ctx.setCookie('session', token, { httpOnly: true, secure: true, sameSite: 'Lax', path: '/', maxAge: 86400 * 7, }) return ctx.json({ ok: true })}) const strategy = sessionAuth({ secret: SESSION_SECRET }) app.get('/me', requireAuth([strategy]), (ctx) => { return ctx.json({ ok: true })})Note
httpOnly: true and secure: true for session cookies in production. Omitting these is a common security mistake.