Quickstarts
Backend service for integrated quickstarts.
Run the service locally
- 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)
Alternative: Manual steps
- 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'
IMPORTANT
oc port-forward -n quickstarts svc/quickstarts-service 8000:8000!
Sample requests
Create progress
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"}
}'
Delete progress
curl --location --request DELETE 'http://localhost:8000/api/quickstarts/v1/progress/14'
API Developer Guide
This section explains the API architecture and how to contribute to the backend service.
Architecture Overview
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
Key Principles
- 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
Core Components
1. OpenAPI Specification (spec/openapi.yaml)
Defines the API contract with endpoints, schemas, parameters, and validation rules.
Regenerate after changes:
make generate
make openapi-json
2. Generated Code (pkg/generated/api.go)
Auto-generated by oapi-codegen from the OpenAPI spec. ⚠️ Never edit this file directly.
3. Server Adapter (pkg/routes/server_adapter.go)
Implements the generated ServerInterface and contains:
- Parameter parsing and validation
- Legacy format support
- Service layer orchestration
- Response formatting
4. Service Layer (pkg/services/)
Contains business logic and data access.
5. Database Layer (pkg/database/)
Handles database connections and models via GORM.
Parameter Handling
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 |
Adding New Parameters
- 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
Standard Pagination
limit: Number of results to return (default: 50)
offset: Number of results to skip (default: 0)
Special Limit Values
limit=-1: No pagination - returns ALL results
limit=0: Invalid, defaults to 50
limit<-1: Invalid, defaults to 50
Filtering System
Tag-Based Filtering
The API supports filtering by multiple tag types:
bundle: Filter by bundle tags (rhel, insights, etc.)
application: Filter by application tags
product-families: Filter by product family tags
use-case: Filter by use case tags
content: Filter by content type tags
kind: Filter by kind tags
topic: Filter by topic tags
Name/Display Name Filtering
name: Exact match on quickstart name
display-name: Partial match (ILIKE) on display name in content JSON
Filter Priority
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)
Adding New Endpoints
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.
Success Response
{
"data": [/* array of results */]
}
Error Response
{
"msg": "Error message description"
}
HTTP Status Codes
200: Success
400: Bad Request (validation errors, invalid parameters)
404: Not Found (resource doesn't exist)
Testing
Running Tests
# All tests
go test ./...
# Specific package
go test ./pkg/routes -v
# Specific test
go test ./pkg/routes -run TestGetQuickstarts -v
Building and Deployment
Local Development
# 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
Docker Build
# Build with all generation steps
docker build -t quickstarts .
The Docker build includes API code generation, OpenAPI JSON conversion, validation, testing, and binary compilation.
Common Development Patterns
Parameter Validation
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
}
Error Handling
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)
Best Practices
- 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)
Troubleshooting
Common Issues
- 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.