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 Go SDK generator creates native Go client libraries from your contract definitions. The generated code uses only the standard library, requires no external dependencies, and provides a resource-based API with full type safety.
Key Features
- Zero Dependencies: Only uses Go’s standard library (
net/http, encoding/json)
- Single File Output: One self-contained Go file
- Resource-Based API: Intuitive access (
client.Todos.Create())
- Full Type Safety: Complete Go types with proper JSON tags
- SSE Streaming: Built-in
EventStream[T] for server-sent events
- Union Types: Proper marshal/unmarshal for discriminated unions
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 go --output ./sdk/todoclient --package todoclient
Step 3: Use the Generated Client
package main
import (
"context"
"fmt"
"log"
"yourapp/sdk/todoclient"
)
func main() {
// Create a client
client := todoclient.NewClient("http://localhost:8080",
todoclient.WithAuth("your-api-key"),
)
ctx := context.Background()
// Create a todo
todo, err := client.Todos.Create(ctx, &todoclient.CreateInput{
Title: "Learn Mizu Go SDK",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created: %s\n", todo.ID)
// List all todos
result, err := client.Todos.List(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Total: %d todos\n", result.Total)
// Get a specific todo
todo, err = client.Todos.Get(ctx, &todoclient.GetInput{ID: todo.ID})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Got: %s - %s\n", todo.ID, todo.Title)
}
Generated Code Structure
The generated file contains everything in a single package:
// Code generated by mizu contract gen. DO NOT EDIT.
package todoclient
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
// === Types ===
type Todo struct { ... }
type CreateInput struct { ... }
type ListOutput struct { ... }
// === Client ===
type Client struct { ... }
func NewClient(baseURL string, opts ...Option) *Client { ... }
// === Options ===
type Option func(*Client)
func WithHTTPClient(h *http.Client) Option { ... }
func WithAuth(token string) Option { ... }
func WithHeaders(headers map[string]string) Option { ... }
// === Resources ===
type TodosResource struct { ... }
func (r *TodosResource) Create(ctx context.Context, in *CreateInput) (*Todo, error) { ... }
func (r *TodosResource) List(ctx context.Context) (*ListOutput, error) { ... }
func (r *TodosResource) Get(ctx context.Context, in *GetInput) (*Todo, error) { ... }
func (r *TodosResource) Delete(ctx context.Context, in *DeleteInput) error { ... }
// === Streaming (if applicable) ===
type EventStream[T any] struct { ... }
func (s *EventStream[T]) Next() bool { ... }
func (s *EventStream[T]) Event() T { ... }
func (s *EventStream[T]) Err() error { ... }
func (s *EventStream[T]) Close() error { ... }
// === Internal Helpers ===
func (c *Client) do(ctx context.Context, method, path string, in, out any) error { ... }
Client Configuration
Creating a Client
// Basic client with just base URL
client := todoclient.NewClient("http://localhost:8080")
// With options
client := todoclient.NewClient("http://localhost:8080",
todoclient.WithAuth("your-api-key"),
todoclient.WithHTTPClient(&http.Client{
Timeout: 30 * time.Second,
}),
todoclient.WithHeaders(map[string]string{
"X-Custom-Header": "value",
}),
)
Available Options
| Option | Description |
|---|
WithAuth(token string) | Set Bearer authentication token |
WithHTTPClient(client *http.Client) | Use custom HTTP client |
WithHeaders(headers map[string]string) | Add default headers to all requests |
Type System
Type Mapping Reference
| Contract Type | Go Type |
|---|
string | string |
bool | bool |
int, int32 | int32 |
int64 | int64 |
int8, int16 | int8, int16 |
uint, uint32 | uint32 |
uint64 | uint64 |
float32 | float32 |
float64 | float64 |
time.Time | time.Time |
json.RawMessage | json.RawMessage |
any | any |
[]T | []T |
map[string]T | map[string]T |
Struct Types
Contract struct types generate Go structs with JSON tags:
// Generated from contract type
type Todo struct {
ID string `json:"id"`
Title string `json:"title"`
Done bool `json:"done"`
CreatedAt time.Time `json:"created_at"`
}
Optional and Nullable Fields
Optional and nullable fields become pointers with omitempty:
| Contract Definition | Go Type | JSON Tag |
|---|
| Required field | T | json:"name" |
optional: true | *T | json:"name,omitempty" |
nullable: true | *T | json:"name" |
| Both optional and nullable | *T | json:"name,omitempty" |
Example:
// Contract definition
type UpdateInput struct {
Title string `json:"title"` // required
Done *bool `json:"done,omitempty"` // optional
}
// Usage
client.Todos.Update(ctx, &UpdateInput{
Title: "Updated title",
Done: ptr(true), // helper function: func ptr[T any](v T) *T { return &v }
})
Enum Fields
Enum fields generate as the base type with a comment:
// Contract: enum: [pending, active, completed]
// Generated:
// Status is one of: pending, active, completed
Status string `json:"status"`
Slice and Map Types
// Contract: kind: slice, elem: Todo
type TodoList []Todo
// Contract: kind: map, elem: string
type Metadata map[string]string
Union Types (Discriminated)
Union types generate a struct with pointer fields for each variant, plus marshal/unmarshal methods:
// ContentPart is a discriminated union (tag: "type").
// Variants: ContentPartText, ContentPartImage
type ContentPart struct {
Text *ContentPartText `json:"-"`
Image *ContentPartImage `json:"-"`
}
func (u *ContentPart) MarshalJSON() ([]byte, error) {
if u.Text != nil {
return json.Marshal(u.Text)
}
if u.Image != nil {
return json.Marshal(u.Image)
}
return []byte("null"), nil
}
func (u *ContentPart) UnmarshalJSON(data []byte) error {
var disc struct{ Type string `json:"type"` }
if err := json.Unmarshal(data, &disc); err != nil {
return err
}
switch disc.Type {
case "text":
u.Text = new(ContentPartText)
return json.Unmarshal(data, u.Text)
case "image":
u.Image = new(ContentPartImage)
return json.Unmarshal(data, u.Image)
}
return fmt.Errorf("unknown ContentPart type: %q", disc.Type)
}
Usage:
// Creating a union value
part := &ContentPart{
Text: &ContentPartText{
Type: "text",
Content: "Hello",
},
}
// Checking which variant
if part.Text != nil {
fmt.Println("Text:", part.Text.Content)
} else if part.Image != nil {
fmt.Println("Image:", part.Image.URL)
}
Resources and Methods
Resource Pattern
Each contract resource becomes a struct attached to the client:
type Client struct {
Todos *TodosResource
Users *UsersResource
// ...
}
Access resources through the client:
client.Todos.Create(ctx, input)
client.Users.Get(ctx, &GetInput{ID: "user-123"})
Method Signatures
Generated method signatures follow these patterns:
| Contract Method | Generated Signature |
|---|
| No input, no output | Method(ctx) error |
| No input, with output | Method(ctx) (*Output, error) |
| With input, no output | Method(ctx, *Input) error |
| With input, with output | Method(ctx, *Input) (*Output, error) |
All methods take context.Context as the first parameter for cancellation and timeout support.
Streaming (SSE)
For methods with streaming support, the generator creates an EventStream[T] type:
Stream Method Signature
func (r *ResponsesResource) Stream(ctx context.Context, in *StreamRequest) *EventStream[ResponseEvent]
EventStream API
type EventStream[T any] struct {
// internal fields
}
// Next advances to the next event. Returns false when done or on error.
func (s *EventStream[T]) Next() bool
// Event returns the current event (call after Next returns true).
func (s *EventStream[T]) Event() T
// Err returns the error if Next returned false due to an error.
func (s *EventStream[T]) Err() error
// Close releases the underlying connection.
func (s *EventStream[T]) Close() error
Streaming Usage
stream := client.Responses.Stream(ctx, &StreamRequest{
Model: "gpt-4",
Input: "Hello!",
})
defer stream.Close()
for stream.Next() {
event := stream.Event()
// Handle different event types (for unions)
switch {
case event.TextDelta != nil:
fmt.Print(event.TextDelta.Text)
case event.Completed != nil:
fmt.Println("\n--- Done ---")
}
}
if err := stream.Err(); err != nil {
log.Fatal("Stream error:", err)
}
Cancellation
Use context cancellation to stop a stream early:
ctx, cancel := context.WithCancel(context.Background())
stream := client.Responses.Stream(ctx, input)
defer stream.Close()
go func() {
time.Sleep(5 * time.Second)
cancel() // Stop the stream after 5 seconds
}()
for stream.Next() {
fmt.Print(stream.Event().Text)
}
Error Handling
Error Type
Generated code includes an Error type for API errors:
type Error struct {
StatusCode int `json:"-"`
Code string `json:"code,omitempty"`
Message string `json:"message"`
}
func (e *Error) Error() string {
if e.Code != "" {
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
return e.Message
}
Checking Errors
todo, err := client.Todos.Get(ctx, &GetInput{ID: "nonexistent"})
if err != nil {
var apiErr *todoclient.Error
if errors.As(err, &apiErr) {
switch apiErr.StatusCode {
case 404:
fmt.Println("Todo not found")
case 401:
fmt.Println("Unauthorized")
case 400:
fmt.Printf("Bad request: %s\n", apiErr.Message)
default:
fmt.Printf("API error %d: %s\n", apiErr.StatusCode, apiErr.Message)
}
return
}
// Network or other error
log.Fatal(err)
}
Advanced Usage
Custom HTTP Client
// With timeout
client := todoclient.NewClient(baseURL,
todoclient.WithHTTPClient(&http.Client{
Timeout: 30 * time.Second,
}),
)
// With transport for proxies, TLS config, etc.
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
},
}
client := todoclient.NewClient(baseURL,
todoclient.WithHTTPClient(&http.Client{
Transport: transport,
Timeout: 30 * time.Second,
}),
)
Context Usage
Always pass context for proper cancellation and timeout:
// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
todo, err := client.Todos.Create(ctx, input)
// With values (e.g., request ID for logging)
ctx = context.WithValue(ctx, "requestID", "abc-123")
Concurrent Requests
var wg sync.WaitGroup
results := make([]*Todo, 3)
errors := make([]error, 3)
ids := []string{"1", "2", "3"}
for i, id := range ids {
wg.Add(1)
go func(idx int, todoID string) {
defer wg.Done()
results[idx], errors[idx] = client.Todos.Get(ctx, &GetInput{ID: todoID})
}(i, id)
}
wg.Wait()
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/main.go
package main
import (
"context"
"fmt"
"log"
"yourapp/sdk/todoclient"
)
func main() {
client := todoclient.NewClient("http://localhost:8080")
ctx := context.Background()
// Create
todo, err := client.Todos.Create(ctx, &todoclient.CreateInput{
Title: "Buy groceries",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created: %s\n", todo.ID)
// List
result, err := client.Todos.List(ctx)
if err != nil {
log.Fatal(err)
}
for _, t := range result.Items {
status := " "
if t.Done {
status = "x"
}
fmt.Printf("[%s] %s: %s\n", status, t.ID, t.Title)
}
// Delete
if err := client.Todos.Delete(ctx, &todoclient.DeleteInput{ID: todo.ID}); err != nil {
log.Fatal(err)
}
fmt.Println("Deleted successfully")
}
See Also