An annotation-driven OpenAPI 3.1 specification generator for Go HTTP services using Chi router. This package automatically generates comprehensive API documentation from your Go code with zero configuration required.
About This Project: This project was initially part of a larger monolith, where we needed immediate documentation. It was using Chi and sqlc with pgx/v5, therefore, this package would have the best support for that tech stack only. If this fits your usecase, feel free to use it. There are a lot of rooms for improvements, but it serves the purpose necessary for us at this point of time. Contributions are welcome, see the CONTRIBUTING.md file. This readme.md is AI generated.
- Zero Configuration: No manual type registration or complex setup required
- Chi Router Native: Specifically designed and optimized for
go-chi/chirouter - Annotation-Driven: Uses standard Swagger-style comments for documentation
- Dynamic Schema Generation: Automatically generates JSON schemas from Go types
- High Performance: Built-in caching and type indexing for optimal performance
- Type Safety: Leverages Go's type system for accurate schema generation
- Deep Type Discovery: Recursively finds and documents all referenced types
- External Type Support: Configurable support for third-party library types
- Runtime Generation: Updates documentation dynamically without restarts
As mentioned, this package was extracted from a larger project and has room for improvements:
- Chi Router Only: Currently only supports
go-chi/chirouter (by design) - Top-Level Functions: Handlers must be top-level functions, not struct methods
- SQLC/pgx Optimized: Best performance with SQLC-generated types and pgx/v5
- AST Parsing Limitations: Complex comment patterns may not be parsed correctly
- Limited Router Support: No plans to support other routers (Gin, Echo, etc.)
- Type Discovery: May struggle with deeply nested or complex generic types
- Documentation: Some edge cases in annotation parsing may need manual workarounds
Despite these limitations, the package serves its core purpose effectively for Chi + SQLC + pgx/v5 projects.
go get github.com/AxelTahmid/openapi-gen// User represents a user in the system
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email *string `json:"email,omitempty"`
Age int `json:"age"`
IsActive bool `json:"is_active"`
CreatedAt time.Time `json:"created_at"`
Tags []string `json:"tags"`
}
// CreateUserRequest represents the request payload for creating a user
type CreateUserRequest struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
// UserListResponse represents a paginated list of users
type UserListResponse struct {
Users []User `json:"users"`
Total int `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
}Important: Handlers must be top-level functions, not struct methods, for annotation parsing to work correctly.
// GetUsers retrieves a paginated list of users
// @Summary Get all users
// @Description Retrieve a paginated list of users with optional filtering
// @Tags users
// @Accept application/json
// @Produce application/json
// @Param page query int false "Page number" default(1)
// @Param limit query int false "Items per page" default(10)
// @Param active query bool false "Filter by active status"
// @Success 200 {object} UserListResponse "List of users"
// @Failure 400 {object} ProblemDetails "Invalid request parameters"
// @Failure 500 {object} ProblemDetails "Internal server error"
func GetUsers(w http.ResponseWriter, r *http.Request) {
// Implementation here
users := UserListResponse{
Users: []User{{ID: 1, Name: "John Doe", Age: 30}},
Total: 1,
Page: 1,
Limit: 10,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
}
// CreateUser creates a new user in the system
// @Summary Create a new user
// @Description Create a new user with the provided details
// @Tags users
// @Accept application/json
// @Produce application/json
// @Param user body CreateUserRequest true "User creation data"
// @Success 201 {object} User "User created successfully"
// @Failure 400 {object} ProblemDetails "Invalid request data"
// @Failure 409 {object} ProblemDetails "User already exists"
// @Failure 500 {object} ProblemDetails "Internal server error"
// @Security BearerAuth
func CreateUser(w http.ResponseWriter, r *http.Request) {
// Implementation here
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
user := User{
ID: 2,
Name: req.Name,
Age: req.Age,
// ... other fields
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user)
}package main
import (
"log"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/AxelTahmid/openapi-gen"
)
func main() {
r := chi.NewRouter()
// Add middleware
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
// Configure OpenAPI
config := openapi.Config{
Title: "User Management API",
Description: "A comprehensive API for managing users",
Version: "1.0.0",
TermsOfService: "https://example.com/terms",
Server: "https://api.example.com",
Contact: &openapi.Contact{
Name: "API Support Team",
Email: "api-support@example.com",
URL: "https://example.com/support",
},
License: &openapi.License{
Name: "Apache 2.0",
URL: "https://www.apache.org/licenses/LICENSE-2.0.html",
},
}
// Add OpenAPI endpoints (typically only in development)
r.Route("/openapi", func(r chi.Router) {
r.Get("/", openapi.CachedHandler(r, config))
r.Get("/generate", openapi.GenerateFileHandler(r, config))
r.Get("/invalidate", openapi.InvalidateCache)
})
// Add your API routes
r.Route("/api/v1", func(r chi.Router) {
r.Get("/users", GetUsers)
r.Post("/users", CreateUser)
r.Get("/users/{id}", GetUserByID)
r.Put("/users/{id}", UpdateUser)
r.Delete("/users/{id}", DeleteUser)
})
log.Println("Server starting on :8080")
log.Println("OpenAPI spec available at: http://localhost:8080/openapi")
http.ListenAndServe(":8080", r)
}// cmd/generate-docs/main.go
package main
import (
"log"
"os"
"github.com/go-chi/chi/v5"
"github.com/AxelTahmid/openapi-gen"
)
func main() {
// Create router and register routes (same as above)
r := chi.NewRouter()
r.Get("/api/v1/users", GetUsers)
r.Post("/api/v1/users", CreateUser)
// ... other routes
config := openapi.Config{
Title: "User Management API",
Version: "1.0.0",
Server: "https://api.example.com",
}
// Generate OpenAPI spec file
err := openapi.GenerateOpenAPISpecFile(r, config, "openapi.json", true)
if err != nil {
log.Fatalf("Failed to generate OpenAPI spec: %v", err)
}
log.Println("OpenAPI specification generated successfully: openapi.json")
}# View the OpenAPI specification
curl http://localhost:8080/openapi | jq .
# Use with Swagger UI
docker run -p 8080:8080 -e SWAGGER_JSON_URL=http://host.docker.internal:8080/openapi swaggerapi/swagger-ui
# Generate static file
curl http://localhost:8080/openapi/generate
# Invalidate cache for fresh generation
curl http://localhost:8080/openapi/invalidateThis package supports various deployment and usage patterns. Choose the approach that best fits your project:
Perfect for local development and staging environments:
func main() {
r := chi.NewRouter()
// Add your API routes
r.Route("/api/v1", func(r chi.Router) {
r.Get("/users", GetUsers)
r.Post("/users", CreateUser)
})
// Add OpenAPI endpoints for development
if os.Getenv("ENV") != "production" {
config := openapi.Config{
Title: "My API",
Version: "1.0.0",
Server: "http://localhost:8080",
}
r.Route("/docs", func(r chi.Router) {
r.Get("/openapi.json", openapi.CachedHandler(r, config))
r.Get("/", swaggerUIHandler) // Your Swagger UI handler
})
}
log.Println("API docs: http://localhost:8080/docs")
http.ListenAndServe(":8080", r)
}Generate static OpenAPI files for production deployment:
// cmd/generate-docs/main.go
package main
import (
"log"
"github.com/AxelTahmid/openapi-gen"
)
func main() {
// Setup your router with all routes
r := setupProductionRoutes()
config := openapi.Config{
Title: "Production API",
Version: "1.0.0",
Server: "https://api.yourdomain.com",
Description: "Production API documentation",
}
// Generate static file
err := openapi.GenerateOpenAPISpecFile(r, config, "docs/openapi.json", true)
if err != nil {
log.Fatalf("Failed to generate spec: %v", err)
}
log.Println("Static OpenAPI spec generated at docs/openapi.json")
}Integrate documentation generation into your build pipeline:
# .github/workflows/docs.yml
name: Generate API Documentation
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
generate-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: '1.24'
- name: Generate OpenAPI spec
run: |
go run cmd/generate-docs/main.go
- name: Validate OpenAPI spec
run: |
npx @apidevtools/swagger-cli validate docs/openapi.json
- name: Deploy to GitHub Pages
if: github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docsUse in your test suites for API contract testing:
func TestAPIDocumentation(t *testing.T) {
// Setup test router
r := setupTestRoutes()
config := openapi.Config{
Title: "Test API",
Version: "1.0.0",
}
// Generate spec
handler := openapi.CachedHandler(r, config)
req := httptest.NewRequest("GET", "/openapi", nil)
w := httptest.NewRecorder()
handler(w, req)
// Validate spec structure
var spec openapi.Spec
err := json.Unmarshal(w.Body.Bytes(), &spec)
require.NoError(t, err)
// Test specific endpoints are documented
assert.Contains(t, spec.Paths, "/api/v1/users")
assert.Equal(t, "Test API", spec.Info.Title)
}
func TestAPIContractCompliance(t *testing.T) {
// Ensure your API responses match the generated schema
// Use tools like go-swagger validator or custom validation
}Generate documentation for multiple services:
// Service A
func setupUserService() *chi.Mux {
r := chi.NewRouter()
r.Route("/api/v1/users", func(r chi.Router) {
r.Get("/", GetUsers)
r.Post("/", CreateUser)
})
return r
}
// Service B
func setupOrderService() *chi.Mux {
r := chi.NewRouter()
r.Route("/api/v1/orders", func(r chi.Router) {
r.Get("/", GetOrders)
r.Post("/", CreateOrder)
})
return r
}
// Generate separate specs for each service
func generateServiceDocs() {
services := map[string]*chi.Mux{
"user-service": setupUserService(),
"order-service": setupOrderService(),
}
for name, router := range services {
config := openapi.Config{
Title: fmt.Sprintf("%s API", strings.Title(name)),
Version: "1.0.0",
Server: fmt.Sprintf("https://%s.api.company.com", name),
}
filename := fmt.Sprintf("docs/%s-openapi.json", name)
err := openapi.GenerateOpenAPISpecFile(router, config, filename, true)
if err != nil {
log.Printf("Failed to generate %s docs: %v", name, err)
}
}
}Works seamlessly with Chi middleware:
func main() {
r := chi.NewRouter()
// Global middleware
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.RequestID)
r.Use(corsMiddleware)
// API routes with specific middleware
r.Route("/api/v1", func(r chi.Router) {
r.Use(authMiddleware) // JWT authentication
r.Use(rateLimitMiddleware) // Rate limiting
r.Use(metricsMiddleware) // Prometheus metrics
r.Get("/users", GetUsers)
r.Post("/users", CreateUser)
})
// Documentation routes (no auth required)
r.Route("/docs", func(r chi.Router) {
r.Use(middleware.BasicAuth("docs", map[string]string{
"admin": "secret", // Basic auth for docs
}))
config := openapi.Config{
Title: "Protected API",
Version: "1.0.0",
}
r.Get("/openapi.json", openapi.CachedHandler(r, config))
})
http.ListenAndServe(":8080", r)
}This package uses Go's AST parsing to extract function comments and annotations. For the parser to correctly identify and process your handler functions, they must be defined as top-level functions rather than struct methods.
type UserHandler struct {
service UserService
}
// This annotation won't be parsed correctly
// @Summary Create user
func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
// Implementation
}// This will be found and parsed correctly
// @Summary Create user
// @Description Create a new user with the provided details
func CreateUser(w http.ResponseWriter, r *http.Request) {
// Implementation
}
// You can still use dependency injection patterns
func CreateUserWithService(service UserService) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Implementation using service
}
}| Annotation | Format | Description | Example |
|---|---|---|---|
@Summary |
@Summary <text> |
Brief endpoint description | @Summary Create a new user |
@Description |
@Description <text> |
Detailed endpoint description | @Description Create a new user with the provided details |
@Tags |
@Tags <tag1>,<tag2> |
Comma-separated list of tags | @Tags users,management |
@Accept |
@Accept <media-type> |
Request content type | @Accept application/json |
@Produce |
@Produce <media-type> |
Response content type | @Produce application/json |
@Param |
@Param <name> <in> <type> <required> "<description>" |
Request parameters | See examples below |
@Success |
@Success <code> {<format>} <type> "<description>" |
Success responses | @Success 200 {object} User "Success" |
@Failure |
@Failure <code> {<format>} <type> "<description>" |
Error responses | @Failure 400 {object} ProblemDetails "Bad Request" |
@Security |
@Security <scheme> |
Security requirements | @Security BearerAuth |
| Store | Example | Description |
|---|---|---|
body |
@Param user body CreateUserRequest true "User data" |
Request body |
path |
@Param id path int true "User ID" |
URL path parameter |
query |
@Param limit query int false "Page limit" |
Query parameter |
header |
@Param Authorization header string true "Auth token" |
Header parameter |
| Format | Example | Description |
|---|---|---|
{object} |
@Success 200 {object} User "Single user" |
Single object |
{array} |
@Success 200 {array} User "List of users" |
Array of objects |
{data} |
@Success 200 {data} string "Raw data" |
Raw data type |
config := openapi.Config{
Title: "E-Commerce API",
Description: "Comprehensive REST API for e-commerce operations",
Version: "2.1.0",
TermsOfService: "https://example.com/terms",
Server: "https://api.example.com",
Contact: &openapi.Contact{
Name: "E-Commerce API Team",
Email: "api-team@example.com",
URL: "https://example.com/support",
},
License: &openapi.License{
Name: "Apache 2.0",
URL: "https://www.apache.org/licenses/LICENSE-2.0.html",
},
}// Add support for custom types from external libraries
openapi.AddExternalKnownType("github.com/shopspring/decimal.Decimal", &openapi.Schema{
Type: "string",
Description: "Decimal number represented as string",
Example: "123.45",
})
openapi.AddExternalKnownType("github.com/google/uuid.UUID", &openapi.Schema{
Type: "string",
Format: "uuid",
Description: "UUID v4",
Example: "550e8400-e29b-41d4-a716-446655440000",
})The package automatically generates JSON schemas for your Go types with the following features:
- Automatic Discovery: Finds types by scanning your project files
- Package-Aware: Supports both local types (
User) and package-qualified types (db.User) - Struct Tag Support: Respects
jsontags andomitemptydirectives - Type Mapping: Maps Go types to appropriate OpenAPI types
- Reference Resolution: Handles circular references and type reuse
- Performance Optimized: Built-in type indexing and caching
- Current Package: For unqualified types like
CreateUserRequest - Project Packages: Recursively searches under project directories
- Package-Qualified: For types like
db.Userormodels.Product - External Types: Configurable mappings for third-party types
Given this Go struct:
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
Price float64 `json:"price"`
InStock bool `json:"in_stock"`
Tags []string `json:"tags"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
CreatedAt time.Time `json:"created_at"`
Category Category `json:"category"`
}The generator produces this OpenAPI schema:
{
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"description": { "type": "string" },
"price": { "type": "number" },
"in_stock": { "type": "boolean" },
"tags": {
"type": "array",
"items": { "type": "string" }
},
"metadata": { "type": "object" },
"created_at": { "type": "string", "format": "date-time" },
"category": { "$ref": "#/components/schemas/Category" }
},
"required": [
"id",
"name",
"price",
"in_stock",
"tags",
"created_at",
"category"
]
}The package automatically detects security requirements and generates appropriate security schemes:
// Protected endpoint
// @Security BearerAuth
func CreateUser(w http.ResponseWriter, r *http.Request) {
// Implementation
}
// Multiple security schemes
// @Security BearerAuth
// @Security ApiKeyAuth
func AdminOnlyEndpoint(w http.ResponseWriter, r *http.Request) {
// Implementation
}r.Route("/api/v1", func(r chi.Router) {
// Public routes
r.Post("/auth/login", LoginUser)
r.Post("/auth/register", RegisterUser)
// Protected routes (will automatically include BearerAuth requirement)
r.Group(func(r chi.Router) {
r.Use(authMiddleware) // JWT middleware
r.Get("/users", ListUsers)
r.Post("/users", CreateUser)
})
})// v1 routes
r.Route("/api/v1", func(r chi.Router) {
r.Get("/users", V1ListUsers)
r.Post("/users", V1CreateUser)
})
// v2 routes
r.Route("/api/v2", func(r chi.Router) {
r.Get("/users", V2ListUsers)
r.Post("/users", V2CreateUser)
})
// Separate OpenAPI specs for each version
r.Get("/api/v1/openapi.json", openapi.CachedHandler(r, v1Config))
r.Get("/api/v2/openapi.json", openapi.CachedHandler(r, v2Config))// Define standard error response
type ProblemDetails struct {
Type string `json:"type"`
Title string `json:"title"`
Status int `json:"status"`
Detail string `json:"detail,omitempty"`
Instance string `json:"instance,omitempty"`
}
// Use consistent error responses
// @Failure 400 {object} ProblemDetails "Bad request"
// @Failure 401 {object} ProblemDetails "Unauthorized"
// @Failure 403 {object} ProblemDetails "Forbidden"
// @Failure 404 {object} ProblemDetails "Not found"
// @Failure 500 {object} ProblemDetails "Internal server error"
func MyHandler(w http.ResponseWriter, r *http.Request) {
// Implementation
}# Test the endpoint
curl http://localhost:8080/openapi | jq .
# Validate with swagger-codegen
npx @apidevtools/swagger-cli validate http://localhost:8080/openapi
# Generate client code
npx @openapitools/openapi-generator-cli generate \
-i http://localhost:8080/openapi \
-g typescript-fetch \
-o ./generated-client# Run Swagger UI with Docker
docker run -p 8080:8080 \
-e SWAGGER_JSON_URL=http://host.docker.internal:3000/openapi \
swaggerapi/swagger-ui
# Or with docker-compose
version: '3.8'
services:
swagger-ui:
image: swaggerapi/swagger-ui
ports:
- "8080:8080"
environment:
SWAGGER_JSON_URL: http://host.docker.internal:3000/openapifunc TestOpenAPISpec(t *testing.T) {
router := setupTestRouter()
config := openapi.Config{
Title: "Test API",
Version: "1.0.0",
}
handler := openapi.CachedHandler(router, config)
req := httptest.NewRequest("GET", "/openapi", nil)
w := httptest.NewRecorder()
handler(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var spec openapi.Spec
err := json.Unmarshal(w.Body.Bytes(), &spec)
assert.NoError(t, err)
assert.Equal(t, "3.1.0", spec.OpenAPI)
assert.Equal(t, "Test API", spec.Info.Title)
}The package includes several performance optimizations:
- Type Index Caching: Built-in type discovery cache
- Spec Caching: Generated specifications are cached
- Smart Invalidation: Cache invalidation when needed
- Lazy Loading: Types are discovered and parsed on-demand
// Invalidate cache programmatically
openapi.InvalidateCache()
// Force refresh via HTTP
curl http://localhost:8080/openapi?refresh=true
// Invalidate via endpoint
curl http://localhost:8080/openapi/invalidateThe package consists of several main components:
| Component | Purpose | Key Features |
|---|---|---|
| Generator | Main specification generator | Route walking, operation building |
| Router Discovery | Chi router analysis and route extraction | Route introspection, handler identification |
| Annotations | Comment parsing and annotation extraction | Swagger annotation support, error reporting |
| Schema Generator | Dynamic Go type to JSON schema conversion | Type discovery, recursive generation |
| Cache | Type indexing and performance optimization | AST caching, type lookup, smart invalidation |
Problem: Handler annotations are ignored.
Solution: Ensure handlers are top-level functions, not struct methods.
// Wrong
type Handler struct{}
func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {}
// Correct
func Create(w http.ResponseWriter, r *http.Request) {}Problem: Custom types not appearing in generated schemas.
Solution: Ensure types are in the same project or add external mappings.
// Add external type mapping
openapi.AddExternalKnownType("external.Type", &openapi.Schema{
Type: "object",
Description: "External type description",
})Problem: Infinite recursion with self-referencing types.
Solution: The package handles this automatically with reference cycles.
Problem: Slow generation with many types.
Solution: Use the built-in caching and consider pre-building type index.
- Go 1.24 or higher
- Git
# Clone the repository
git clone https://github.com/AxelTahmid/openapi-gen.git
cd openapi-gen
# Install dependencies
go mod download
# Run tests
go test ./...
# Run tests with coverage
go test -race -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Run benchmarks
go test -bench=. ./...
# Lint code
golangci-lint runopenapi-gen/
├── annotations.go # Annotation parsing and validation
├── annotations_test.go # Annotation parsing tests
├── cache.go # Type indexing and caching system
├── generator.go # Core OpenAPI specification generator
├── generator_spec_test.go # Generator integration tests
├── handlers.go # HTTP handlers for serving specs
├── openapi_test.go # OpenAPI generation tests
├── qualified_names_test.go # Type name resolution tests
├── router_discovery.go # Chi router route discovery
├── router_discovery_test.go # Router discovery tests
├── schema.go # Core schema generation logic
├── schema_basic_types.go # Basic Go type mappings
├── schema_enums.go # Enum type handling
├── schema_structs.go # Struct schema generation
├── schema_tags.go # JSON tag processing
├── schema_test.go # Schema generation tests
├── test_helpers.go # Test utilities and helpers
└── README.md # This file
# Run all tests
make test-openapi
# Run with verbose output
make test-openapi-verbose
# Run specific test
go test -run TestGenerator_GenerateSpec ./pkg/openapi
# Run benchmarks
go test -bench=BenchmarkGenerateSpec ./pkg/openapiSee the CONTRIBUTING.md.
When adding new features:
- Add comprehensive tests
- Update documentation and examples
- Consider backward compatibility
- Add benchmarks for performance-critical code
- Update the changelog
- Follow standard Go formatting (
gofmt) - Use meaningful variable and function names
- Add documentation for exported functions
- Keep functions focused and small
- Use structured logging with appropriate levels
- Better Error Messages: Improve AST parsing error reporting and debugging
- Enhanced Type Support: Better support for generics and complex nested types
- Documentation: More examples and edge case handling
- Performance: Optimize type discovery for large codebases
- Testing: Expand test coverage for edge cases
- OpenAPI 3.1 Full Compliance: Complete OpenAPI 3.1 specification support
- Validation Integration: Runtime request/response validation
- Mock Server Generation: Generate mock servers from specs
- Better SQLC Integration: Native support for more SQLC patterns
- Multiple Router Support: This package is Chi-specific by design
- GraphQL Support: Out of scope for this project
- Client Generation: Use existing OpenAPI client generators
- Struct Method Support: AST limitations make this impractical
Note: This project serves a specific need (Chi + SQLC + pgx/v5). Major feature additions should align with this core use case. For other routers or frameworks, consider using more general-purpose OpenAPI generators.
See the CHANGELOG.md.
- Documentation: Check this README and code examples
- Bug Reports: Open an issue on GitHub with detailed reproduction steps
- Feature Requests: Open an issue with the
enhancementlabel - Questions: Start a discussion in the repository discussions
- GitHub Issues: Bug reports and feature requests
- GitHub Discussions: Questions and community support
- Examples Repository: Real-world usage examples
- Blog Posts: Tutorials and best practices
This project is licensed under the MIT License - see the LICENSE file for details.
- Chi Router Team: For the excellent HTTP router
- OpenAPI Initiative: For the comprehensive API specification standard
- Go Community: For the amazing ecosystem and tools
- Contributors: Everyone who has contributed to this project
Made for the Go community
If this project helps you, please consider giving it a star on GitHub!
