A Go SDK for building OpenAPI 3.0.3 specifications programmatically. Generate complete OpenAPI documentation from your Go code with type-safe schema generation and comprehensive security support.
- Complete OpenAPI 3.0.3 Support: Generate valid OpenAPI specifications
- Type-Safe Schema Generation: Automatic schema creation from Go structs, maps, and JSON
- Comprehensive Security: Support for API Key, Bearer, Basic, OAuth2, and OpenID Connect
- Parameter Management: Path, query, header, and cookie parameters with examples
- Request/Response Bodies: Multiple content types and examples
- Route Management: Fluent API for building routes and operations
- Schema Compilation: Automatic schema consolidation and reference generation
go get github.com/hydronica/go-openapipackage main
import (
"fmt"
"github.com/hydronica/go-openapi"
)
func main() {
// Create a new OpenAPI document
doc := openapi.New("My API", "1.0.0", "A sample API")
// Add a route
route := doc.GetRoute("/users/{id}", "GET")
route.AddResponse(openapi.Response{
Status: 200,
Desc: "User retrieved successfully",
}.WithExample(map[string]any{
"id": 123,
"name": "John Doe",
"email": "[email protected]",
}))
// Generate JSON
fmt.Println(doc.JSON())
}//go:embed openapi.json
var baseSpec string
doc, err := openapi.NewFromJson(baseSpec)
if err != nil {
log.Fatal(err)
}Use Go structs with json and desc tags for the cleanest schema generation:
type User struct {
ID int `json:"id" desc:"Unique user identifier"`
Name string `json:"name" desc:"User's full name"`
Email string `json:"email" desc:"User's email address"`
Active bool `json:"active" desc:"Whether the user is active"`
Created time.Time `json:"created" desc:"Account creation timestamp"`
}
route.AddRequest(openapi.RequestBody{}.WithExample(User{}))Benefits:
- Clear, readable schema names (
Userinstead of hash) - Field descriptions from
desctags - Type-safe and refactor-friendly
Convert JSON strings directly to schemas:
route.AddRequest(openapi.RequestBody{}.WithJSONString(`{
"name": "John Doe",
"age": 30,
"email": "[email protected]"
}`))Benefits:
- Quick prototyping
- Easy for simple schemas
Limitations:
- Auto-generated hash names
- No field descriptions
Use map[string]Example for detailed field descriptions:
route.AddRequest(openapi.RequestBody{}.WithExample(map[string]openapi.Example{
"name": {Value: "John Doe", Desc: "User's full name"},
"age": {Value: 30, Desc: "User's age in years"},
"email": {Value: "[email protected]", Desc: "User's email address"},
}))Benefits:
- Field-level descriptions
- Flexible value types
Limitations:
- Auto-generated hash names
- More verbose syntax
// Get or create a route
route := doc.GetRoute("/users/{id}", "GET")
// Add tags for grouping
route.Tags("users", "management")
// Add summary
route.Summary = "Get user by ID"Path parameters are automatically detected from the path:
route := doc.GetRoute("/users/{id}/posts/{postId}", "GET")
// Automatically creates path parameters for 'id' and 'postId'
// Add examples for path parameters
route.PathParam("id", 123, "User ID")
route.PathParam("postId", "abc-123", "Post ID")// Single parameter
route.QueryParam("limit", 10, "Maximum number of results")
route.QueryParam("offset", 0, "Number of results to skip")
// Multiple parameters from struct
type QueryParams struct {
Limit int `json:"limit" desc:"Maximum results"`
Offset int `json:"offset" desc:"Results to skip"`
Search string `json:"search" desc:"Search term"`
}
route.QueryParams(QueryParams{Limit: 10, Offset: 0, Search: "example"})
// Multiple parameters from map
route.QueryParams(map[string]any{
"limit": 10,
"offset": 0,
"search": "example",
})route.HeaderParam("X-API-Version", "v1", "API version")
route.HeaderParam("X-Request-ID", "abc-123", "Request identifier")route.CookieParam("session", "abc123", "Session identifier")// Multiple examples for a single parameter
route.QueryParam("status", []string{"active", "inactive", "pending"}, "User status")
// Using Example struct for custom names
route.QueryParam("priority", []openapi.Example{
{Summary: "low", Value: 1},
{Summary: "medium", Value: 5},
{Summary: "high", Value: 10},
}, "Priority level")// From struct
type CreateUserRequest struct {
Name string `json:"name" desc:"User's name"`
Email string `json:"email" desc:"User's email"`
}
route.AddRequest(openapi.RequestBody{
Desc: "User creation data",
Required: true,
}.WithExample(CreateUserRequest{}))
// Multiple examples
route.AddRequest(openapi.RequestBody{}.
WithNamedExample("admin", CreateUserRequest{Name: "Admin", Email: "[email protected]"}).
WithNamedExample("user", CreateUserRequest{Name: "User", Email: "[email protected]"}))// Success response
route.AddResponse(openapi.Response{
Status: 200,
Desc: "User created successfully",
}.WithExample(User{}))
// Error response
route.AddResponse(openapi.Response{
Status: 400,
Desc: "Invalid request",
}.WithJSONString(`{"error": "validation failed", "details": "name is required"}`))
// Multiple status codes
route.AddResponse(openapi.Response{Status: 200, Desc: "Success"}.WithExample(User{}))
route.AddResponse(openapi.Response{Status: 404, Desc: "Not found"}.WithJSONString(`{"error": "user not found"}`))
route.AddResponse(openapi.Response{Status: 500, Desc: "Server error"}.WithJSONString(`{"error": "internal server error"}`))// Header-based API key
doc.AddAPIKeyAuth("YourUniqueNameApiKeyAuth", "X-API-Key", openapi.APIKeyInHeader, "API key for authentication")
// Query parameter API key
doc.AddAPIKeyAuth("ApiKeyQuery", "api_key", openapi.APIKeyInQuery, "API key as query parameter")
// Cookie-based API key
doc.AddAPIKeyAuth("ApiKeyCookie", "auth_token", openapi.APIKeyInCookie, "API key in cookie")doc.AddBearerAuth("BearerAuth", openapi.BearerFormatJWT, "Bearer token authentication")doc.AddBasicAuth("BasicAuth", "HTTP Basic authentication")flows := &openapi.Flows{
AuthorizationCode: &openapi.Flow{
AuthorizationURL: "https://example.com/oauth/authorize",
TokenURL: "https://example.com/oauth/token",
Scopes: map[string]string{
"read": "Read access",
"write": "Write access",
"admin": "Admin access",
},
},
}
doc.AddOAuth2Auth("OAuth2", flows, "OAuth2 authentication")doc.AddOpenIDConnectAuth("OpenIDConnect", "https://example.com/.well-known/openid_configuration", "OpenID Connect authentication")// Single security requirement for all endpoints
doc.AddSecurityRequirement("ApiKeyAuth", []string{})
// OAuth2 with scopes for all endpoints
doc.AddSecurityRequirement("OAuth2", []string{"read", "write"})
// Multiple security schemes (AND logic) for all endpoints
doc.AddMultipleSecurityRequirement(map[string][]string{
"ApiKeyAuth": {},
"OAuth2": {"read"},
})// Add security to individual routes
route := doc.GetRoute("/admin/users", "POST")
// Single security requirement for this route only
route.AddSecurity("OAuth2Auth", "admin", "write")
// Multiple security schemes for this route (AND logic)
route.AddMultipleSecurity(map[string][]string{
"BearerAuth": {},
"ApiKeyAuth": {},
})
// Different routes can have different security requirements
publicRoute := doc.GetRoute("/public", "GET")
// No security requirements needed for public route
protectedRoute := doc.GetRoute("/protected", "GET")
protectedRoute.AddSecurity("BearerAuth")
adminRoute := doc.GetRoute("/admin", "DELETE")
adminRoute.AddSecurity("OAuth2Auth", "admin")
// Clear security from a route if needed
route.ClearSecurity()The library provides constants for common security values:
// Security scheme types
openapi.SecurityTypeAPIKey // "apiKey"
openapi.SecurityTypeHTTP // "http"
openapi.SecurityTypeOAuth2 // "oauth2"
openapi.SecurityTypeOpenID // "openIdConnect"
// HTTP authentication schemes
openapi.HTTPSchemeBearer // "bearer"
openapi.HTTPSchemeBasic // "basic"
// API key locations
openapi.APIKeyInQuery // "query"
openapi.APIKeyInHeader // "header"
openapi.APIKeyInCookie // "cookie"
// Common bearer token formats
openapi.BearerFormatJWT // "JWT"doc.AddTags(
openapi.Tag{
Name: "users",
Desc: "User management operations",
},
openapi.Tag{
Name: "posts",
Desc: "Post management operations",
},
)doc.Servers = []openapi.Server{
{
URL: "https://api.example.com/v1",
Desc: "Production server",
},
{
URL: "https://staging-api.example.com/v1",
Desc: "Staging server",
},
}Convert Go-style paths to OpenAPI format:
// Convert ":id" to "{id}"
path := openapi.CleanPath("/users/:id/posts/:postId")
// Result: "/users/{id}/posts/{postId}"type Event struct {
Name string `json:"name"`
Date openapi.Time `json:"date"`
}
event := Event{
Name: "Meeting",
Date: openapi.Time{Time: time.Now(), Format: "2006-01-02"},
}Compile the document to consolidate schemas and validate:
if err := doc.Compile(); err != nil {
log.Printf("Validation errors: %v", err)
}// Set custom name for JSON schema
jsonData := openapi.JSONString(`{"name": "value"}`).SetName("CustomSchema")
route.AddRequest(openapi.RequestBody{}.WithExample(jsonData))package main
import (
"fmt"
"time"
"github.com/hydronica/go-openapi"
)
type User struct {
ID int `json:"id" desc:"Unique user identifier"`
Name string `json:"name" desc:"User's full name"`
Email string `json:"email" desc:"User's email address"`
Active bool `json:"active" desc:"Whether the user is active"`
Created time.Time `json:"created" desc:"Account creation timestamp"`
}
type CreateUserRequest struct {
Name string `json:"name" desc:"User's name"`
Email string `json:"email" desc:"User's email"`
}
type ErrorResponse struct {
Error string `json:"error" desc:"Error message"`
Details string `json:"details" desc:"Error details"`
}
func main() {
// Create document
doc := openapi.New("User API", "1.0.0", "A simple user management API")
// Add security schemes
doc.AddBearerAuth("BearerAuth", openapi.BearerFormatJWT, "Bearer token authentication")
// Add OAuth2 for admin endpoints
flows := &openapi.Flows{
AuthorizationCode: &openapi.Flow{
AuthorizationURL: "https://example.com/oauth/authorize",
TokenURL: "https://example.com/oauth/token",
Scopes: map[string]string{
"read": "Read access to user data",
"write": "Write access to user data",
"admin": "Administrative access",
},
},
}
doc.AddOAuth2Auth("OAuth2Auth", flows, "OAuth2 authentication for admin operations")
// Add tags
doc.AddTags(openapi.Tag{
Name: "users",
Desc: "User management operations",
})
// GET /users
listRoute := doc.GetRoute("/users", "GET")
listRoute.Tags("users")
listRoute.Summary = "List users"
listRoute.QueryParam("limit", 10, "Maximum number of users to return")
listRoute.QueryParam("offset", 0, "Number of users to skip")
listRoute.AddResponse(openapi.Response{
Status: 200,
Desc: "List of users",
}.WithExample([]User{{ID: 1, Name: "John Doe", Email: "[email protected]", Active: true}}))
// POST /users
createRoute := doc.GetRoute("/users", "POST")
createRoute.Tags("users")
createRoute.Summary = "Create user"
createRoute.AddRequest(openapi.RequestBody{
Desc: "User creation data",
Required: true,
}.WithExample(CreateUserRequest{Name: "Jane Doe", Email: "[email protected]"}))
createRoute.AddResponse(openapi.Response{
Status: 201,
Desc: "User created successfully",
}.WithExample(User{ID: 2, Name: "Jane Doe", Email: "[email protected]", Active: true}))
createRoute.AddResponse(openapi.Response{
Status: 400,
Desc: "Invalid request",
}.WithExample(ErrorResponse{Error: "validation failed", Details: "name is required"}))
// GET /users/{id} - requires bearer auth
getRoute := doc.GetRoute("/users/{id}", "GET")
getRoute.Tags("users")
getRoute.Summary = "Get user by ID"
getRoute.PathParam("id", 123, "User ID")
getRoute.AddSecurity("BearerAuth") // Route-specific security
getRoute.AddResponse(openapi.Response{
Status: 200,
Desc: "User details",
}.WithExample(User{ID: 1, Name: "John Doe", Email: "[email protected]", Active: true}))
getRoute.AddResponse(openapi.Response{
Status: 404,
Desc: "User not found",
}.WithExample(ErrorResponse{Error: "not found", Details: "user with id 123 not found"}))
// DELETE /users/{id} - requires admin OAuth2 scope
deleteRoute := doc.GetRoute("/users/{id}", "DELETE")
deleteRoute.Tags("users")
deleteRoute.Summary = "Delete user"
deleteRoute.PathParam("id", 123, "User ID")
deleteRoute.AddSecurity("OAuth2Auth", "admin") // Admin-only endpoint
deleteRoute.AddResponse(openapi.Response{
Status: 204,
Desc: "User deleted successfully",
})
deleteRoute.AddResponse(openapi.Response{
Status: 403,
Desc: "Insufficient permissions",
}.WithExample(ErrorResponse{Error: "forbidden", Details: "admin scope required"}))
// GET /public/info - no security required
publicRoute := doc.GetRoute("/public/info", "GET")
publicRoute.Tags("public")
publicRoute.Summary = "Get public information"
publicRoute.AddResponse(openapi.Response{
Status: 200,
Desc: "Public information",
}.WithJSONString(`{"version": "1.0.0", "status": "operational"}`))
// No security requirements for public endpoints
// Compile and validate
if err := doc.Compile(); err != nil {
fmt.Printf("Validation errors: %v\n", err)
}
// Output JSON
fmt.Println(doc.JSON())
}OpenAPI: Main document structureRoute: Individual API endpointRequestBody: Request body definitionResponse: Response definitionParam: Parameter definitionSchema: Data type definitionExample: Example value with description
New(title, version, description): Create new documentNewFromJson(spec): Create from existing JSONGetRoute(path, method): Get or create routeJSON(): Generate JSON outputCompile(): Validate and consolidate schemas
AddAPIKeyAuth(name, keyName, location, description): Add API key authenticationAddBearerAuth(name, bearerFormat, description): Add bearer token authenticationAddBasicAuth(name, description): Add basic authenticationAddOAuth2Auth(name, flows, description): Add OAuth2 authenticationAddOpenIDConnectAuth(name, url, description): Add OpenID Connect authenticationAddSecurityRequirement(schemeName, scopes...): Add global security requirementAddMultipleSecurityRequirement(schemes): Add multiple global security schemes
AddResponse(response): Add response to routeAddRequest(request): Add request body to routeAddSecurity(schemeName, scopes...): Add security requirement to specific routeAddMultipleSecurity(schemes): Add multiple security schemes to specific routeClearSecurity(): Remove all security requirements from routePathParam(name, value, desc): Add path parameterQueryParam(name, value, desc): Add query parameterHeaderParam(name, value, desc): Add header parameterCookieParam(name, value, desc): Add cookie parameterTags(tags...): Add tags to route
WithExample(value): Add example to request/responseWithJSONString(json): Add JSON string exampleWithNamedExample(name, value): Add named example
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.