All API changes start with the OpenAPI specification. Never implement an endpoint without first defining it in the spec.
- Edit
spec/openapi.yaml— define endpoints, parameters, schemas - Run
make generate— regeneratepkg/generated/api.go - Run
make openapi-json— update JSON version - Implement in
pkg/routes/server_adapter.go— theServerAdaptermust satisfygenerated.ServerInterface - Add service logic in
pkg/services/if needed - Add tests
- Run
make validate-apito verify spec compliance
pkg/generated/api.go is auto-generated by oapi-codegen. Configuration is in oapi-codegen.yaml:
package: generated
generate:
models: true
chi-server: true
client: false
embedded-spec: true
output: pkg/generated/api.goNever edit pkg/generated/api.go. All behavioral logic goes in server_adapter.go.
The ServerAdapter struct implements generated.ServerInterface. It delegates to service-layer objects:
type ServerAdapter struct {
quickstartService *services.QuickstartService
helpTopicService *services.HelpTopicService
favoriteService *services.FavoriteService
progressService *services.ProgressService
}Each handler method should:
- Parse and validate parameters (including legacy format support)
- Call the appropriate service method
- Format the response using generated types
- Set appropriate HTTP status codes
The API supports both OpenAPI-standard and legacy bracket-notation parameters:
| Standard | Legacy | Both must work |
|---|---|---|
bundle=rhel&bundle=insights |
bundle[]=rhel&bundle[]=insights |
Yes |
product-families=ansible |
product-families[]=ansible |
Yes |
When adding new array parameters, always implement both formats. Use r.URL.Query() to check for legacy [] suffixed keys.
limit(default: 50) — number of resultsoffset(default: 0) — skip countlimit=-1— returns all results (no pagination)limit=0orlimit<-1— defaults to 50
- Define in
spec/openapi.yamlundercomponents/parameters - Reference in the endpoint's
parameterslist - Regenerate:
make generate - Parse in server adapter, including legacy format fallback
- Pass to the service layer
{"data": [...]}Always wrap results in a data key, even for single items.
Use generated error types:
// 400 Bad Request
msg := "Invalid parameter"
resp := generated.BadRequest{Msg: &msg}
// 404 Not Found
msg := "Quickstart not found"
resp := generated.NotFound{Msg: &msg}Set Content-Type: application/json and the appropriate status code before encoding.
| Code | When |
|---|---|
| 200 | Successful read/update |
| 400 | Validation error, invalid parameters |
| 404 | Resource not found |
The service layer supports tag-based filtering. Tag types: bundle, application, product-families, use-case, content, kind, topic.
Filter priority in the service layer:
- Exact name match (highest)
- Tag filtering + display name
- Display name only
- No filters (return all, paginated)
Prefer additive changes. Removing or renaming parameters, changing response shapes, or altering filter behavior can break the HCC frontend (learning-resources repo). When in doubt, support both old and new formats.