OpenAPI in Frame¶
Frame serves OpenAPI specs that are embedded at compile time and exposed through a deterministic, discoverable endpoint. No runtime parsing or dynamic discovery is required.
Production Goals¶
- Compile-time OpenAPI specs baked into the binary
- Deterministic runtime behavior (byte-for-byte serving)
- Clear regeneration path (single command +
go generate) - CI-friendly generation and validation
Quick Start (One Command)¶
Generate specs and wire them automatically:
go run github.com/pitabwire/frame/cmd/frame@latest openapi \
-proto-dir proto \
-out pkg/openapi/specs \
-embed-dir pkg/openapi \
-package openapi
This will:
- Run
buf generatewith the Connect OpenAPI plugin - Write OpenAPI JSON into
pkg/openapi/specs - Generate
pkg/openapi/embed.gowith anOption()helper - Add a
go:generatedirective for easy regeneration
Then enable in your service:
package main
import (
"context"
"log"
"github.com/pitabwire/frame"
"your/module/pkg/openapi"
)
func main() {
ctx, svc := frame.NewService(
frame.WithName("users-service"),
openapi.Option(),
)
if err := svc.Run(ctx, ":8080"); err != nil {
log.Fatal(err)
}
}
Served Endpoints¶
When at least one spec is registered:
GET /debug/frame/openapireturns a JSON index of specsGET /debug/frame/openapi/{name}returns a specific spec
Example response:
{
"specs": [
{"name": "users", "filename": "users.json"},
{"name": "billing", "filename": "billing.json"}
]
}
Compile-Time Guarantee¶
Specs are embedded with //go:embed and registered as static byte slices. The runtime only serves those bytes; it does not parse or regenerate OpenAPI.
Regenerate With go generate¶
After the first run, you can regenerate from the embed package:
go generate ./pkg/openapi
Custom Base Path¶
frame.WithOpenAPIBasePath("/debug/frame/openapi")
CI Integration¶
Recommended pipeline step (example):
go run github.com/pitabwire/frame/cmd/frame@latest openapi --proto-dir proto --out pkg/openapi/specs --embed-dir pkg/openapi --package openapi
go test ./...
For CI, ensure buf is installed and available in PATH.
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
| Specs not served | openapi.Option() not passed |
Register the option in NewService |
| No specs found | Wrong output dir or embed path | Ensure pkg/openapi/specs/*.json exists |
buf generate fails |
Buf not installed or invalid workspace | Install buf and ensure proto/ has a valid buf.yaml or buf.work.yaml |