Typed is a Go tool that automatically generates OpenAPI 3.0 specifications for projects using the Echo web framework. It uses a sophisticated two-stage approach combining AST (Abstract Syntax Tree) parsing and reflection to analyze your code and produce accurate, comprehensive API documentation.
Most of existing code-first OpenAPI generators for Go require extensive manual annotations. Typed solves this by automatically analyzing your Echo handlers and generating accurate OpenAPI specifications without large code modifications.
- Two-Stage Generation Process: Combines AST analysis with runtime reflection for maximum accuracy
- Code-first OpenAPI Generation approach: Generates OpenAPI 3.0 specifications from your echo server, no magic comments required
- Intelligent Parameter Detection: Automatically detects path, query, and form parameters from inline usage
- Type Inference: Determines parameter types from usage context (e.g.
strconv.Atoi,uuid.Parse) - Extensible Architecture: Customizable type providers and schema customizers
- Multiple Parameter Sources: Supports path parameters, query parameters, and form data (including file uploads)
- Type Registry Generation: Creates Go code with type registry for reflection-based analysis
- Multiple Output Formats: Supports both YAML and JSON output formats
- Echo Framework Integration: Specifically designed for Echo-based projects
- Library Mode: Generated code can be used as a library
go install github.com/d1vbyz3r0/typed/cmd/typed@latest
go get github.com/d1vbyz3r0/typed@latestYou can find demo swagger here. Used openapi spec was generated with typed, you can find it in examples
To generate OpenAPI specification for your Echo project, write yaml config and add go generate directives to your code ( or run them manually). Example of config file can be found here
You should have somewhere an object, which will expose routes and middlewares and implement following interface:
type RoutesProvider interface {
OnRouteAdded(func(
host string,
route echo.Route,
handler echo.HandlerFunc,
middleware []echo.MiddlewareFunc,
))
ProvideRoutes()
}Then provide a package of your implementation and name of object constructor in configuration file:
routes-provider-ctor: NewServerBuilder
routes-provider-pkg: github.com/d1vbyz3r0/typed/examples/api//go:generate typed -config ../typed.yaml
//go:generate go run ../gen/spec_gen.goIf you don't want to implement interface, you can use typed as library and provide options to Generate function
typed [flags]
Flags:
-config string Path to config fileThe tool automatically detects the output format based on file extension:
- YAML:
.yamlor.ymlextensions - JSON:
.jsonextension
One of Typed's most powerful features is its ability to automatically detect and analyze parameter usage from your Echo handlers:
func GetUser(c echo.Context) error {
// Typed detects 'id' parameter and infers int type from strconv.Atoi usage. Note that for now you should pass c.Param directly as argument
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
return err
}
// Handler logic...
return c.JSON(http.StatusOK, user)
}
// Route: e.GET("/users/:id", GetUser)
// Result: OpenAPI path parameter 'id' with integer typefunc SearchUsers(c echo.Context) error {
// Typed detects 'limit' as integer and 'active' as boolean
limit, _ := strconv.Atoi(c.QueryParam("limit"))
active, _ := strconv.ParseBool(c.QueryParam("active"))
// Handler logic...
return c.JSON(http.StatusOK, users)
}
// Result: OpenAPI query parameters with correct typesfunc UpdateProfile(c echo.Context) error {
// Typed detects form fields and file uploads
name := c.FormValue("name")
email := c.FormValue("email")
// File upload detection
avatar, err := c.FormFile("avatar")
if err != nil {
return err
}
// Handler logic...
return c.JSON(http.StatusOK, response)
}
// Result: OpenAPI form schema with string fields and binary file fieldOf course, you also can declare parameters as struct fields with necessary tags. When both struct tag and inline usage are found, the struct field will have priority.
For more look at examples.
For now, xml and form tags are not fully supported. You can still use them, but names should be as they occur in field names if the tag is standalone:
type AuthForm struct {
Name string `form:"Name"`
Age int `xml:"Age"`
}Or same as json tag, if you for some reason need to process multiple data formats
type AuthForm struct {
Name string `json:"name" form:"name" xml:"name"`
Age int `json:"age" form:"name" xml:"name"`
}It will be fixed in the nearest future, when I figure out how to properly implement that
Typed automatically infers parameter types from common conversion functions:
| Package | Function | Inferred Type |
|---|---|---|
strconv |
Atoi |
int |
strconv |
ParseInt |
int64 |
strconv |
ParseUint |
uint |
strconv |
ParseFloat |
float64 |
strconv |
ParseBool |
bool |
uuid |
Parse, MustParse |
uuid.UUID |
time |
Parse |
time.Time |
Typed automatically detects and extracts Go enums (constants with custom types) and includes them in the OpenAPI specification as enum values. This ensures your API documentation accurately reflects the allowed values for enum fields.
Typed analyzes your Go constants and automatically generates OpenAPI enum schemas:
// String-based enums
type Role string
const (
RoleAdmin = Role("admin")
RoleUser = Role("user")
RoleGuest = Role("guest")
)
// Integer-based enums
type Status int
const (
StatusNew Status = 1
StatusDone Status = 2
StatusCancelled Status = 3
)
// Usage in structs
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Role Role `json:"role"`
Status Status `json:"status"`
}The above Go enums are automatically converted to OpenAPI enum schemas:
components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: string
role:
type: string
enum: [ "admin", "user", "guest" ]
status:
type: integer
enum: [ 1, 2, 3 ]Typed supports various enum value types:
| Go Type | Example | OpenAPI Type |
|---|---|---|
string |
Role("admin") |
string with enum values |
int |
Status(1) |
integer with enum values |
float64 |
Priority(1.5) |
number with enum values |
bool |
Flag(true) |
boolean with enum values |
You can extend type inference by registering custom type providers:
// Example from common/typing/type.go
func RegisterTypeProvider(p Provider) {
providers = append(providers, p)
}
// Custom provider example
func customProvider(pkg string, funcName string) (reflect.Type, bool) {
if pkg == "mypackage" && funcName == "ParseCustomType" {
return reflect.TypeOf(MyCustomType{}), true
}
return nil, false
}Customize OpenAPI schema generation with custom functions:
// Example from customizers.go
func RegisterCustomizer(fn openapi3gen.SchemaCustomizerFn) {
customizers = append(customizers, fn)
}
// Built-in customizers include:
// - Field name overrides from struct tags
// - File upload handling
// - UUID format specification
// - Enum value supportTyped provides a hook system that allows you to customize OpenAPI specification generation based on handler analysis. This is particularly useful for automatically detecting and documenting middleware-specific behavior.
Typed includes a built-in hook that automatically detects Echo JWT middleware usage and adds appropriate security schemes to your OpenAPI specification: To enable it, add following to your config file:
processing-hooks:
- "EchoJWTMiddlewareHook"func GetProtectedResource(c echo.Context) error {
// Your protected handler logic
return c.JSON(http.StatusOK, data)
}
// Route with JWT middleware
protected := e.Group("/api")
protected.Use(echojwt.WithConfig(echojwt.Config{
SigningKey: []byte("secret"),
}))
protected.GET("/users", GetProtectedResource)Result: Automatically adds Bearer token security scheme to the OpenAPI specification:
components:
securitySchemes:
bearerAuthScheme:
type: http
scheme: bearer
bearerFormat: JWT
paths:
/api/users:
get:
security:
- bearerAuthScheme: [ ]You can register custom hooks to extend the specification generation process:
// Example from middlewares.go
func RegisterHandlerProcessingHook(hook HandlerProcessingHookFn) {
handlerProcessingHooks = append(handlerProcessingHooks, hook)
}
// Custom hook example
func CustomAuthHook(spec *openapi3.T, operation *openapi3.Operation, handler handlers.Handler) {
// Analyze handler middlewares
for _, mw := range handler.Middlewares() {
middlewareName := typed.GetMiddlewareFuncName(mw)
if strings.Contains(middlewareName, "myauth") {
// Add custom security scheme
if spec.Components.SecuritySchemes == nil {
spec.Components.SecuritySchemes = make(map[string]*openapi3.SecuritySchemeRef)
}
spec.Components.SecuritySchemes["customAuth"] = &openapi3.SecuritySchemeRef{
Value: &openapi3.SecurityScheme{
Type: "apiKey",
In: "header",
Name: "X-API-Key",
},
}
// Apply to operation
if operation.Security == nil {
operation.Security = openapi3.NewSecurityRequirements()
}
operation.Security.With(openapi3.SecurityRequirement{
"customAuth": []string{},
})
}
}
}
// Register your custom hook
func init() {
typed.RegisterHandlerProcessingHook(CustomAuthHook)
}type HandlerProcessingHookFn func (spec *openapi3.T, operation *openapi3.Operation, handler handlers.Handler)Parameters:
spec: The OpenAPI specification being builtoperation: The current operation being processedhandler: Handler information including middlewares, route, and metadata
- Authentication/Authorization: Automatically detect auth middlewares and add security schemes
- Rate Limiting: Add rate limit headers and responses based on middleware detection
- CORS: Document CORS headers and preflight responses
- Validation: Add validation error responses based on validation middleware
- Logging/Monitoring: Add operation IDs or tags based on middleware configuration
- Custom Headers: Document custom headers added by middlewares
Typed provides utilities to analyze middleware functions:
// Get middleware function name for analysis
middlewareName := GetMiddlewareFuncName(middleware)
// Example middleware names:
// "github.com/labstack/echo-jwt.(*Config).ToMiddleware.func1"
// "github.com/labstack/echo/v4/middleware.CORS.func1"
// "myproject/middleware.CustomAuth"This hook system makes Typed highly extensible and allows it to automatically document complex middleware behavior without manual specification.
Typed uses a sophisticated two-stage approach to overcome the limitations of pure AST analysis:
- AST Parsing: Analyzes your Go source code using Go's AST parser
- Type Discovery: Identifies all types used in Echo handlers and routes
- Registry Generation: Generates Go code with a type registry containing all discovered types
- Standard Functions: Includes utility functions for reflection-based analysis
- Library Output: The generated code can be used as a standalone library
- Registry Execution: Runs the generated code to access
reflect.Typeinformation - Echo Route Analysis: Maps Echo routes to their corresponding handlers
- Schema Generation: Uses kin-openapi to create OpenAPI schemas from reflection data
- Specification Assembly: Builds complete OpenAPI 3.0 specification with proper SchemaRefs
- Output Generation: Saves specification in requested format (YAML/JSON)
The two-stage approach is necessary because:
- AST Limitation: During AST analysis, we cannot access
reflect.Typeinformation - Runtime Reflection: We need actual type information to generate accurate schemas
- Best of Both Worlds: Combines compile-time analysis with runtime type information
- kin-openapi: Powers the OpenAPI 3.0 specification generation, schema creation, and SchemaRef handling
- Go AST: For source code analysis and type discovery
- Go Reflection: For runtime type information access
Contributions are welcome! This project was created because similar tools weren't available for Echo framework projects.
This project is licensed under the MIT License - see the LICENSE file for details.
- kin-openapi - Essential for OpenAPI 3.0 specification generation and schema handling
- Echo Framework - High performance, minimalist Go web framework
- Go AST & Reflection - Powerful code analysis and runtime type inspection capabilities
If you encounter any issues or have questions:
- Check existing Issues
- Create a new issue with detailed description
- Include code examples and error messages
- Add support for form and xml tags
- Write doc for configuration
- Enhanced comment parsing for OpenAPI descriptions
- Headers support
- Add more std hooks