Spec Authoring
Your Typeweaver spec is the source of truth for your API contract. It defines the resources, operations, request schemas, and response schemas that generated clients, handlers, types, and validators all rely on.
That means the spec should describe the contract clearly:
- resources
- operations
- request schemas
- response schemas
Runtime behavior, such as fetching from a database or calling another service, belongs in your handlers, not in the spec.
The mental model
defineSpec(...)groups your API by resource.- Each resource contains operations.
- Each operation defines one HTTP action.
- Each operation lists the responses it can return.
If you are unsure where something belongs, ask: “Is this part of the contract, or part of the implementation?” If it is contract data, it belongs in the spec.
A small resource-based spec
import {
defineOperation,
defineResponse,
defineSpec,
HttpMethod,
HttpStatusCode,
} from "@rexeus/typeweaver-core";
import { z } from "zod";
const todoSchema = z.object({
id: z.string(),
title: z.string(),
completed: z.boolean(),
});
const GetTodoDefinition = defineOperation({
operationId: "GetTodo",
method: HttpMethod.GET,
summary: "Get todo",
path: "/todos/:todoId",
request: {
param: z.object({
todoId: z.string(),
}),
},
responses: [
defineResponse({
name: "GetTodoSuccess",
statusCode: HttpStatusCode.OK,
description: "Todo retrieved successfully",
body: todoSchema,
}),
],
});
const CreateTodoDefinition = defineOperation({
operationId: "CreateTodo",
method: HttpMethod.POST,
summary: "Create todo",
path: "/todos",
request: {
body: z.object({
title: z.string().min(1),
}),
},
responses: [
defineResponse({
name: "CreateTodoSuccess",
statusCode: HttpStatusCode.CREATED,
description: "Todo created successfully",
body: todoSchema,
}),
],
});
export const spec = defineSpec({
resources: {
todo: {
operations: [GetTodoDefinition, CreateTodoDefinition],
},
},
});
Folder structure is up to you
You can keep everything in one file or split operations, schemas, and shared responses into separate files. Typeweaver cares about the spec entrypoint you pass to the CLI, not about a required folder layout.
In other words, this is valid:
api/spec/index.tsapi/spec/todo/GetTodoDefinition.tsapi/spec/todo/CreateTodoDefinition.tsapi/spec/shared/sharedResponses.ts
Or a completely different structure, as long as the entrypoint exports your defineSpec(...) result.
Naming basics
- Prefer singular camelCase resource names such as
todo. - Use one
operationIdstyle consistently across your spec. PascalCase names such asGetTodoand camelCase names such asgetTodoare both supported. - These docs use PascalCase
operationIdvalues so the generated class names line up with what you see in examples. snake_caseandkebab-caseare not supported for resource names or operation IDs.
This page is about what belongs in the spec at all. For deeper guidance on how to split operations into resources, see Resources and Operations.
What to read next
- First Operation
- Response Schemas
- Generate and Run
- Troubleshooting — common spec validation errors and how to fix them