A powerful, modern code generator that creates clean server boilerplate from OpenAPI v3 specifications. Built with Go 1.22+ and the latest dependencies, featuring enhanced error handling and extensive customization options.
- Quick Start
- Key Ideas
- Installation
- Usage
- Complete Workflow Example
- OpenAPI Features
- Extensions Reference
- Generic Response Mode
- Limitations
- Questions or Feature Requests
- Contributing
- Go 1.22+ (updated for latest language features)
- Valid OpenAPI 3.0+ specification file
- Basic understanding of Go modules
go install github.com/mikekonan/go-oas3@latest# Generate from local file
go-oas3 -swagger-addr swagger.yaml -package myapi -path ./generated
# Generate from remote URL
go-oas3 -swagger-addr https://example.com/api/swagger.yaml -package myapi -path ./generated- Request Parsing: Stubs handle all request parsing logic automatically
- Type Safety: Response builders ensure you can only respond according to your specification
- Validation: Built-in validation for all request parameters and bodies
- Security: Automatic security middleware generation from OpenAPI security schemes
Note: Path stub generation relies on the first tag from your paths. While tags are not required, they are strongly recommended for better organization:
- With tags: Creates separate service interfaces per tag (e.g.,
UserService,OrderService) - Without tags: All operations are grouped under a single
DefaultServiceinterface
Example with tags:
paths:
/users:
get:
tags: [users] # Creates UserService interface
/orders:
post:
tags: [orders] # Creates OrderService interface| Flag | Type | Description | Default |
|---|---|---|---|
-swagger-addr |
string | Path or URL to OpenAPI specification | swagger.yaml |
-package |
string | Required. Go package name for generated code | - |
-path |
string | Required. Output directory for generated files | - |
-componentsPackage |
string | Package name for components (if different from main) | Same as -package |
-componentsPath |
string | Path for components (if different from main) | Same as -path |
-authorization |
string | Headers for remote swagger files (key1:value1,key2:value2) |
- |
-prioritize-x-go-type |
bool | Prioritize x-go-type over schema properties |
false |
-pass-raw-request |
bool | Pass raw HTTP request to handler functions | false |
-default-generics |
bool | Generate every operation in generic mode by default (see Generic Response Mode); can be overridden per-operation with x-go-generics. |
false |
# Basic local generation
go-oas3 -swagger-addr swagger.yaml -package api -path ./generated
# Remote file with custom components
go-oas3 \
-swagger-addr https://petstore3.swagger.io/api/v3/openapi.json \
-package petstore \
-path ./api \
-componentsPackage models \
-componentsPath ./api/models
# With authorization headers
go-oas3 \
-swagger-addr https://api.example.com/swagger.yaml \
-package myapi \
-path ./generated \
-authorization "X-API-Key:secret,Authorization:Bearer token"Here's a complete example from an OpenAPI spec to a running server:
openapi: 3.0.0
info:
title: User API
version: 1.0.0
paths:
/users/{id}:
get:
tags: [users]
parameters:
- name: id
in: path
required: true
schema:
type: string
x-go-type: github.com/google/uuid.UUID
responses:
'200':
description: User found
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
description: User not found
components:
schemas:
User:
type: object
required: [id, email]
properties:
id:
type: string
format: uuid
email:
type: string
format: email
name:
type: string
x-go-omitempty: truego-oas3 -swagger-addr api.yaml -package userapi -path ./generatedpackage main
import (
"context"
"net/http"
"github.com/go-chi/chi/v5"
userapi "./generated"
)
type UsersService struct{}
func (s *UsersService) GetUsersId(ctx context.Context, request userapi.GetUsersIdRequestObject) userapi.GetUsersIdResponseObject {
// Your business logic here
user := userapi.User{
Id: request.Id,
Email: "user@example.com",
Name: "John Doe",
}
return userapi.GetUsersId200JSONResponse{
Body: user,
}
}
func main() {
service := &UsersService{}
r := chi.NewRouter()
userapi.HandlerFromMux(service, r)
http.ListenAndServe(":8080", r)
}# Initialize Go module
go mod init myproject
# Add dependencies and run
go mod tidy
go run main.go# Test the endpoint
curl http://localhost:8080/users/550e8400-e29b-41d4-a716-446655440000
# Response:
# {
# "id": "550e8400-e29b-41d4-a716-446655440000",
# "email": "user@example.com",
# "name": "John Doe"
# }The generated boilerplate includes:
- Type-safe handlers - Request/response objects with proper Go types
- Automatic validation - Built-in validation for all parameters and request bodies
- Router integration - Ready-to-use chi router setup
- Error handling - Structured error responses matching your OpenAPI spec
Required fields in path, query, headers, and components are supported.
Security schemes for HTTP and API key (header/cookie) are supported.
Response header Set-Cookie is supported. Cookies in requests are supported via security schemes only.
Type validation supports the following data types:
- string: minLength, maxLength
- number, integer: minimum, maximum, exclusiveMinimum, exclusiveMaximum
The generator supports several OpenAPI types for components:
| OpenAPI Type | Go Type |
|---|---|
| uuid | github.com/google/uuid.UUID |
| iso4217-currency-code | github.com/mikekonan/go-types/v2/currency.Code |
| iso3166-alpha-2 | github.com/mikekonan/go-types/v2/country.Alpha2Code |
| iso3166-alpha-3 | github.com/mikekonan/go-types/v2/country.Alpha3Code |
The generator supports powerful OpenAPI extensions to customize Go code generation:
Specify custom Go types for any schema:
# Use encoding/json.RawMessage for flexible JSON
metadata:
type: object
x-go-type: encoding/json.RawMessage
# Use third-party types
user_id:
type: string
x-go-type: github.com/google/uuid.UUID
# Use custom domain types
amount:
type: string
x-go-type: github.com/shopspring/decimal.DecimalFor string parameters that need custom parsing:
# Custom UUID parsing from string
user_id:
type: string
x-go-type: github.com/google/uuid.UUID
x-go-type-string-parse: github.com/google/uuid.Parse
# Custom time parsing
created_at:
type: string
x-go-type: time.Time
x-go-type-string-parse: github.com/spf13/cast.ToTimeE# Make field a pointer (useful for optional fields)
optional_amount:
type: integer
x-go-pointer: true
# Generates: OptionalAmount *int `json:"optional_amount,omitempty"`# Add omitempty to JSON tag
description:
type: string
x-go-omitempty: true
# Generates: Description string `json:"description,omitempty"`# Automatically trim whitespace before validation
title:
type: string
minLength: 1
x-go-string-trimmable: true
# Trims spaces before checking minLength# Add regex validation to parameters
phone:
type: string
x-go-regex: ^\+?[1-9]\d{1,14}$
# Generates validation code with regexp.MustCompile# Skip validation for performance-critical paths
large_payload:
type: object
properties:
data:
type: string
x-go-skip-validation: trueOpt an operation into generic-mode generation. The response builder cascade
(20+ types per operation) is replaced by a single shared *Response[B]. See
Generic Response Mode for the full picture.
paths:
/widgets:
post:
x-go-generics: true
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/Widget'Use -default-generics on the CLI to flip the default for the whole spec; then
x-go-generics: false can opt individual operations back to classic.
# For operations that parse auth but don't enforce it
paths:
/health:
get:
x-go-skip-security-check: true
security:
- ApiKeyAuth: []
# Parses auth header but doesn't fail on missing/invalid auth# Custom map with typed keys/values
metadata:
type: object
additionalProperties:
type: string
x-go-map-type: map[github.com/google/uuid.UUID]string
# Generates: map[uuid.UUID]string instead of map[string]stringcomponents:
schemas:
User:
type: object
required: [id, email]
properties:
id:
type: string
x-go-type: github.com/google/uuid.UUID
email:
type: string
format: email
x-go-regex: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
profile:
type: object
x-go-pointer: true
x-go-omitempty: true
properties:
bio:
type: string
x-go-string-trimmable: true
metadata:
type: object
additionalProperties:
type: string
x-go-type: map[string]interface{}
x-go-skip-validation: false
parameters:
UserID:
name: user_id
in: path
required: true
schema:
type: string
x-go-type: github.com/google/uuid.UUID
x-go-type-string-parse: github.com/google/uuid.ParseBy default the generator emits a dedicated response-builder cascade for every
operation: XxxResponseBuilder, XxxStatusCodeBuilder, Xxx200ContentTypeBuilder,
Xxx200ApplicationJsonResponseBuilder, plus their methods. For a spec with
several status codes per endpoint that's ~20 types and ~280 lines of generated
code per operation.
Generic mode collapses this into a single shared *Response[B] type per
package. Opt in by marking the operation with x-go-generics: true (or flip
the global default via -default-generics).
- The service-interface return type becomes
*Response[BodyT]whereBodyTis inferred from the 2xx response'sapplication/jsonschema (anyif no body is declared). - The per-operation builder cascade is not emitted.
- The request struct embeds
RequestMeta(sharedProcessingResult+SecurityCheckResults) and gains aGetHeader() anyaccessor, so it satisfies the package-levelRequestinterface.
When at least one operation uses x-go-generics, the generator emits these
one-time symbols:
// Implemented by every generic request — application helpers can accept any
// generic request as a single parameter.
type Request interface {
GetProcessingResult() RequestProcessingResult
GetSecurityCheckResults() map[SecurityScheme]string
GetHeader() any
}
type RequestMeta struct {
ProcessingResult RequestProcessingResult
SecurityCheckResults map[SecurityScheme]string
}
// Single shared response type — replaces the per-operation cascade.
type Response[B any] struct { /* ... */ }
type ResponseBuilder[B any] struct { /* ... */ }
func NewResponse[B any]() *ResponseBuilder[B]
// Fluent setters on ResponseBuilder[B]:
// .Status(code int)
// .ContentType(ct string) // default "application/json"
// .ApplicationJson()
// .ApplicationXml()
// .ApplicationOctetStream()
// .TextHtml() / .TextPlain() / .TextCsv()
// .Body(B) // typed success body
// .BodyAny(any) // error body of a different type
// .BodyRaw([]byte)
// .Header(key, value string)
// .Cookie(c http.Cookie)
// .Redirect(url string)
// .Build() *Response[B]These were extracted to keep per-operation code small regardless of generic mode:
respond(w, r, hooks, name, response, hasContent)— single shared HTTP writer. Per-operation handlers become 4 lines.ensureHooks(*Hooks) *Hooks— fills every nil hook with a no-op stub, so generated code calls hooks unconditionally.extractSecurity(r, hooks, name, requirements, skipCheck)— runs the OR-of-AND security matrix; replaces ~80 inlined lines per operation.
paths:
/widgets:
post:
tags: [widgets]
x-go-generics: true
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CreateWidgetRequest'
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/Widget'
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorBody'Service implementation:
func (s *widgetsService) PostWidgets(ctx context.Context, req PostWidgetsRequest) *Response[Widget] {
if err := req.ProcessingResult.Err(); err != nil {
return NewResponse[Widget]().Status(400).
BodyAny(ErrorBody{Code: "bad_request", Message: err.Error()}).
Build()
}
return NewResponse[Widget]().Status(200).
Body(Widget{ID: "w-1", Name: req.Body.Name}).
Build()
}Generic and classic operations live side-by-side in the same generated
package — only operations marked with x-go-generics: true (or covered by
-default-generics) use the new shape. Migration can be done one operation
at a time without touching the rest of the codebase.
The generator currently has limited support for inline schemas in responses. For example:
# ❌ Not fully supported - may cause compilation issues
responses:
'200':
content:
application/json:
schema:
type: object
properties:
message:
type: string
# ✅ Recommended - use $ref to components
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/SuccessResponse'Workaround: Define all response schemas in components/schemas and reference them using $ref.
- Anonymous slice elements and map entries have limited support
- Complex nested anonymous types may not generate correctly
Workaround: Define explicit component schemas for complex types.
- Always use $ref for schemas - Avoid inline type definitions
- Define reusable components - Create schemas in
components/schemas - Use meaningful names - Component names become Go type names
- Test generated code - Always compile and test after generation
Have a question or need some functionality? Feel free to open an issue or submit a pull request.
Go OpenAPI v3 server code generator uses github.com/dave/jennifer for code generation. Using github.com/aloder/tojen is the suggested way to generate Jennifer code.