6.0 KiB
6.0 KiB
Feature: User Preferences API
Problem Statement
Users of the platform have no way to persist their personal settings (theme, language, notification preferences). Without a preferences API, every client must manage settings locally, leading to inconsistent experiences across devices and sessions. The preferences-api service currently contains only scaffold/example code and needs to be replaced with real preference management functionality.
User Stories
- As a user, I want to save my preferred theme so that the UI looks the same every time I log in.
- As a user, I want to set my preferred language so that content is displayed in my language across devices.
- As a user, I want to configure my notification settings so that I only receive the alerts I care about.
- As a frontend developer, I want a simple GET/PUT API to read and write user preferences so that I can build settings pages without managing local storage.
Acceptance Criteria
GET /api/preferences-api/preferences/{user_id}returns all preferences for a user as key-value pairsGETreturns a default set of preferences (theme,language,notifications) when the user has no saved preferencesPUT /api/preferences-api/preferences/{user_id}creates or updates preferences for a user (upsert semantics)PUTvalidates thatthemeis one of:light,dark,systemPUTvalidates thatlanguageis a valid BCP-47 language tag (e.g.,en,fr,es,de,ja)PUTvalidates thatnotificationsis an object with boolean fields:email,push,in_appPUTsupports partial updates — omitted keys retain their current values- All responses follow the
{data, meta}envelope pattern - Preferences are persisted in PostgreSQL (not in-memory)
- Database migration creates the preferences table
user_idis validated as a UUID formatPUTon protected route group (requires auth whenAUTH_ENABLED=true)GETon protected route group (requires auth whenAUTH_ENABLED=true)- Handler tests cover success, validation errors, not-found defaults, and invalid user ID
- Service-layer tests cover business logic with mock repository
- Domain model enforces preference value constraints
- OpenAPI spec documents both endpoints with request/response schemas
- Existing Example CRUD scaffold code is removed
API Design
GET /api/preferences-api/preferences/{user_id}
Response 200:
{
"data": {
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"preferences": {
"theme": "dark",
"language": "en",
"notifications": {
"email": true,
"push": false,
"in_app": true
}
},
"updated_at": "2026-01-15T10:30:00Z"
},
"meta": {
"request_id": "...",
"timestamp": "..."
}
}
PUT /api/preferences-api/preferences/{user_id}
Request body (partial update):
{
"theme": "dark",
"notifications": {
"email": false
}
}
Response 200:
{
"data": {
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"preferences": {
"theme": "dark",
"language": "en",
"notifications": {
"email": false,
"push": true,
"in_app": true
}
},
"updated_at": "2026-02-08T12:00:00Z"
},
"meta": {
"request_id": "...",
"timestamp": "..."
}
}
Default Preference Values
| Key | Default Value |
|---|---|
theme |
system |
language |
en |
notifications.email |
true |
notifications.push |
true |
notifications.in_app |
true |
Technical Constraints
- Database: PostgreSQL via
pkg/database(connection pool, migrations, transactions) - Architecture: Hexagonal — domain, service, port (interface), adapter (postgres implementation)
- Router: chi with
{param}brace syntax for URL parameters - Validation:
pkg/httpvalidationwithgo-playground/validatorstruct tags - Error handling: Domain errors mapped to HTTP errors via
httperror.*factories - Response format: All responses wrapped via
httpresponse.OK,httpresponse.Created, etc. - Auth: Opt-in via
auth.Middleware()in route group, controlled byAUTH_ENABLEDenv var - Migrations: SQL files in
services/preferences-api/migrations/usingpkg/database.RunMigrations - Port: Service runs on port 8001 (existing allocation)
Database Schema
CREATE TABLE IF NOT EXISTS user_preferences (
user_id UUID PRIMARY KEY,
theme VARCHAR(10) NOT NULL DEFAULT 'system',
language VARCHAR(10) NOT NULL DEFAULT 'en',
notif_email BOOLEAN NOT NULL DEFAULT true,
notif_push BOOLEAN NOT NULL DEFAULT true,
notif_in_app BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
Dependencies
- PostgreSQL database available (connection string via
DATABASE_URLenv var) pkg/databasepackage for connection pooling and migrationspkg/app,pkg/httperror,pkg/httpresponse,pkg/httpvalidationpackages (all exist)pkg/authfor JWT middleware (exists)
Out of Scope
- User creation/management (user IDs come from an external auth system)
- Preference history/audit log
- Preference schemas beyond
theme,language,notifications - Admin endpoints to manage preferences for other users
- Real-time preference sync via WebSocket
- Preference import/export
Open Questions
- Authorization model: Should
GET /preferences/{user_id}be restricted so users can only read their own preferences, or can any authenticated user read any user's preferences? (Spec assumes auth-protected but no ownership check — simplest to start.) - Extensible keys: Should the API support arbitrary preference keys beyond the three defined, or should it be a fixed schema? (Spec assumes fixed schema for type safety and validation.)
- Rate limiting: Should PUT be rate-limited to prevent abuse? (Spec assumes no — can be added later via middleware.)