feat-dev-e2e3/.sdlc/features/add-hello-endpoint/design.md
rdev-worker 7d9eafbb43
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
build: /design-feature add-hello-endpoint
2026-02-03 03:04:46 +00:00

10 KiB

Design: Add /hello Endpoint to API Service

Architecture Approach

This is a minimal addition that follows existing patterns exactly. No new architectural components are introduced.

Layers changed:

  • services/api/internal/api/handlers/ - New hello.go handler file
  • services/api/internal/api/routes.go - Route registration
  • services/api/internal/api/spec.go - OpenAPI documentation

Approach: Create a standalone handler struct with a single Say method that returns a greeting. The handler follows the error-returning pattern used by Example handler (not the direct http.HandlerFunc pattern used by Health), allowing for consistent error handling if we add complexity later.

Data Model Changes

No database or schema changes required.

Response structure (uses existing envelope):

// HelloResponse is the data returned by GET /api/v1/hello
type HelloResponse struct {
    Message string `json:"message"`
}

Envelope output:

{
  "data": {
    "message": "Hello, World!"
  },
  "meta": {
    "request_id": "abc123",
    "timestamp": "2026-02-03T12:00:00Z"
  }
}

API Changes

New Endpoint

Method Path Auth Description
GET /api/v1/hello None Returns greeting message

Request

No request body or parameters.

Response

Success (200 OK):

{
  "data": {
    "message": "Hello, World!"
  },
  "meta": {
    "request_id": "...",
    "timestamp": "..."
  }
}

Component Diagram

┌──────────────────────────────────────────────────────────────┐
│                        HTTP Request                          │
│                     GET /api/v1/hello                        │
└──────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌──────────────────────────────────────────────────────────────┐
│                    Chi Router (routes.go)                    │
│                                                              │
│  r.Get("/hello", app.Wrap(helloHandler.Say))                │
└──────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌──────────────────────────────────────────────────────────────┐
│                   Middleware Stack                           │
│  [RequestID] → [CORS] → [Recovery] → [Logger]               │
│  (Already configured in app.App)                             │
└──────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌──────────────────────────────────────────────────────────────┐
│                    app.Wrap()                                │
│  Converts error-returning handler to http.HandlerFunc       │
│  Maps HTTPError → status code, other errors → 500           │
└──────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌──────────────────────────────────────────────────────────────┐
│                handlers/hello.go                             │
│                                                              │
│  func (h *Hello) Say(w, r) error {                          │
│      httpresponse.OK(w, r, HelloResponse{                   │
│          Message: "Hello, World!",                          │
│      })                                                      │
│      return nil                                              │
│  }                                                           │
└──────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌──────────────────────────────────────────────────────────────┐
│                  httpresponse.OK()                           │
│  Wraps data in {data, meta} envelope                        │
│  Adds request_id and timestamp to meta                      │
│  Sets Content-Type: application/json                        │
└──────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌──────────────────────────────────────────────────────────────┐
│                        HTTP Response                         │
│                         200 OK                               │
│        {"data": {"message": "Hello, World!"}, "meta": ...}  │
└──────────────────────────────────────────────────────────────┘

Error Handling Strategy

This endpoint has minimal failure modes:

Scenario Handling Response
Normal request Return greeting 200 with {data, meta}
Middleware panic Recovery middleware catches 500 with generic error
JSON encoding failure Caught by httpresponse.OK 500 internal error

Since this handler:

  • Takes no input (no validation errors possible)
  • Makes no external calls (no timeout/network errors)
  • Has no authorization (no auth errors possible)

The only realistic failure mode is a panic in the middleware stack, which is already handled by the Recovery middleware.

Decision: Use error-returning handler pattern (app.Wrap) for consistency with other handlers, even though this specific handler always returns nil. This makes future extension easier if we add features like parameterized greetings.

Security Considerations

Concern Mitigation
Authentication Endpoint is intentionally public (no auth required)
Authorization No protected resources accessed
Input validation No input accepted (GET with no params/body)
Data exposure Only static string returned, no sensitive data
Rate limiting Uses global rate limiting if configured (out of scope for this feature)
CORS Uses existing CORS middleware configuration

Risk assessment: Low risk. This is a read-only endpoint returning static content with no user input and no sensitive data.

Performance Considerations

Aspect Analysis
Expected load Low - primarily used for connectivity checks
Response time Sub-millisecond (no I/O, no computation)
Memory Negligible - single small response struct
Caching Not needed - response is static and fast to generate
Connection pooling N/A - no database or external calls

Optimization: None required. The endpoint is already optimal by design.

Migration / Rollout Plan

Zero-downtime deployment: This is a purely additive change.

  1. No migration needed - No database changes
  2. No feature flags needed - Endpoint is stateless and low-risk
  3. No backwards compatibility concerns - New endpoint, no existing consumers

Rollout steps:

  1. Deploy code changes (handler, routes, spec)
  2. Verify endpoint responds at /api/v1/hello
  3. Verify OpenAPI spec includes new endpoint at /docs

Rollback: Standard deployment rollback if issues arise (no data cleanup needed).

File Changes Summary

File Change Type Description
services/api/internal/api/handlers/hello.go New Handler struct and Say method
services/api/internal/api/handlers/hello_test.go New Unit tests for handler
services/api/internal/api/routes.go Modify Register GET /api/v1/hello route
services/api/internal/api/spec.go Modify Add Hello tag and /hello path

Implementation Notes

  1. Handler location: Create handlers/hello.go alongside existing handlers rather than adding to health.go - the spec recommends a dedicated "Hello" tag to distinguish from infrastructure health checks.

  2. Response type: Define HelloResponse in the handler file (not a separate types file) since it's only used by this handler.

  3. Route registration: Add helloHandler initialization in RegisterRoutes and register route in the public routes section (outside the auth group).

  4. OpenAPI tag: Add "Hello" tag with description "Simple greeting endpoint" to differentiate from Health endpoints.

  5. Test pattern: Follow example_test.go pattern with table-driven tests, though a single happy-path test is sufficient for this simple endpoint.