Request Schemas

Request schemas define what an operation accepts. In Typeweaver, request data lives under request and is split into four parts:

  • param for path parameters
  • query for query string values
  • header for request headers
  • body for the request body

Those schemas drive both generated request types and generated request validators.

The request shape

request: {
  param?: z.object(...),
  query?: z.object(...),
  header?: z.object(...),
  body?: z.object(...),
}

Only include the parts an operation actually uses.

param: path parameters

Path parameters use :paramName syntax in the path and must match the keys in request.param.

const GetTodoDefinition = defineOperation({
  operationId: "GetTodo",
  summary: "Get todo",
  method: HttpMethod.GET,
  path: "/todos/:todoId",
  request: {
    param: z.object({
      todoId: z.string(),
    }),
  },
  responses: [/* ... */],
});

If the path says :todoId, the schema must use todoId too.

query: filters and paging

query is a good fit for optional filters, search terms, and pagination on read operations.

const ListTodosDefinition = defineOperation({
  operationId: "ListTodos",
  summary: "List todos",
  method: HttpMethod.GET,
  path: "/todos",
  request: {
    query: z.object({
      status: z.enum(["open", "done"]).optional(),
      page: z.coerce.number().int().positive().optional(),
    }),
  },
  responses: [/* ... */],
});

For most GET endpoints, param and query are the main request parts.

header: request metadata

Use header when the contract requires specific headers.

const GetTodoDefinition = defineOperation({
  operationId: "GetTodo",
  summary: "Get todo",
  method: HttpMethod.GET,
  path: "/todos/:todoId",
  request: {
    param: z.object({
      todoId: z.string(),
    }),
    header: z.object({
      authorization: z.string(),
    }),
  },
  responses: [/* ... */],
});

body: create and update input

body is usually where POST, PUT, and PATCH operations put their main input.

const CreateTodoDefinition = defineOperation({
  operationId: "CreateTodo",
  summary: "Create todo",
  method: HttpMethod.POST,
  path: "/todos",
  request: {
    body: z.object({
      title: z.string().min(1),
      description: z.string().optional(),
    }),
  },
  responses: [/* ... */],
});

As a rule of thumb:

  • GET operations usually use param and query
  • POST, PUT, and PATCH operations often use body, sometimes with param

Why this matters

Once the request schema is in the spec, Typeweaver can generate artifacts such as:

  • <OperationId>Request.ts
  • <OperationId>RequestValidator.ts

That keeps your handlers, clients, and validators aligned with one source of truth.

This page stays focused on structure. For how invalid input is enforced at runtime, see Validation.

Was this page helpful?