REST framework + OpenAPI SDK generation for the Lethean Go ecosystem
Replace the hardcoded Swagger 2.0 JSON template with SpecBuilder-backed OpenAPI 3.1 generation. The swagger spec is now built lazily from registered RouteGroups (including DescribableGroup and ToolBridge endpoints) and cached via sync.Once. Uses unique swag instance names to avoid global registry collisions in tests. Co-Authored-By: Virgil <virgil@lethean.io> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| api.go | ||
| api_test.go | ||
| authentik.go | ||
| authentik_integration_test.go | ||
| authentik_test.go | ||
| authz_test.go | ||
| bridge.go | ||
| bridge_test.go | ||
| brotli.go | ||
| brotli_test.go | ||
| cache.go | ||
| cache_test.go | ||
| CLAUDE.md | ||
| export.go | ||
| export_test.go | ||
| expvar_test.go | ||
| go.mod | ||
| go.sum | ||
| graphql.go | ||
| graphql_test.go | ||
| group.go | ||
| group_test.go | ||
| gzip_test.go | ||
| httpsign_test.go | ||
| i18n.go | ||
| i18n_test.go | ||
| LICENCE | ||
| location_test.go | ||
| middleware.go | ||
| middleware_test.go | ||
| openapi.go | ||
| openapi_test.go | ||
| options.go | ||
| pprof_test.go | ||
| README.md | ||
| response.go | ||
| response_test.go | ||
| secure_test.go | ||
| sessions_test.go | ||
| slog_test.go | ||
| sse.go | ||
| sse_test.go | ||
| static_test.go | ||
| swagger.go | ||
| swagger_test.go | ||
| timeout_test.go | ||
| tracing.go | ||
| tracing_test.go | ||
| websocket.go | ||
| websocket_test.go | ||
go-api
REST framework + OpenAPI SDK generation for the Lethean Go ecosystem.
Overview
go-api provides a Gin-based HTTP engine that subsystems plug into via the RouteGroup interface. Each ecosystem package (go-ml, go-rag, go-agentic, etc.) registers its own route group, and go-api handles the HTTP plumbing, middleware, response envelopes, WebSocket integration, and OpenAPI spec generation.
Quick Start
import api "forge.lthn.ai/core/go-api"
engine, _ := api.New(
api.WithAddr(":8080"),
api.WithBearerAuth("my-token"),
api.WithCORS("*"),
api.WithRequestID(),
api.WithSwagger("My API", "Description", "0.1.0"),
)
engine.Register(myRoutes)
engine.Serve(ctx)
Implementing a RouteGroup
type Routes struct{ service *mypackage.Service }
func (r *Routes) Name() string { return "mypackage" }
func (r *Routes) BasePath() string { return "/v1/mypackage" }
func (r *Routes) RegisterRoutes(rg *gin.RouterGroup) {
rg.GET("/items", r.ListItems)
rg.POST("/items", r.CreateItem)
}
func (r *Routes) ListItems(c *gin.Context) {
items, _ := r.service.List(c.Request.Context())
c.JSON(200, api.OK(items))
}
Authentik Integration
engine, _ := api.New(
api.WithAuthentik(api.AuthentikConfig{
Issuer: "https://auth.lthn.io/application/o/core-api/",
ClientID: "core-api",
TrustedProxy: true, // Read X-authentik-* headers from Traefik
}),
)
In handlers, use GetUser() to access the authenticated user:
func (r *Routes) ListItems(c *gin.Context) {
user := api.GetUser(c) // nil if unauthenticated
if user != nil {
// user.Username, user.Email, user.Groups, user.UID
}
}
For protected routes, use RequireAuth() or RequireGroup():
func (r *Routes) RegisterRoutes(rg *gin.RouterGroup) {
rg.GET("/public", r.PublicEndpoint)
rg.GET("/private", api.RequireAuth(), r.PrivateEndpoint)
rg.GET("/admin", api.RequireGroup("admins"), r.AdminEndpoint)
}
Features
- Response envelope —
api.OK(),api.Fail(),api.Paginated()for consistent JSON responses - Middleware — Bearer auth, CORS, request ID generation, panic recovery
- Authentik — Forward auth headers + OIDC JWT validation,
RequireAuth()andRequireGroup()guards - WebSocket —
WithWSHandler()mounts anyhttp.Handlerat/ws - Swagger UI —
WithSwagger()serves interactive API docs at/swagger/ - Health check — Built-in
GET /healthendpoint
Licence
EUPL-1.2