What is Registration?
Registration is the process of telling Contract about your service. Think of it like introducing your service to Contract: βHereβs my interface (what I can do) and my implementation (how I do it). Please make me available to clients.β When you register a service, Contract does several things behind the scenes:- Inspects your interface - Uses Goβs reflection to discover all methods in your interface
- Extracts type information - Finds all input and output types for each method
- Generates schemas - Creates JSON schemas from your Go types (used for validation and documentation)
- Creates invokers - Builds efficient callable wrappers for your methods
- Prepares HTTP bindings - Determines HTTP methods and paths for each operation
*RegisteredService that can be mounted on any transport (REST, JSON-RPC, MCP, etc.).
Basic Registration
The simplest registration takes your implementation and interface:[todo.API] is crucial - it tells Contract which interface defines your API. Your implementation must satisfy this interface, which Goβs compiler verifies at compile time.
Why the Generic Parameter?
You might wonder why we explicitly specify the interface instead of just passing the implementation. There are good reasons:Registration Options
Options let you customize how your service is registered. Pass them as additional arguments after the implementation:WithName
Sets the service name. This name appears in documentation and is used for method namespacing:todo.API).
Where the name appears:
- OpenAPI specification: The
info.titlefield - JSON-RPC: Method prefix (e.g.,
Todo.Createortodos.createdepending on resource) - MCP: Tool group name shown to AI assistants
WithDescription
Adds a human-readable description to your service. This helps users understand what your API does:- OpenAPI specification: The
info.descriptionfield - MCP: Server description shown to AI assistants before they use your tools
- Generated documentation: Any auto-generated API docs
WithDefaultResource
Groups all methods under a resource name. This is one of the most important options for REST APIs:WithDefaultResource("todos"), methods get proper RESTful paths:
/todos represents the collection of all todos, /todos/{id} represents a single todo.
WithResource
Groups specific methods under a resource name. Use this when one interface manages multiple resources:WithMethodHTTP
Override the HTTP binding for a specific method. Use this when automatic inference doesnβt match your needs:-
API versioning: Add version prefix to paths
-
Custom actions: Operations that donβt fit CRUD
-
Nested resources: Related sub-resources
WithHTTP
Set HTTP bindings for multiple methods at once. Useful when you need to customize many methods:WithMethodHTTP for each method, but more concise when customizing multiple bindings.
WithDefaults
Set global defaults for the service that appear in generated specifications:- OpenAPI specification:
serversarray includes the BaseURL - Client generators: Generated clients use these as defaults
- Documentation: Shows users the production URL
WithStreaming
Mark a method as supporting streaming responses:| Mode | Description | Use Case |
|---|---|---|
StreamSSE | Server-Sent Events | Real-time updates over HTTP (chat, live data) |
StreamWS | WebSocket | Bidirectional communication |
StreamGRPC | gRPC streaming | High-performance service-to-service |
StreamAsync | Async messaging | Message queue integration |
The Registered Service
After registration, you get a*RegisteredService. This object provides several useful methods:
Descriptor
Get the service descriptor containing all metadata about your service:- Generating custom documentation
- Building admin dashboards that show available endpoints
- Debugging registration issues
Call
Invoke a method programmatically without going through HTTP:ctx- Context for the call (timeouts, cancellation)resource- Resource name as string (e.g., βtodosβ)method- Method name in lowercase (e.g., βcreateβ, βlistβ)input- Input value, ornilfor methods without input
- Testing without HTTP
- Internal service-to-service calls
- Building CLI tools that use the same service logic
NewInput
Create a new instance of a methodβs input type. This is useful for transports and testing:NewInput to get the correct type, then unmarshal the request body into it.
How Method Names are Transformed
Contract transforms your Go method names when exposing them as API methods. The transformation is simple:| Go Interface Method | API Method Name |
|---|---|
Create | create |
CreateTodo | createTodo |
GetByID | getByID |
ListAll | listAll |
How HTTP Bindings are Inferred
Contract automatically determines the HTTP method and path based on your Go method name. This inference follows REST conventions.Method Name to HTTP Verb
Contract looks at how your method name starts to determine the HTTP verb:| Method Name Starts With | HTTP Method | Why |
|---|---|---|
Create, Add, New | POST | Creating a new resource |
List, All, Search, Find*s (plural) | GET | Retrieving multiple resources |
Get, Find, Fetch, Read | GET | Retrieving a single resource |
Update, Edit, Modify, Set | PUT | Replacing a resource |
Delete, Remove | DELETE | Removing a resource |
Patch | PATCH | Partially updating a resource |
| Everything else | POST | Custom action (default to POST) |
Path Generation
The path is determined by the method pattern and resource name:| Method Pattern | Generated Path | Example |
|---|---|---|
| Create-like | /{resource} | POST /todos |
| List-like | /{resource} | GET /todos |
| Get-like | /{resource}/{id} | GET /todos/ |
| Update-like | /{resource}/{id} | PUT /todos/ |
| Delete-like | /{resource}/{id} | DELETE /todos/ |
| Other | /{resource}/{methodName} | POST /todos/archive |
Complete Example
Path Parameter Extraction
For methods with{id} in the path (Get, Update, Delete), Contract needs to know which field in your input struct contains the ID.
Default Behavior
Contract looks for these fields in your input struct, in order:- Field with
path:"id"tag - Explicit path parameter binding - Field named
ID- Standard Go naming - Field ending in
ID- LikeTodoID,UserID
Using the path Tag
Thepath tag explicitly marks a field as a path parameter:
GET /todos/abc123, Contract:
- Extracts
abc123from the URL path - Creates a new
GetInputinstance - Sets the
IDfield to"abc123" - Passes this to your method
Multiple Path Parameters
For nested resources, you can have multiple path parameters:Complete Registration Example
Hereβs a full example demonstrating various registration options with package-based organization:Registration Errors
Registration can fail if your interface doesnβt follow Contractβs rules. Here are common issues:Missing Context Parameter
Every method must havecontext.Context as the first parameter:
Invalid Return Type
Methods can only return(*Output, error) or just error:
Implementation Doesnβt Match Interface
The implementation must implement all methods in the interface with exact signatures:Best Practices
Use Descriptive Metadata
Good names and descriptions help users (and AI assistants) understand your API:Organize with Separate Packages
Prefer separate packages over one giant interface:Create a Register Function
Keep registration logic close to the implementation:Common Questions
Can I register multiple services?
Yes! Register each service separately and mount them all:Can I modify registration after itβs created?
No, registration is immutable. If you need different options, create a new registration:How do I access the original implementation?
The registered service wraps your implementation. Keep a reference if you need direct access:Whatβs Next?
Now that you understand registration:- Type System - How Go types are converted to JSON schemas
- Transports - Mount your service on REST, JSON-RPC, or MCP
- Error Handling - Handle errors consistently across protocols