@rexeus/typeweaver
v0.11.0
Published
π§΅β¨ Typeweaver CLI. Entry point into the Typeweaver framework to scaffold, validate, and generate API assets.
Downloads
2,701
Maintainers
Readme
π§΅β¨ @rexeus/typeweaver
Typeweaver is a type-safe HTTP API framework built for API-first development with a focus on developer experience. Use typeweaver to specify your HTTP APIs in TypeScript and Zod, and generate clients, validators, routers, and more β¨
π₯ Installation
# Node.js (npm)
npm install -D @rexeus/typeweaver
npm install @rexeus/typeweaver-core
# Node.js (pnpm)
pnpm add -D @rexeus/typeweaver
pnpm add @rexeus/typeweaver-core
# Deno
deno add npm:@rexeus/typeweaver npm:@rexeus/typeweaver-core
# Bun
bun add -D @rexeus/typeweaver
bun add @rexeus/typeweaver-coreNow you are ready to start building! Check out Quickstart
π― Why typeweaver?
- π Define once, generate everything: API contracts in Zod become clients, servers, validators, and docs.
- π Resource-based architecture: APIs organized by resources (like user, todo, project, tag, blog-post, etc.), each with its operations and generated components (e.g. clients). Scale naturally as your API grows.
- π Real type safety: From API definition to client usage, every request and response is fully
typed. No more
anytypes sneaking in. - β Automatic validation: Invalid requests never reach your code.
- π Bring your own framework: Ready-made adapters for popular frameworks, extensible plugin system for everything else.
- π Finally, DX that doesn't suck: One schema, no duplication, pure TypeScript.
π Available Plugins
| Package | Description | Version |
| ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| @rexeus/typeweaver-types | Plugin for request/response types and validation - the foundation for all other plugins and always included | |
| @rexeus/typeweaver-clients | Plugin for HTTP clients using fetch |
|
| @rexeus/typeweaver-server | Plugin for a zero-dependency, Fetch API-native server with built-in routing and middleware |
|
| @rexeus/typeweaver-hono | Plugin for Hono routers |
|
| @rexeus/typeweaver-aws-cdk | Plugin for AWS CDK constructs for API Gateway V2 |
|
More plugins are planned. If you want to build your own, check out the plugin system
β¨οΈ CLI
Generate TypeScript code from a spec entrypoint file:
# Node.js (npm)
npx typeweaver generate --input ./api/spec/index.ts --output ./api/generated --plugins clients
# Node.js (pnpm)
pnpx typeweaver generate --input ./api/spec/index.ts --output ./api/generated --plugins clients
# Deno
deno run -A npm:@rexeus/typeweaver generate --input ./api/spec/index.ts --output ./api/generated --plugins clients
# Bun
bunx typeweaver generate --input ./api/spec/index.ts --output ./api/generated --plugins clientsNote: Deno may require the
--sloppy-importsflag or equivalent configuration indeno.jsonwhen your API definitions use extensionless TypeScript imports.
βοΈ Options
--input, -i <path>: Spec entrypoint file (required)--output, -o <path>: Output directory for generated code (required)--config, -c <path>: Configuration file path (.js,.mjs, or.cjs, optional)--plugins, -p <plugins>: Comma-separated list of plugins to use (e.g., "clients,hono" or "all" for all plugins)--format / --no-format: Enable/disable code formatting with oxfmt (default: true)--clean / --no-clean: Enable/disable output directory cleaning (default: true)
π Configuration File
Create a JavaScript config file (for example typeweaver.config.mjs) for more complex
configurations:
export default {
input: "./api/spec/index.ts",
output: "./api/generated",
plugins: ["clients", "hono", "aws-cdk"],
format: true,
clean: true,
};Then run:
npx typeweaver generate --config ./typeweaver.config.mjsReplace
npxwithpnpx,deno run -A npm:@rexeus/typeweaver, orbunxdepending on your runtime.TypeScript config files (
.ts,.mts,.cts) are no longer supported by the published CLI. Convert them to JavaScript first if needed.
π± Get Started
π Project Structure
Typeweaver reads a single spec entrypoint. Organize files however you want, then assemble the
resource map in defineSpec(...). Here is an example layout:
api/spec/
βββ index.ts # Spec entrypoint β exports defineSpec(...)
βββ user/
β βββ index.ts # Barrel exports for the user resource
β βββ userSchema.ts # Zod schemas for the user entity
β βββ GetUserDefinition.ts # defineOperation(...) for GET /users/:userId
β βββ errors/
β βββ UserNotFoundErrorDefinition.ts
βββ shared/
βββ sharedResponses.ts # Array of common error responses
βββ ValidationErrorDefinition.tsThis is just one way to organize your spec. The directory layout is up to you β typeweaver only
cares about the defineSpec(...) entrypoint, not about folder names or file conventions.
- Resource names come from
defineSpec({ resources: ... }), not from directory names. - Shared responses and schemas can live anywhere that your spec entrypoint imports from.
- The CLI bundles the entrypoint, so local spec imports should stay within your project.
π» Sample Spec
// api/spec/user/GetUserDefinition.ts
import {
defineOperation,
defineResponse,
HttpMethod,
HttpStatusCode,
} from "@rexeus/typeweaver-core";
import { z } from "zod";
import { sharedResponses } from "../shared/sharedResponses";
import { userSchema } from "./userSchema";
import { UserNotFoundErrorDefinition } from "./errors/UserNotFoundErrorDefinition";
export const GetUserDefinition = defineOperation({
operationId: "getUser",
method: HttpMethod.GET,
path: "/users/:userId",
summary: "Get a user by id",
request: {
param: z.object({
userId: z.uuid(),
}),
},
responses: [
defineResponse({
name: "GetUserSuccess",
statusCode: HttpStatusCode.OK,
description: "User successfully retrieved",
header: z.object({
"Content-Type": z.literal("application/json"),
}),
body: userSchema,
}),
UserNotFoundErrorDefinition,
...sharedResponses,
],
});// api/spec/index.ts
import { defineSpec } from "@rexeus/typeweaver-core";
import { GetUserDefinition } from "./user/GetUserDefinition";
export const spec = defineSpec({
resources: {
user: {
operations: [GetUserDefinition],
},
},
});// api/spec/user/userSchema.ts
import { z } from "zod";
export const userStatusSchema = z.enum(["ACTIVE", "INACTIVE", "SUSPENDED"]);
export const userSchema = z.object({
id: z.uuid(),
name: z.string(),
email: z.email(),
status: userStatusSchema,
createdAt: z.iso.date(),
updatedAt: z.iso.date(),
});// api/spec/shared/sharedResponses.ts
import { ForbiddenErrorDefinition } from "./ForbiddenErrorDefinition";
import { InternalServerErrorDefinition } from "./InternalServerErrorDefinition";
import { TooManyRequestsErrorDefinition } from "./TooManyRequestsErrorDefinition";
import { UnauthorizedErrorDefinition } from "./UnauthorizedErrorDefinition";
import { UnsupportedMediaTypeErrorDefinition } from "./UnsupportedMediaTypeErrorDefinition";
import { ValidationErrorDefinition } from "./ValidationErrorDefinition";
export const sharedResponses = [
ForbiddenErrorDefinition,
InternalServerErrorDefinition,
TooManyRequestsErrorDefinition,
UnauthorizedErrorDefinition,
UnsupportedMediaTypeErrorDefinition,
ValidationErrorDefinition,
];π§ Generate using plugins
# Generate with plugins:
# - Hono: to easily provide a web server
# - Clients: to get fitting API clients
npx typeweaver generate --input ./api/spec/index.ts --output ./api/generated --plugins clients,honoThe CLI accepts a default export, a named
specexport, or the module namespace itself as theSpecDefinitionentrypoint.
π Create Hono web server
// api/user-handlers.ts
import type { Context } from "hono";
import type { HonoUserApiHandler, IGetUserRequest, GetUserResponse } from "./generated";
import { createGetUserSuccessResponse } from "./generated";
// Implement HonoUserApiHandler β the generated interface enforces
// that every operation in the "user" resource has a handler.
export class UserHandlers implements HonoUserApiHandler {
public constructor() {}
public async handleGetUserRequest(
request: IGetUserRequest,
context: Context
): Promise<GetUserResponse> {
// Simulate fetching user data
const fetchedUser = {
id: request.param.userId,
name: "John Doe",
email: "[email protected]",
status: "ACTIVE",
createdAt: new Date("2023-01-01").toISOString(),
updatedAt: new Date("2023-01-01").toISOString(),
};
return createGetUserSuccessResponse({
header: {
"Content-Type": "application/json",
},
body: fetchedUser,
});
}
// Implement further handlers for each operation in the resource.
// TypeScript enforces the contract β every handler declared in
// HonoUserApiHandler must be implemented before the code compiles.
}// api/server.ts
import { serve } from "@hono/node-server";
import { Hono } from "hono";
// an index file exporting all generated components is automatically provided
import { UserHandlers } from "./user-handlers";
import { PostHandlers } from "./post-handlers"; // Implement similarly to UserHandlers
import { UserHono, PostHono } from "./generated";
const app = new Hono();
const userHandlers = new UserHandlers();
const postHandlers = new PostHandlers();
// you have further config options, e.g. custom error response handling
// (useful for mapping validation errors to your specific response format)
const userRouter = new UserHono({
requestHandlers: userHandlers,
});
const postRouter = new PostHono({
requestHandlers: postHandlers,
});
app.route("/", userRouter);
app.route("/", postRouter);
// Start server on port 3000
serve(
{
fetch: app.fetch,
port: 3000,
},
() => {
console.log("Server is running on http://localhost:3000");
}
);# Start your server locally
tsx api/server.tsπ Communicate by using Clients
// api/client-test.ts
import { UserClient, GetUserRequestCommand } from "./generated";
const client = new UserClient({ baseUrl: "http://localhost:3000" });
const getUserRequestCommand = new GetUserRequestCommand({
param: { userId: "123" },
});
const response = await client.send(getUserRequestCommand);
if (response.type === "GetUserSuccess") {
console.log("Successfully fetched user:", response.body);
} else if (response.type === "UserNotFoundError") {
console.error("User not found:", response.body);
} else {
console.error("Other error occurred:", response.type);
}# Call your created Hono server
tsx api/client-test.tsπ License
Apache 2.0 Β© Dennis Wentzien 2026
