-
Notifications
You must be signed in to change notification settings - Fork 0
Public and Internal Specs
Neoma can serve two OpenAPI specifications from the same API. The public spec filters out items marked hidden, while the internal spec includes everything. This lets you expose debug endpoints or internal fields only to internal consumers.
Add hidden:"true" to any struct field. The field still works at runtime but is excluded from the public spec.
type ListInput struct {
Page int `query:"page" default:"1" doc:"Page number"`
Debug bool `query:"debug" hidden:"true" doc:"Enable debug output"`
}
type Item struct {
ID int `json:"id" doc:"Item ID"`
InternalID string `json:"internal_id" hidden:"true" doc:"Internal tracking ID"`
}Set Hidden: true on the operation. The endpoint is still routed and callable, but does not appear in the public spec.
neoma.Register(api, core.Operation{
OperationID: "debug-info",
Method: http.MethodGet,
Path: "/debug",
Hidden: true,
}, handler)The HiddenParameters field on core.Operation holds parameters that are excluded from the public spec but injected into the internal spec. The framework populates this automatically from hidden:"true" input fields.
config := neoma.DefaultConfig("My API", "1.0.0")
config.InternalSpec = core.InternalSpecConfig{
Enabled: true,
Path: "/internal/openapi",
DocsPath: "/internal/docs", // Defaults to "/internal/docs" if empty
Middlewares: core.Middlewares{}, // Optional: protect with auth middleware
}InternalSpecConfig fields:
| Field | Type | Description |
|---|---|---|
Enabled |
bool |
Activate internal spec serving |
Path |
string |
Base path for internal spec (.json and .yaml suffixes are added) |
DocsPath |
string |
Path for the internal docs UI (defaults to /internal/docs) |
Middlewares |
core.Middlewares |
Middleware applied to all internal spec and docs endpoints |
Public (always active):
| Endpoint | Description |
|---|---|
GET /openapi.json |
Public OpenAPI JSON |
GET /openapi.yaml |
Public OpenAPI YAML |
GET /public/docs |
Public docs UI |
Internal (when enabled):
| Endpoint | Description |
|---|---|
GET /internal/openapi.json |
Internal OpenAPI JSON (includes hidden items) |
GET /internal/openapi.yaml |
Internal OpenAPI YAML (includes hidden items) |
GET /internal/docs |
Internal docs UI |
package main
import (
"context"
"fmt"
"log"
"net/http"
"github.com/MeGaNeKoS/neoma/adapters/neomachi/v5"
"github.com/MeGaNeKoS/neoma/core"
"github.com/MeGaNeKoS/neoma/neoma"
"github.com/go-chi/chi/v5"
)
type ListInput struct {
Page int `query:"page" default:"1"`
Debug bool `query:"debug" hidden:"true" doc:"Internal debug flag"`
}
type ListOutput struct {
Body struct {
Items []string `json:"items"`
DebugInfo string `json:"debug_info" hidden:"true"`
}
}
func main() {
router := chi.NewMux()
adapter := neomachi.NewAdapter(router)
config := neoma.DefaultConfig("Dual Spec API", "1.0.0")
config.InternalSpec = core.InternalSpecConfig{
Enabled: true,
Path: "/internal/openapi",
}
api := neoma.NewAPI(config, adapter)
// Public endpoint with hidden field + param
neoma.Get(api, "/items",
func(ctx context.Context, input *ListInput) (*ListOutput, error) {
resp := &ListOutput{}
resp.Body.Items = []string{"a", "b"}
if input.Debug {
resp.Body.DebugInfo = "debug data"
}
return resp, nil
},
)
// Hidden endpoint (internal only)
neoma.Register(api, core.Operation{
OperationID: "health-check",
Method: http.MethodGet,
Path: "/health",
Hidden: true,
}, func(ctx context.Context, _ *struct{}) (*struct{ Body struct{ OK bool } }, error) {
resp := &struct{ Body struct{ OK bool } }{}
resp.Body.OK = true
return resp, nil
})
fmt.Println("Public docs: http://localhost:8080/public/docs")
fmt.Println("Internal docs: http://localhost:8080/internal/docs")
log.Fatal(http.ListenAndServe(":8080", router))
}In this example, GET /items appears in both specs, but the debug query parameter and debug_info response field only appear in the internal spec. GET /health only appears in the internal spec.