Apigen
HTTP API Layer Generator for the Go (golang) projects
Install / Use
/learn @gemyago/ApigenREADME
apigen - HTTP API Layer Generator
HTTP API Layer Generator for the Go (golang) projects. Write less boilerplate code, focus on the business logic.
Features:
- OpenAPI first approach. Write the spec and generate the code.
- No runtime dependencies. Generated code is self-contained.
- No reflection. Code to parse and validate requests is fully generated.
- Framework agnostic and http.Handler compatible.
Project status:
- The generated code is extensively tested and production ready
- The generator itself is in beta stage. This means that minor breaking changes in the generated code may occur.
Table of Contents
- Getting Started
- Basic Concepts
- Controllers in depth
- Root Handler
- Separate packages for models and controllers
- Unit Testing
- Supported OpenAPI features
- Contributing
- Releasing
Getting Started
The only runtime dependency is a go 1.24 or higher. The generator is a plugin for the OpenAPI Generator which is Java based and requires Java 11 runtime at a minimum. The java and openapi-generator are only required to generate the code. The generated code is self-contained and does not have any runtime dependencies.
To get started, install apigen cli tool:
go install github.com/gemyago/apigen
Define the OpenAPI spec somewhere in your project. For example: internal/api/http/routes.yaml. You can use below as a starting point:
openapi: "3.0.0"
info:
version: 1.0.0
title: Minimalistic API definition
paths:
/ping:
get:
operationId: ping
tags:
- ping
parameters:
- name: message
in: query
required: false
schema:
type: string
responses:
'200':
description: Request succeeded
content:
application/json:
schema:
type: object
properties:
message:
type: string
Add a golang file with generation instructions. For example: internal/api/http/routes.go:
//go:generate go run github.com/gemyago/apigen server ./routes.yaml ./routes
Run the generation:
go generate ./internal/api/http
The above will generate the code in the internal/api/http/routes folder. Commit the generated code to the repository.
Declare controller that implements the generated interface, for example:
func pingHandler(_ context.Context, params *models.PingParams) (*models.Ping200Response, error) {
message := params.Message
if message == "" {
message = "pong"
}
return &models.Ping200Response{Message: message}, nil
}
type pingController struct{}
func (c *pingController) Ping(b handlers.HandlerBuilder[
*models.PingParams,
*models.Ping200Response,
]) http.Handler {
return b.HandleWith(pingHandler)
}
Define router adapter. For example http.ServeMux adapter may look like this:
type httpRouter http.ServeMux
func (*httpRouter) PathValue(r *http.Request, paramName string) string {
return r.PathValue(paramName)
}
func (router *httpRouter) HandleRoute(method, pathPattern string, h http.Handler) {
(*http.ServeMux)(router).Handle(method+" "+pathPattern, h)
}
func (router *httpRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
(*http.ServeMux)(router).ServeHTTP(w, r)
}
Wire everything together:
rootHandler := handlers.NewRootHandler((*httpRouter)(http.NewServeMux()))
rootHandler.RegisterPingRoutes(&pingController{})
const readHeaderTimeout = 5 * time.Second
srv := &http.Server{
Addr: "[::]:8080",
ReadHeaderTimeout: readHeaderTimeout,
Handler: rootHandler,
}
log.Println("Starting server on port: 8080")
if err := srv.ListenAndServe(); err != nil {
panic(err)
}
Fully functional example based on the above steps can be found here. More advanced examples:
- petstore-server - example with more routes
- petstore-server-app-layer - models and controllers generated in a separate packages.
Basic Concepts
Generated code expects you to provide a controller that implements the generated interface. The controller is an adapter between the generated code and your business logic. Generated code will parse the request, validate parameters and call the corresponding controller method in a type-safe manner.
The controller is generated based on the tags in the OpenAPI spec. Prefer defining a single tag per operation. You can have multiple operations with a same tag in order to group them under the same generated controller. Single OpenAPI spec can define as many tags (controllers) as needed. Please note that operationIDs in OpenAPI are global and should be unique across the spec. Please see the Controllers in depth section for more details.
The generated code also includes so called RootHandler. The root handler is a bridge between your router of choice and the generated code. Please see the Root Handler section for more details.
Typically you will need to import generated code from the following packages:
handlerscontains controller interfaces, root handler and other components handle requests.modelscontains data structures corresponding to schemas defined in the OpenAPI spec.
It is possible to generate models and controllers in separate packages. This is useful when you want to keep your application layer separate from the HTTP API layer. Please see the Separate packages for models and controllers section for more details.
Controllers in depth
Controller should implement a set of methods, each corresponding to an operation in the OpenAPI spec. The method signature is as follows:
func (c *PetsController) GetPetByID(
b handlers.HandlerBuilder[*models.GetPetByIDParams, *models.PetResponse],
) http.Handler {
// Your implementation here
}
The HandlerBuilder allows you to create an actual http.Handler that will be used to process requests. In most simplest case you can implement the method in place. However in real-world scenarios you may want to extract the implementation to a separate component and keep your controller clean and declarative. It is not required to use the HandlerBuilder and you can return a http.Handler directly if your use-case requires it, this allows you to fully bypass the generated code and handle the request processing as required.
The HandlerBuilder has the following methods:
-
HandleWith- will bind your application logic to the generated code. The handler function should have the following signature:func(context.Context, *models.GetPetByIDParams) (*models.PetResponse, error)This would usually be the most typical way to implement the controller method. You can define the handler in place however it is advised have a separate component that implements the handler function. This approach will help you to keep your controller clean and declarative.
-
HandleWithHTTP- similar to the above, but allows you to access the underlying http.Request and http.ResponseWriter. The handler function should have the following signature:func(http.ResponseWriter, *http.Request, *models.GetPetByIDParams) (*models.PetResponse, error)This method is useful when you need to access the underlying http request and response objects. For example, when you need to set custom headers or status codes.
Notes:
- You may return response that will be automatically written to the response writer.
- The generated code will not attempt to write to the response writer if you have already written to it.
- You may still return an error. In this case the generated code will handle the error as explained in the Handling errors section.
Due to Go language constraints there are several variations of the HandlerBuilder. The variations are:
NoResponseHandlerBuilder- for operations that do not return a response. The handler function should have the following signature:func(context.Context, *models.DeletePetParams) errorNoRequestHandlerBuilder- for operations that do not have request parameters. The handler function should have the following signature:func(context.Context) (*models.PetResponse, error)NoRequestNoResponseHandlerBuilder- for operations that do not have request parameters and do not return a response. The handler function should have the following signature:func(context.Context) error
The generated code is type safe so you will catch mismatches at compile time.
Root Handler
The root handler is an adapter that allows you to attach generated routes to a router of your choice. Once initialized, the root handler is a self contained http.Handler and can be used in any scenario where you would use a standard http handler.
The root handler will have Register[Controller]Routes methods generated for each controller of your APIs. The method will accept an instance of the controller and will register all routes defined in the OpenAPI spec. Example:
rootHandler := handlers.New
Related Skills
bluebubbles
347.6kUse when you need to send or manage iMessages via BlueBubbles (recommended iMessage integration). Calls go through the generic message tool with channel="bluebubbles".
gh-issues
347.6kFetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]
healthcheck
347.6kHost security hardening and risk-tolerance configuration for OpenClaw deployments
node-connect
347.6kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
