Quickstart
Get a type-safe HTTP API running in 5 minutes.
Installation
go get github.com/zoobz-io/rocco
Your First API
Create a file main.go:
package main
import (
"fmt"
"github.com/zoobz-io/rocco"
)
// 1. Define your input type (validate tags generate OpenAPI constraints)
type CreateUserInput struct {
Name string `json:"name" validate:"required,min=2,max=100"`
Email string `json:"email" validate:"required,email"`
}
// 2. Define your output type
type UserOutput struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
// 3. Create an engine
engine := rocco.NewEngine()
// 4. Create a typed handler
createUser := rocco.POST[CreateUserInput, UserOutput]("/users",
func(req *rocco.Request[CreateUserInput]) (UserOutput, error) {
// req.Body is your parsed CreateUserInput
return UserOutput{
ID: "usr_" + req.Body.Name[:3],
Name: req.Body.Name,
Email: req.Body.Email,
}, nil
},
).WithSuccessStatus(201) // Return 201 Created
// 5. Register handler
engine.WithHandlers(createUser)
// 6. Start server
fmt.Println("Server running at http://localhost:8080")
engine.Start(rocco.HostAll, 8080)
}
Run it:
go run main.go
Test Your API
Create a user:
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{"name": "John Doe", "email": "john@example.com"}'
Response:
{
"id": "usr_Joh",
"name": "John Doe",
"email": "john@example.com"
}
Try invalid JSON:
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{invalid}'
Response (422 Unprocessable Entity):
{
"code": "UNPROCESSABLE_ENTITY",
"message": "invalid request body"
}
Note: Runtime validation is opt-in. To validate inputs, implement the
Validatableinterface on your types. See Concepts: Validation for details.
View OpenAPI Documentation
Rocco automatically generates OpenAPI documentation. Visit:
http://localhost:8080/openapi- OpenAPI JSON spechttp://localhost:8080/docs- Interactive Scalar documentation
Add More Handlers
Expand your API with GET, PUT, DELETE handlers:
// GET /users/{id}
getUser := rocco.GET[rocco.NoBody, UserOutput]("/users/{id}",
func(req *rocco.Request[rocco.NoBody]) (UserOutput, error) {
userID := req.Params.Path["id"]
// Fetch user from database...
return UserOutput{ID: userID, Name: "John", Email: "john@example.com"}, nil
},
).WithPathParams("id")
// GET /users (with query params)
type UserListOutput struct {
Users []UserOutput `json:"users"`
Total int `json:"total"`
}
listUsers := rocco.GET[rocco.NoBody, UserListOutput]("/users",
func(req *rocco.Request[rocco.NoBody]) (UserListOutput, error) {
page := req.Params.Query["page"]
limit := req.Params.Query["limit"]
// Query database...
return UserListOutput{Users: []UserOutput{}, Total: 0}, nil
},
).WithQueryParams("page", "limit")
// Register all handlers
engine.WithHandlers(createUser, getUser, listUsers)
Handle Errors
Use sentinel errors for consistent error responses:
getUser := rocco.GET[rocco.NoBody, UserOutput]("/users/{id}",
func(req *rocco.Request[rocco.NoBody]) (UserOutput, error) {
user, err := db.FindUser(req.Params.Path["id"])
if err != nil {
// Return 404 Not Found
return UserOutput{}, rocco.ErrNotFound.WithMessage("user not found")
}
return UserOutput{...}, nil
},
).
WithPathParams("id").
WithErrors(rocco.ErrNotFound) // Declare possible errors
What's Next?
You've built a type-safe API with automatic OpenAPI documentation. Continue learning:
- Core Concepts - Understand handlers, requests, and responses
- Architecture - How rocco processes requests
- Handler Guide - Advanced handler configuration
See Also
- CRUD API Cookbook - Complete CRUD example
- Error Handling Guide - Error patterns
- API Reference - Full API documentation