Backend service for integrated quickstarts.
- There are environment variables required for the application to start. It's
recommended you copy
.env.example
to.env
and set these appropriately for local development. - Start required infrastructure (database):
make infra
- Migrate the database:
make migrate
. It will seed the DB with testing quickstart - Start the development server:
make dev
(generates API and starts server)
- Generate API:
make generate
- Start server:
go run .
- Query data:
curl --location --request GET 'http://localhost:8000/api/quickstarts/v1/quickstarts/'
curl --location --request GET 'http://localhost:8000/api/quickstarts/v1/quickstarts/?bundle[]=rhel&bundle[]=insights'
oc port-forward -n quickstarts svc/quickstarts-service 8000:8000
!
curl --location --request POST 'http://localhost:8000/api/quickstarts/v1/progress' --header 'Content-Type: application/json' --data-raw '{
"accountId": 123, "quickstartName": "some-name", "progress": {"Some": "Progress-updated"}
}'
curl --location --request DELETE 'http://localhost:8000/api/quickstarts/v1/progress/14'
This section explains the API architecture and how to contribute to the backend service.
The API follows a layered architecture using OpenAPI code generation:
OpenAPI Spec → oapi-codegen → Server Adapter → Services → Database
↓ ↓ ↓ ↓ ↓
spec/openapi.yaml → pkg/generated → pkg/routes → pkg/services → pkg/database
- OpenAPI Spec is Source of Truth: The specification in
spec/openapi.yaml
defines the API contract - Implementation Follows Spec: Server adapter and services implement the behavior defined in the OpenAPI spec
- Spec-First Development: API changes start with updating the OpenAPI specification, then implementing
- Backward Compatibility: Legacy parameter formats must be supported to avoid frontend regressions
Defines the API contract with endpoints, schemas, parameters, and validation rules.
Regenerate after changes:
make generate
make openapi-json
Auto-generated by oapi-codegen from the OpenAPI spec.
Implements the generated ServerInterface
and contains:
- Parameter parsing and validation
- Legacy format support
- Service layer orchestration
- Response formatting
Contains business logic and data access.
Handles database connections and models via GORM.
The API supports both OpenAPI-standard and legacy array parameter formats:
Parameter Type | Standard Format | Legacy Format | Example |
---|---|---|---|
Single Array | bundle=rhel&bundle=insights |
bundle[]=rhel&bundle[]=insights |
Multiple values |
Product Families | product-families=ansible |
product-families[]=ansible |
ansible, tower |
Name/Display Name | name=quickstart-name |
display-name[]=name |
Single strings |
- Add to OpenAPI spec (
spec/openapi.yaml
):
parameters:
NewParam:
name: new-param
description: Description of new parameter
in: query
required: false
schema:
type: array
items:
type: string
explode: true
style: form
- Reference in endpoint:
parameters:
- $ref: '#/components/parameters/NewParam'
-
Regenerate code:
make generate
-
Update server adapter to handle the parameter
-
Update service layer to use the new parameter
limit
: Number of results to return (default: 50)offset
: Number of results to skip (default: 0)
limit=-1
: No pagination - returns ALL resultslimit=0
: Invalid, defaults to 50limit<-1
: Invalid, defaults to 50
The API supports filtering by multiple tag types:
bundle
: Filter by bundle tags (rhel, insights, etc.)application
: Filter by application tagsproduct-families
: Filter by product family tagsuse-case
: Filter by use case tagscontent
: Filter by content type tagskind
: Filter by kind tagstopic
: Filter by topic tags
name
: Exact match on quickstart namedisplay-name
: Partial match (ILIKE) on display name in content JSON
The service layer applies filters in this order:
- Exact name match (highest priority)
- Tag filtering + display name
- Display name only
- No filters (return all with pagination)
Follow this spec-first workflow:
- Define in OpenAPI Spec (
spec/openapi.yaml
) - Design the API contract first - Regenerate Code:
make generate
- Generate types and interfaces - Implement in Server Adapter (
pkg/routes/server_adapter.go
) - Implement the generated interface - Add Service Layer Logic (
pkg/services/
) - Add business logic and data access - Add Tests - Test the complete endpoint functionality
Important: Always start with the OpenAPI specification. The spec defines the contract that the implementation must follow.
{
"data": [/* array of results */]
}
{
"msg": "Error message description"
}
200
: Success400
: Bad Request (validation errors, invalid parameters)404
: Not Found (resource doesn't exist)
# All tests
go test ./...
# Specific package
go test ./pkg/routes -v
# Specific test
go test ./pkg/routes -run TestGetQuickstarts -v
# Setup
cp .env.example .env # Configure environment variables
# Install dependencies
go mod download
# Start infrastructure (database)
make infra
# Migrate database (includes test data seeding)
make migrate
# Quick start: Generate API and start development server
make dev
# Or run individual steps:
# Generate API code
make generate
# Start server manually
go run .
# Run tests
make test
# Build binary
go build -o quickstarts
# Cleanup: Stop infrastructure
make stop-infra
# Build with all generation steps
docker build -t quickstarts .
The Docker build includes API code generation, OpenAPI JSON conversion, validation, testing, and binary compilation.
if requiredParam == "" {
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("Content-Type", "application/json")
msg := "Required parameter missing"
resp := generated.BadRequest{Msg: &msg}
json.NewEncoder(w).Encode(resp)
return
}
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("Content-Type", "application/json")
msg := err.Error()
resp := generated.BadRequest{Msg: &msg}
json.NewEncoder(w).Encode(resp)
return
}
// Convert internal models to API types
genResults := make([]generated.SomeType, len(results))
for i, result := range results {
genResults[i] = result.ToAPI()
}
// Standard response format
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
resp := map[string][]generated.SomeType{"data": genResults}
json.NewEncoder(w).Encode(resp)
- Always maintain backward compatibility
- Test both standard and legacy parameter formats
- Document parameter behavior changes
- Use consistent error response formats
- Add tests for new functionality
- Update OpenAPI spec to match implementation
- Validate edge cases (empty arrays, special values like -1)
- Parameter not working: Check both OpenAPI spec and legacy parameter parsing
- Generated code outdated: Run
make generate
after spec changes - Type mismatches: Ensure OpenAPI types match Go service layer expectations
- Tests failing: Check for breaking changes in parameter handling
For questions about the API architecture, refer to spec/openapi.yaml
as the authoritative source of API behavior, with implementation details in pkg/routes/server_adapter.go
.