4.8 KiB
4.8 KiB
Feature: User Preferences API
Problem Statement
Users need a way to persist and retrieve their application preferences (theme, language, notification settings) across sessions and devices. Currently, no preference storage exists — any customization is lost when a user logs out or switches devices. The preferences-api service exists as a scaffold with example CRUD patterns but has no preference-specific domain logic.
User Stories
- As an authenticated user, I want to save my theme preference so that the UI matches my preference across sessions.
- As an authenticated user, I want to set my preferred language so that the application displays in my chosen locale.
- As an authenticated user, I want to configure my notification settings so that I only receive alerts I care about.
- As a frontend application, I want to retrieve all preferences for a user in a single request so that the UI can be initialized efficiently.
- As an authenticated user, I want to update individual preferences without overwriting all others so that partial updates are safe.
Acceptance Criteria
GET /api/preferences-api/preferences/{user_id}returns all preferences for a user as key-value pairs in the{data, meta}envelopePUT /api/preferences-api/preferences/{user_id}creates or updates preferences for a user (upsert semantics)- Supported preference keys:
theme(values:light,dark,system),language(ISO 639-1 codes, e.g.en,fr,es),notifications_enabled(boolean as string:true,false) - Unknown preference keys are rejected with a
400 Bad Requestand descriptive error - Invalid preference values are rejected with a
400 Bad Requestand descriptive error (e.g.,theme: "blue"is invalid) - PUT is idempotent — sending the same request twice produces the same result
- PUT supports partial updates — sending only
{"theme": "dark"}does not clearlanguageornotifications_enabled - GET for a user with no preferences returns
200with an empty object{}(not 404) - Both endpoints require authentication via JWT (auth middleware)
- Authenticated users can only access their own preferences (user_id in path must match JWT subject), returning
403 Forbiddenotherwise - Preferences are persisted in PostgreSQL with a migration
- OpenAPI spec documents both endpoints with request/response schemas
- Handler tests cover success, validation errors, not-found, and authorization cases
- Service-layer tests cover business logic with mock repository
- Response times < 50ms at p99 for single-user preference reads
Technical Constraints
- Must follow existing hexagonal architecture: domain → service → port (interface) → adapter (implementation)
- Handlers must return
error, wrapped withapp.Wrap() - Request binding must use
app.Bind()orapp.BindAndValidate() - Errors must use
httperror.BadRequest,httperror.NotFound,httperror.Forbidden— never barehttp.Error() - Responses must use
httpresponse.OK,httpresponse.Created,httpresponse.NoContentwith{data, meta}envelope - Routes mount under
/api/preferences-apito match ingress routing - Database connection uses
pkg/databasewith embedded SQL migrations - Auth middleware uses
pkg/authwith JWT validation - The existing example CRUD code should be replaced, not left alongside preference code
user_idis a UUID, validated at the handler layer
Dependencies
- PostgreSQL database available (connection via
DATABASE_URLenvironment variable) pkg/databasepackage for connection pooling and migration supportpkg/authpackage for JWT middleware and user extraction- JWT secret configured via
JWT_SECRETenvironment variable AUTH_ENABLED=truein production environments
Out of Scope
- User registration or authentication (handled by a separate auth service)
- Preference inheritance or defaults cascade (e.g., org-level defaults)
- Real-time preference sync via WebSockets
- Preference history or audit trail
- Bulk preference operations across multiple users
- Frontend UI for preference management
- Rate limiting on preference endpoints
- Preference schema versioning or migration
Open Questions
- Default values: Should
GETreturn system defaults for keys that haven't been explicitly set (e.g.,{"theme": "system", "language": "en", "notifications_enabled": "true"}) or only return keys the user has explicitly set? - DELETE support: Should there be a
DELETE /preferences/{user_id}to reset all preferences, or aDELETE /preferences/{user_id}/{key}to reset individual ones? - Extensibility: When new preference keys are added in the future, should they be added to a configuration file/constant list, or is a code change acceptable?
- Admin access: Should admin users be allowed to read/write preferences for other users, or is self-access the only mode?