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/- Newhello.gohandler fileservices/api/internal/api/routes.go- Route registrationservices/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.
- No migration needed - No database changes
- No feature flags needed - Endpoint is stateless and low-risk
- No backwards compatibility concerns - New endpoint, no existing consumers
Rollout steps:
- Deploy code changes (handler, routes, spec)
- Verify endpoint responds at
/api/v1/hello - 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
-
Handler location: Create
handlers/hello.goalongside existing handlers rather than adding tohealth.go- the spec recommends a dedicated "Hello" tag to distinguish from infrastructure health checks. -
Response type: Define
HelloResponsein the handler file (not a separate types file) since it's only used by this handler. -
Route registration: Add
helloHandlerinitialization inRegisterRoutesand register route in the public routes section (outside the auth group). -
OpenAPI tag: Add
"Hello"tag with description"Simple greeting endpoint"to differentiate from Health endpoints. -
Test pattern: Follow
example_test.gopattern with table-driven tests, though a single happy-path test is sufficient for this simple endpoint.