Kynetra FX

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>.

app.ts
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

Params are always strings — you are responsible for parsing integers, UUIDs, or other types. Validate them with the contract layer (Contracts) or manually inside the handler.

Wildcard capture#

A trailing * segment matches the remainder of the path. The matched portion is stored as ctx.params['*'].

app.ts
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>.

app.ts
// GET /search?q=typescript&page=2
app.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

For typed and validated query parameters, use the 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.

app.ts
// Request: GET /files/my%20document%20(2024).pdf
app.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.ts
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.

app.ts
// GET /items?tag=ts&tag=edge&tag=wasm
app.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:

app.ts
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 })
},
})
Params & Query · Kynetra FX