Skip to content
RELKIT
Esc
↑↓navigate↵open⌘Jpreview
On this page

Request mapping

Map HTTP path, query, header, cookie, JSON, and multipart values into validated domain input.

Map HTTP parameters, headers, and body fields into the input expected by your function. The handler receives validated business data, not a framework request object. Keep parsing choices on the route and reusable constraints in the function’s schema.

Start with inference

When request is omitted, RELKIT projects the target’s object input schema:

Method Properties matching path parameters Remaining properties
GET, HEAD, DELETE, OPTIONS Read from the path Read from query parameters
POST, PUT, PATCH Read from the path Read from the JSON body

For a write route with accept: "multipart/form-data", remaining properties map from multipart fields instead. Array properties use repeated fields; scalar properties use one field. See Uploads.

Thus POST /echo maps a JSON message into the Echo function without an explicit descriptor:

/** Maps the HTTP endpoint to its owning service's public member. */
import { defineRoute } from "@relkit/app/routes";
import echo from "@app/services/echo/service.js";

export const POST = defineRoute({
  target: echo.echo,
});

Path, query, header, and cookie scalar values arrive as strings. Use a schema that intentionally coerces a number when needed; do not assume ?quantity=2 is already a JavaScript number. Catch-all path values are arrays of decoded segments, not slash-joined strings.

Replace inference when the HTTP contract differs

Use http.input for headers, cookies, renamed fields, nesting, or explicit transformations:

/**
 * Maps order HTTP operations to public service members with the existing request
 * and rate-limit policies. The rate-limit reference addresses the declared cache
 * by its stable identity without importing a private service implementation.
 */
import { defineServiceRoutes, http } from "@relkit/app/routes";
import { z } from "@relkit/app/schema";
import orders from "../../services/orders/service.js";

export const { GET, POST } = defineServiceRoutes(orders, {
  GET: "searchOrders",
  POST: {
    member: "createOrder",
    request: http.input({
      orderId: http.header("idempotency-key"),
      customerEmail: http.header("x-customer-email"),
      sku: http.body("sku"),
      quantity: http.body("quantity"),
    }),
    rateLimit: {
      limit: 20,
      windowMs: 60_000,
      key: http.header("x-customer-email"),
      store: {
        ref: { kind: "cache", id: "orders.rate-limits" },
        key: z.string(),
        value: z.number().int().nonnegative(),
      },
    },
    successStatus: 201,
  },
});

The public Create Order input remains independent of HTTP. This route chooses to obtain orderId and customerEmail from headers, and sku and quantity from the JSON body.

An explicit request replaces inference completely. Include every required input field, including matching path properties; RELKIT does not merge omitted fields back into the mapping. If the input schema cannot be projected as an object, the compiler asks for an explicit mapping. The mapping root is still http.input, which constructs an object: wrap a scalar or array body in a named function-input property rather than expecting arbitrary non-object root input.

Choose a source for each input property

Helper Value supplied
http.path(name) One path parameter
http.pathSegments(name) Catch-all segment array
http.query(name) One query parameter
http.header(name) One header, matched case-insensitively
http.cookie(name) One cookie
http.body(name) One top-level JSON body field
http.body() or http.wholeBody() The whole parsed JSON body, assigned to a property
http.multipart(name) One multipart text field or file
http.multipartAll(name) Repeated multipart fields or files
http.constant(value) A declared constant
http.nested(fields) An object assembled from nested mappings

http.optional(source) permits absence; http.default(source, value) supplies a fallback for absence. Neither makes malformed or duplicated scalar input valid. Keep the resulting optional/default shape consistent with the function schema.

Scaffold a request transform

From your application’s root:

bun run relkit add transform normalize-id

Creates src/routes/transforms/normalize-id.transform.ts with a string schema. Specialize the schema and reference the descriptor with http.transform in a route’s request mapping; the CLI does not attach it to routes automatically. Transforms belong to the route platform, so no service selection is needed.

For every transform scaffolding flag, see the transform command reference.

Use http.transform(transform, source) with a compiler-known defineTransform descriptor, not an inline callback. Put HTTP transforms under src/routes/transforms/. The commerce transform is schema-based:

import { defineTransform } from "@relkit/app/routes";
import { z } from "@relkit/app/schema";

export default defineTransform({ schema: z.string().min(1) });

The dynamic Orders route applies it to http.path("orderId"). See the Routes API reference for helper signatures.

Verify the transport boundary

For an app containing the Echo tutorial, with the backend running on port 3000:

curl -i -X POST http://localhost:3000/echo \
  -H 'content-type: application/json' \
  -d '{"message":"hello"}'

Expect 200. Repeat with an empty message, a missing field, and malformed JSON; expect validation 422 before the function handler. For explicit mappings, also test a missing header, repeated scalar query fields, and each optional/default branch. Unsupported body content types are mapping failures, not automatically a 415 response.

HTTP middleware can inspect native request details, but function calls from jobs, events, tools, or other domains do not receive a fabricated HTTP request. Pass business data explicitly.

Reference and source