Documentation Index
Fetch the complete documentation index at: https://docs.go-mizu.dev/llms.txt
Use this file to discover all available pages before exploring further.
The TypeScript SDK generator creates modern TypeScript client libraries from your contract definitions. The generated code works across Node.js, Bun, and Deno without modifications, uses native fetch, and provides full type inference with a resource-based developer experience.
Key Features
- Runtime Agnostic: Works on Node.js 18+, Bun, and Deno
- Zero Dependencies: Uses native
fetch API only
- TypeScript-First: Full type inference and autocompletion
- ES Modules: Modern JavaScript with
async/await
- SSE Streaming: First-class
AsyncIterable streaming
- npm-Ready: Includes
package.json and tsconfig.json
Quick Start
Step 1: Define Your Contract
# api.yaml
name: TodoAPI
description: Todo list API
defaults:
base_url: http://localhost:8080
resources:
- name: todos
description: Manage todo items
methods:
- name: create
input: CreateInput
output: Todo
http:
method: POST
path: /todos
- name: list
output: ListOutput
http:
method: GET
path: /todos
- name: get
input: GetInput
output: Todo
http:
method: GET
path: /todos/{id}
- name: delete
input: DeleteInput
http:
method: DELETE
path: /todos/{id}
types:
- name: Todo
kind: struct
fields:
- name: id
type: string
- name: title
type: string
- name: done
type: bool
- name: CreateInput
kind: struct
fields:
- name: title
type: string
- name: GetInput
kind: struct
fields:
- name: id
type: string
- name: DeleteInput
kind: struct
fields:
- name: id
type: string
- name: ListOutput
kind: struct
fields:
- name: items
type: "[]Todo"
- name: total
type: int
Step 2: Generate the SDK
mizu contract gen api.yaml --client --lang typescript --output ./sdk/typescript --package todoclient --version 1.0.0
Step 3: Install and Use
# Install dependencies and build
cd sdk/typescript
npm install
npm run build
import { Client } from './dist/index.js';
// Create a client
const client = new Client({ baseURL: 'http://localhost:8080' });
// Create a todo
const todo = await client.todos.create({ title: 'Learn Mizu TypeScript SDK' });
console.log(`Created: ${todo.id}`);
// List all todos
const result = await client.todos.list();
console.log(`Total: ${result.total} todos`);
// Get a specific todo
const fetched = await client.todos.get({ id: todo.id });
console.log(`Got: ${fetched.id} - ${fetched.title}`);
// Delete
await client.todos.delete({ id: todo.id });
console.log('Deleted successfully');
Generated Code Structure
sdk/typescript/
├── package.json
├── tsconfig.json
└── src/
├── index.ts
├── _client.ts
├── _types.ts
├── _resources.ts
└── _streaming.ts
package.json
{
"name": "todoclient",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"files": ["dist"],
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"typescript": "^5.0.0"
}
}
tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2020", "DOM"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}
Client Configuration
Creating a Client
import { Client } from 'todoclient';
// Basic client
const client = new Client({ baseURL: 'http://localhost:8080' });
// With authentication
const client = new Client({
apiKey: 'your-api-key',
baseURL: 'http://localhost:8080',
});
// With all options
const client = new Client({
apiKey: 'your-api-key',
baseURL: 'http://localhost:8080',
timeout: 30000, // 30 seconds
maxRetries: 3,
defaultHeaders: {
'X-Custom-Header': 'value',
},
});
Configuration Options
| Option | Type | Default | Description |
|---|
apiKey | string? | undefined | Authentication token |
baseURL | string? | Service default | API base URL |
timeout | number? | 60000 | Request timeout in milliseconds |
maxRetries | number? | 2 | Maximum retry attempts |
defaultHeaders | Record<string, string>? | {} | Headers for all requests |
Accessing Config
const client = new Client({ baseURL: 'http://localhost:8080' });
// Access current configuration
console.log(client.config.baseURL); // "http://localhost:8080"
console.log(client.config.timeout); // 60000
Type System
Type Mapping Reference
| Contract Type | TypeScript Type |
|---|
string | string |
bool | boolean |
int, int8-int64 | number |
uint, uint8-uint64 | number |
float32, float64 | number |
time.Time | string (ISO 8601) |
json.RawMessage | unknown |
any | unknown |
[]T | T[] |
map[string]T | Record<string, T> |
Interface Types
Contract struct types generate TypeScript interfaces:
export interface Todo {
id: string;
title: string;
done: boolean;
createdAt: string; // ISO 8601 date string
}
Optional and Nullable Fields
| Contract Definition | TypeScript Type |
|---|
| Required field | T |
optional: true | T? (optional property) |
nullable: true | T | null |
| Both optional and nullable | (T | null)? |
Example:
export interface UpdateInput {
title: string; // required
done?: boolean; // optional
description?: string | null; // optional and nullable
}
Enum Fields
Enum fields generate as union types:
export interface Task {
/** One of: "pending", "active", "completed" */
status: "pending" | "active" | "completed";
}
Const Fields
Const fields generate as literal types:
export interface TextContent {
type: "text"; // always "text"
content: string;
}
Array and Record Types
// Contract: kind: slice, elem: Todo
export type TodoList = Todo[];
// Contract: kind: map, elem: string
export type Metadata = Record<string, string>;
Union Types (Discriminated)
Union types generate as TypeScript union types:
export type ContentPart = ContentPartText | ContentPartImage;
// Type guards
export function isContentPartText(v: ContentPart): v is ContentPartText {
return v.type === "text";
}
export function isContentPartImage(v: ContentPart): v is ContentPartImage {
return v.type === "image";
}
Usage:
function handleContent(part: ContentPart) {
if (isContentPartText(part)) {
console.log("Text:", part.content);
} else if (isContentPartImage(part)) {
console.log("Image:", part.url);
}
}
// Or with type narrowing
function handleContent(part: ContentPart) {
switch (part.type) {
case "text":
console.log("Text:", part.content);
break;
case "image":
console.log("Image:", part.url);
break;
}
}
Resources and Methods
Resource Pattern
Each contract resource becomes a property on the client:
client.todos // TodosResource
client.users // UsersResource
Method Signatures
Methods use camelCase naming:
| Contract Method | TypeScript Signature |
|---|
Create | create(input: CreateInput): Promise<Todo> |
List | list(): Promise<ListOutput> |
Get | get(input: { id: string }): Promise<Todo> |
Delete | delete(input: { id: string }): Promise<void> |
Calling Methods
// Create with input object
const todo = await client.todos.create({ title: 'My task' });
// List (no input)
const result = await client.todos.list();
// Get with ID
const fetched = await client.todos.get({ id: 'todo-123' });
// Delete
await client.todos.delete({ id: 'todo-123' });
Streaming (SSE)
For methods with streaming support, the SDK provides AsyncIterable streams:
Basic Streaming
const stream = client.responses.stream({
model: 'gpt-4',
input: 'Hello!',
});
for await (const event of stream) {
if (event.type === 'text_delta') {
process.stdout.write(event.text);
} else if (event.type === 'completed') {
console.log('\n--- Done ---');
}
}
Stream with Cancellation
const stream = client.responses.stream({
model: 'gpt-4',
input: 'Write a long story',
});
// Cancel after 5 seconds
setTimeout(() => {
stream.controller.abort();
}, 5000);
try {
for await (const event of stream) {
console.log(event);
}
} catch (error) {
if (error.name === 'AbortError') {
console.log('Stream cancelled');
} else {
throw error;
}
}
Collecting Stream Events
// Collect all events into an array
const events: ResponseEvent[] = [];
for await (const event of client.responses.stream(input)) {
events.push(event);
}
// Or with Array.fromAsync (Node.js 22+)
const events = await Array.fromAsync(client.responses.stream(input));
Stream Type
The stream object implements AsyncIterable and has an AbortController:
interface Stream<T> extends AsyncIterable<T> {
readonly controller: AbortController;
}
Error Handling
Error Types
The SDK defines three error classes:
export class SDKError extends Error {
name = "SDKError";
}
export class APIConnectionError extends SDKError {
name = "APIConnectionError";
}
export class APIStatusError extends SDKError {
name = "APIStatusError";
readonly status: number;
readonly body: unknown;
}
Handling Errors
import { Client, APIStatusError, APIConnectionError } from 'todoclient';
const client = new Client({ baseURL: 'http://localhost:8080' });
try {
const todo = await client.todos.get({ id: 'nonexistent' });
} catch (error) {
if (error instanceof APIStatusError) {
switch (error.status) {
case 404:
console.log('Todo not found');
break;
case 401:
console.log('Unauthorized');
break;
case 400:
console.log(`Bad request: ${error.message}`);
break;
default:
console.log(`API error ${error.status}: ${error.message}`);
}
} else if (error instanceof APIConnectionError) {
console.log(`Connection failed: ${error.message}`);
} else {
throw error;
}
}
Automatic Retries
The client automatically retries failed requests:
const client = new Client({
baseURL: 'http://localhost:8080',
maxRetries: 5, // Retry up to 5 times
});
Retries are attempted for:
- Network errors
- Timeout errors
Note: 4xx and 5xx errors are NOT retried (they throw immediately).
Advanced Usage
const client = new Client({
baseURL: 'http://localhost:8080',
defaultHeaders: {
'X-Request-ID': 'abc-123',
'X-API-Version': '2024-01',
},
});
Authentication Modes
The SDK supports different authentication modes:
// Bearer token (default)
const client = new Client({ apiKey: 'your-token' });
// Sends: Authorization: Bearer your-token
// Basic auth (if service configured with auth: basic)
const client = new Client({ apiKey: 'base64-credentials' });
// Sends: Authorization: Basic base64-credentials
// No auth (if service configured with auth: none)
const client = new Client({});
// Sends no Authorization header
Timeout Configuration
// Set timeout in milliseconds
const client = new Client({
baseURL: 'http://localhost:8080',
timeout: 30000, // 30 seconds
});
Using with Different Runtimes
The SDK works across all modern JavaScript runtimes:
// Requires Node.js 18+ for native fetch
import { Client } from 'todoclient';
const client = new Client({ baseURL: 'http://localhost:8080' });
const todos = await client.todos.list();
Complete Example
// main.go
package main
import (
"github.com/go-mizu/mizu"
contract "github.com/go-mizu/mizu/contract/v2"
"github.com/go-mizu/mizu/contract/v2/transport/rest"
"yourapp/todo"
)
func main() {
impl := todo.NewService()
svc := contract.Register[todo.API](impl,
contract.WithDefaultResource("todos"),
contract.WithDefaults(&contract.Defaults{
BaseURL: "http://localhost:8080",
}),
)
app := mizu.New()
rest.Mount(app.Router, svc)
app.Listen(":8080")
}
Client Usage
// example.ts
import { Client } from 'todoclient';
async function main() {
const client = new Client({ baseURL: 'http://localhost:8080' });
// Create a todo
const todo = await client.todos.create({ title: 'Buy groceries' });
console.log(`Created: ${todo.id}`);
// List all todos
const result = await client.todos.list();
console.log(`\nAll todos (${result.total} total):`);
for (const t of result.items) {
const status = t.done ? 'x' : ' ';
console.log(` [${status}] ${t.id}: ${t.title}`);
}
// Get a specific todo
const fetched = await client.todos.get({ id: todo.id });
console.log(`\nFetched: ${fetched.title}`);
// Delete
await client.todos.delete({ id: todo.id });
console.log(`\nDeleted: ${todo.id}`);
}
main().catch(console.error);
Streaming Example
// streaming-example.ts
import { Client } from 'chatclient';
async function main() {
const client = new Client({
apiKey: process.env.API_KEY,
baseURL: 'http://localhost:8080',
});
console.log('Streaming response:\n');
const stream = client.chat.stream({
model: 'gpt-4',
messages: [
{ role: 'user', content: 'Write a haiku about programming' },
],
});
for await (const event of stream) {
switch (event.type) {
case 'content_delta':
process.stdout.write(event.delta);
break;
case 'done':
console.log('\n\n--- Stream complete ---');
break;
}
}
}
main().catch(console.error);
Installation and Distribution
Installing Locally
cd sdk/typescript
npm install
npm run build
# Link for local development
npm link
# In another project
npm link todoclient
Publishing to npm
cd sdk/typescript
# Build
npm run build
# Publish
npm publish
# Or with a specific tag
npm publish --tag beta
Installing from Git
npm install github:yourorg/yourrepo#main/sdk/typescript
Using Without npm
The generated code can be used directly in Deno or Bun without npm:
// Deno - import from URL
import { Client } from 'https://raw.githubusercontent.com/yourorg/yourrepo/main/sdk/typescript/src/index.ts';
// Or copy the src/ folder directly
import { Client } from './sdk/index.ts';
See Also