What Youβll Build
This guide walks you through building your first API with Contract. By the end, youβll have a working todo list API accessible via REST and JSON-RPC. Youβll learn the three-step pattern that every Contract service follows:- Define your interface - The contract that describes your API
- Implement the interface - Your business logic
- Register and serve - Make it available via HTTP
todo package with types named API and Service (the package name provides the βtodoβ context).
Prerequisites
Before starting, make sure you have:- Go 1.22 or later installed (download Go)
- A terminal (Command Prompt, Terminal, or any shell)
- A text editor (VS Code, GoLand, or your favorite)
- curl for testing (usually pre-installed on Mac/Linux)
Step 1: Create Your Project
Create a new directory and initialize a Go module. A Go module is a collection of Go packages with ago.mod file that tracks dependencies:
go.mod file tells Go where to find dependencies and what version of Go youβre using.
Now create the directory structure for your todo package:
Step 2: Install Dependencies
Add the required packages. These commands download the mizu web framework and Contract v2:github.com/go-mizu/mizuis the mizu web framework that handles HTTP routinggithub.com/go-mizu/mizu/contract/v2is Contract v2, which provides the interface-first API pattern
go.mod file will list these as dependencies.
Step 3: Create Your Types
Createtodo/types.go. This file defines the data structures your API works with. These are called βDTOsβ (Data Transfer Objects) because they define what data moves between clients and your server:
CreateInput and GetInput instead of just using Todo everywhere. There are good reasons:
- Input types describe what clients send to you. For
Create, we only need a title - we generate the ID. - Output types describe what you send back. We return the full
Todowith the generated ID. - Flexibility: You can add fields to outputs without requiring clients to send them, and vice versa.
- Validation: Input types only contain the fields you actually accept.
Step 4: Define Your Interface (The Contract)
Createtodo/api.go. This is the heart of Contract - your interface defines what your API can do. Think of it as a βmenuβ that lists all available operations:
- Package naming: The interface is named
API(notTodoAPI) becausetodo.APIreads naturally when imported. - Context first: Every method starts with
ctx context.Context. This is a Go pattern for passing request-scoped data like timeouts and cancellation signals. - Pointer inputs: Input types are pointers (
*CreateInput) so they can benilfor methods that donβt need input. - Error handling: Every method returns
erroras the last return value. This is how you communicate failures to clients. - Method naming: Names like
Create,List,Get,Deleteautomatically map to HTTP verbs when using REST.
Step 5: Implement Your Interface
Createtodo/service.go. This is where your actual code lives - the βkitchenβ that prepares the dishes from your βmenuβ (the interface):
- Package naming: The struct is named
Service(notTodoService) becausetodo.Servicereads naturally. - Constructor function:
NewService()is a common Go pattern for creating initialized instances. - Thread safety: We use
sync.RWMutexbecause HTTP servers handle multiple requests concurrently. - Error handling: Return
nil, errorto indicate failure. Contract translates this to the appropriate protocol response.
Step 6: Wire Everything Together
Createmain.go in your project root. This file imports your todo package and wires everything together:
Complete Project Structure
Your project should now look like this:- Clear separation: Each file has a single responsibility
- Package-based naming:
todo.APIandtodo.Serviceare clearer thanTodoAPIandtodoService - Testability: You can easily mock
todo.APIfor testing - Scalability: Add more services by creating new packages (
user/,order/, etc.)
Step 7: Run Your Server
Before running, ensure all dependencies are properly resolved:Step 8: Test Your API
Letβs test your API using curl. Open a new terminal window (keep the server running in the first one).Create a Todo (REST)
POSTtells the server we want to create something/todosis the resource path-H "Content-Type: application/json"tells the server weβre sending JSON-d '{"title": "Buy groceries"}'is the JSON body (our CreateInput)
todo_1) and set completed to false.
Create Another Todo
List All Todos (REST)
/todos calls our List method.
Expected output:
Get a Specific Todo (REST)
/todos/{id} calls our Get method with id=todo_1.
Expected output:
Delete a Todo (REST)
Step 9: Try JSON-RPC
The same service is also available via JSON-RPC. JSON-RPC uses a different format where you specify the method name in the request body. This is useful for:- Batching: Send multiple requests in one HTTP call
- RPC-style clients: Some languages prefer explicit method names over HTTP verbs
Create via JSON-RPC
- All JSON-RPC requests go to
/rpcas POST "jsonrpc": "2.0"identifies this as JSON-RPC (required)"id": 1helps match requests to responses (you choose the ID)"method": "todos.create"is{resource}.{method}"params"is our CreateInput
List via JSON-RPC
Batch Requests (JSON-RPC Only)
One of JSON-RPCβs killer features is batching. Send multiple requests in one HTTP call:id values.
Understanding What Happened
Letβs recap what Contract did for you behind the scenes:-
Inspected your interface: When you called
contract.Register[todo.API](), Contract used Goβs reflection to discover all methods in thetodo.APIinterface and their input/output types. -
Generated JSON schemas: Your Go structs (
Todo,CreateInput, etc.) were automatically converted to JSON schemas. These schemas are used for documentation and AI tool definitions. -
Created REST endpoints: Based on your method names, Contract created HTTP endpoints:
Createβ POST /todos (HTTP convention: POST creates resources)Listβ GET /todos (HTTP convention: GET retrieves resources)Getβ GET /todos/ (path parameter for specific resource)Deleteβ DELETE /todos/ (HTTP convention: DELETE removes resources)
-
Created JSON-RPC handlers: All methods became available at
/rpc:todos.create,todos.list,todos.get,todos.delete
-
Compile-time safety: If your
Servicedidnβt implement all methods inAPI, Goβs compiler would have caught it before you even ran the program.
Common Questions
Why doesnβt my method appear as an endpoint?
Methods must follow Contractβs rules:- Must be in the interface: Methods only on the struct (not in the interface) wonβt be exposed
- First parameter must be
context.Context: This is required for proper request handling - Input must be pointer to struct: Use
*CreateInput, notCreateInput - Last return must be
error: Every method needs to report success/failure
How do I add more transports?
Import the transport package and mount it:How do I handle errors properly?
The quick start uses simpleerrors.New(). For production, use Contractβs typed errors for proper HTTP status codes:
How do I add a database?
Replace the in-memory map with your database client. The Service struct holds your dependencies:Whatβs Next?
Now that you have a working API, explore these topics:- Defining Services - Learn all the method signature patterns and interface design
- Registration - Understand all registration options
- Error Handling - Proper error handling across protocols
- REST Transport - Deep dive into REST configuration
- JSON-RPC Transport - Batch requests and notifications
- MCP Transport - Connect your API to AI assistants
- Testing - How to test your services