Documentation
Params & Query
Kynetra FX exposes route parameters on ctx.params and query-string values via ctx.query(). Both are automatically URL-decoded. Wildcard segments are captured as ctx.params['*'].
Route params#
Named segments prefixed with : are captured into ctx.params as strings. The object is typed as Record<string, string>.
import { createFX } from '@kynetra/fx' const app = createFX() app.get('/users/:id', (ctx) => { const id = ctx.params.id // string return ctx.json({ id })}) app.get('/orgs/:orgId/teams/:teamId', (ctx) => { const { orgId, teamId } = ctx.params return ctx.json({ orgId, teamId })})Note
Wildcard capture#
A trailing * segment matches the remainder of the path. The matched portion is stored as ctx.params['*'].
app.get('/static/*', (ctx) => { const filePath = ctx.params['*'] // 'images/logo.png', 'css/main.css', etc. return ctx.text('Serving: ' + filePath)})The wildcard value does not include a leading slash. For the URL /static/css/main.css, ctx.params['*'] is css/main.css.
Query strings#
ctx.query(name) returns the value of a single query parameter as a string, or null if absent. Call ctx.query() with no arguments to get all parameters as a Record<string, string>.
// GET /search?q=typescript&page=2app.get('/search', (ctx) => { const q = ctx.query('q') // 'typescript' | null const page = ctx.query('page') // '2' | null // Or get everything at once: const all = ctx.query() // { q: 'typescript', page: '2' } return ctx.json({ q, page, all })})Tip
query field on app.route(). The framework validates the raw string values against your schema and exposes the typed result on ctx.validatedQuery. See Contracts.URL decoding#
Both params and query values are automatically URL-decoded by Kynetra FX before they reach your handler. You do not need to call decodeURIComponent.
// Request: GET /files/my%20document%20(2024).pdfapp.get('/files/:name', (ctx) => { console.log(ctx.params.name) // 'my document (2024).pdf' return ctx.text(ctx.params.name)})Accessing the raw URL#
If you need the full parsed URL object (pathname, search, host, etc.), it is available as ctx.url — a standard URL instance:
app.get('/info', (ctx) => { const { pathname, search, host, searchParams } = ctx.url // searchParams is a URLSearchParams, supports getAll() for multi-value const tags = searchParams.getAll('tag') // string[] return ctx.json({ pathname, search, host, tags })})Multi-value query params#
ctx.query(name) returns only the first value when a key appears multiple times. Use ctx.url.searchParams.getAll(name) to collect all values.
// GET /items?tag=ts&tag=edge&tag=wasmapp.get('/items', (ctx) => { const tags = ctx.url.searchParams.getAll('tag') // ['ts', 'edge', 'wasm'] return ctx.json({ tags })})Type-safe params with contracts#
For production APIs, validate and type-coerce params using the contract layer. The query schema is validated automatically:
import { createFX, fx } from '@kynetra/fx' const app = createFX() app.route({ method: 'GET', path: '/users', query: fx.object({ page: fx.optional(fx.number().min(1)), limit: fx.optional(fx.number().min(1).max(100)), role: fx.optional(fx.enum(['admin', 'member'])), }), handler(ctx) { // ctx.validatedQuery is typed: { page?: number; limit?: number; role?: 'admin' | 'member' } const { page = 1, limit = 20, role } = ctx.validatedQuery return ctx.json({ page, limit, role }) },})