12 KiB
API Refactor Task List
This document turns the current API refactor direction into a concrete task list for this repo.
Scope:
- modularize route and service structure
- strengthen validation boundaries
- add meaningful API test coverage
Non-goals:
- rewrite the API framework
- switch ORM or database
- redesign product behavior
Current State
Main pressure points in the current API:
- large route files with mixed concerns:
apps/api/src/routes/reservations.tsapps/api/src/routes/site.tsapps/api/src/routes/marketplace.tsapps/api/src/routes/admin.ts
- business logic split inconsistently between
routes/andservices/ - no real API test runner wired in
apps/api/package.json - ad hoc request parsing and response/error shaping across route files
- direct Prisma access from route handlers in many domains
Target Structure
Target layout after refactor:
apps/api/src/
modules/
customers/
customer.routes.ts
customer.schemas.ts
customer.service.ts
customer.repo.ts
customer.presenter.ts
customer.test.ts
vehicles/
companies/
reservations/
marketplace/
site/
http/
errors/
middleware/
respond/
validate/
lib/
services/
Rules for the target shape:
- route files parse input, call services, and return responses only
- services own business rules and transaction boundaries
- repos own Prisma access
- schemas own
params,query, andbodyvalidation - presenters own response DTO shaping
Phase 1: Foundation
1.1 Add API test tooling
Tasks:
- add
vitestto the repo - add
supertestfor HTTP integration tests - add API test scripts to:
apps/api/package.json- root
package.json
- add a basic test config under
apps/api - decide on test DB bootstrap using existing Docker test stack in
docker-compose.test.yml
Acceptance criteria:
npm run test --workspace @rentaldrivego/apiexists- one smoke test can boot the Express app and hit
/health - one authenticated test can hit a protected route
1.2 Centralize HTTP errors
Tasks:
- create
apps/api/src/http/errors/ - add app error classes:
AppErrorValidationErrorNotFoundErrorConflictErrorForbiddenErrorUnauthorizedError
- add a single Express error middleware
- register it in
apps/api/src/index.ts
Acceptance criteria:
- route files stop hand-shaping most error JSON
- thrown app errors become consistent
{ error, message, statusCode }responses
1.3 Centralize request parsing helpers
Tasks:
- create
apps/api/src/http/validate/ - add helpers for:
parseBodyparseQueryparseParams
- standardize
zodparse error mapping to400
Acceptance criteria:
- new/updated route files no longer call
schema.parse(req.body)inline repeatedly - validation failures follow one shared response shape
1.4 Centralize response helpers
Tasks:
- create
apps/api/src/http/respond/ - add helpers for:
okcreatednoContent
- use the same success envelope conventions across refactored modules
Acceptance criteria:
- refactored routes send responses through shared helpers
Phase 2: First Safe Module Extractions
Goal:
- refactor smaller route files first to establish patterns before touching reservation flows
2.1 Customers module
Files in scope:
apps/api/src/routes/customers.tsapps/api/src/services/licenseValidationService.ts- customer-related Prisma access
Tasks:
- create:
modules/customers/customer.routes.tsmodules/customers/customer.schemas.tsmodules/customers/customer.service.tsmodules/customers/customer.repo.tsmodules/customers/customer.presenter.ts
- move all Prisma reads/writes out of the route file
- move license image upload orchestration into the service
- standardize customer DTO output
Tests to add:
- list customers
- create customer
- update customer
- validate license
- approve license
- upload license image
Acceptance criteria:
routes/customers.tsbecomes a thin delegator or is replaced by module route registration- no direct Prisma calls remain in the customer route layer
2.2 Vehicles module
Files in scope:
apps/api/src/routes/vehicles.tsapps/api/src/services/vehicleAvailabilityService.tsapps/api/src/lib/storage.ts
Tasks:
- extract module files for vehicles
- isolate upload/photo operations from route code
- isolate availability query logic from route formatting
- create presenter functions for dashboard-facing vehicle responses
Tests to add:
- create vehicle
- update vehicle
- upload photos
- delete photo
- availability check
Acceptance criteria:
- vehicle upload and availability logic are both service-owned
2.3 Companies module
Files in scope:
apps/api/src/routes/companies.ts
Tasks:
- extract company brand/profile subdomain flows into a module
- isolate brand asset upload logic
- separate subdomain availability checks from route code
Tests to add:
- get company profile
- update company profile
- upload logo
- upload hero image
- subdomain check
Acceptance criteria:
- no upload/storage branching remains in the route handler
Phase 3: High-Value Reservation Refactor
This is the biggest and highest-risk slice.
3.1 Split reservations into sub-services
Files in scope:
apps/api/src/routes/reservations.tsapps/api/src/services/additionalDriverService.tsapps/api/src/services/insuranceService.tsapps/api/src/services/pricingRuleService.tsapps/api/src/services/licenseValidationService.tsapps/api/src/services/invoicePdfService.ts
Target internal split:
reservation.service.tsreservation.lifecycle.service.tsreservation.pricing.service.tsreservation.insurance.service.tsreservation.additional-driver.service.tsreservation.document.service.tsreservation.inspection.service.tsreservation.presenter.tsreservation.repo.ts
Tasks:
- extract create/update/confirm/check-in/check-out/close flows into dedicated service methods
- move
serializeReservationForDashboardinto a presenter - move document number generation out of route code
- keep Prisma transactions in the service layer only
- remove route-local helper sprawl from
reservations.ts
Tests to add:
- create reservation success
- create reservation conflict
- confirm reservation
- check-in reservation
- check-out reservation
- close reservation
- add additional driver requiring approval
- pricing rule application
- insurance application
Acceptance criteria:
- reservation route file becomes orchestration-only
- all state transitions are covered by integration tests
Phase 4: Public-Surface Modules
These routes should move after internal domains are stabilized.
4.1 Marketplace module
Files in scope:
apps/api/src/routes/marketplace.ts
Tasks:
- extract search/filter/offer validation into module files
- unify database-unavailable handling
- separate presenter logic from business logic
Tests to add:
- list cities
- search vehicles
- fetch vehicle details
- validate offer / promo path
4.2 Site module
Files in scope:
apps/api/src/routes/site.ts
Tasks:
- extract public company site flows
- isolate booking flow for public site reservations
- unify maintenance-mode / database-unavailable behavior
Tests to add:
- fetch site brand
- fetch public vehicle list
- availability check
- create booking request
- payment-init guard paths
Acceptance criteria:
- public route handlers become small and deterministic
Phase 5: Cross-Cutting Cleanup
5.1 Authentication and role boundaries
Files in scope:
apps/api/src/middleware/requireCompanyAuth.tsapps/api/src/middleware/requireRenterAuth.tsapps/api/src/middleware/requireAdminAuth.tsapps/api/src/middleware/requireRole.tsapps/api/src/middleware/requireTenant.tsapps/api/src/middleware/requireSubscription.ts
Tasks:
- standardize auth failure responses
- document what request context each middleware guarantees
- remove duplicate assumptions in route files
Acceptance criteria:
- auth and permission expectations are explicit and shared
5.2 Upload policy standardization
Files in scope:
apps/api/src/routes/vehicles.tsapps/api/src/routes/companies.tsapps/api/src/routes/customers.tsapps/api/src/lib/storage.tsdocker-compose.production.ymldocker-compose.dev.yml
Tasks:
- create shared upload policy helpers for image uploads
- standardize max file size, mime validation, and error responses
- centralize folder path rules for stored uploads
- keep runtime uploads out of the API source tree and out of the image filesystem
- require all uploaded files to be written to the configured storage root mounted from a Docker volume
- verify the API serves uploads from the volume-backed storage root only
- document the storage contract clearly:
- app code may generate URLs and read/write through the storage abstraction
- Docker volumes own persistence
- rebuilds and redeploys must not delete uploaded files
Acceptance criteria:
- upload endpoints share one validation policy
- uploaded files are not stored inside
apps/api/src,apps/api/dist, or any other API-local runtime path - production uploads persist in the named Docker volume mounted into the API container
- dev Docker uploads persist in the dev named Docker volume mounted into the API container
- storage behavior is environment-configured through the storage root, not hardcoded to an app-relative folder
5.3 Remove route-local DTO drift
Tasks:
- define presenter functions for each major domain
- stop returning raw Prisma records where API shape is intended to be stable
Acceptance criteria:
- DTO shape changes are isolated to presenters
Testing Plan
Priority 0: smoke coverage
Add first:
/health- authenticated customer list
- authenticated vehicle list
- reservation create
- site availability
Priority 1: critical business flows
Add next:
- reservation state changes
- uploads
- payments init guards
- license approval paths
Priority 2: regression coverage
Add later:
- analytics
- notifications
- admin routes
- subscriptions
Test types
Use:
- unit tests for pure business logic
- integration tests for route + middleware + DB behavior
- a few contract-style tests for public endpoints
Avoid:
- brittle snapshots
- tests that assert Prisma internal shapes directly unless repo-level persistence behavior is the actual concern
Suggested Execution Order
- add test tooling and smoke tests
- add shared error middleware and validation helpers
- refactor customers
- refactor vehicles
- refactor companies
- refactor reservations in slices
- refactor marketplace
- refactor site
- refactor admin, notifications, analytics, subscriptions, payments
Deliverables Checklist
- API test runner added
- shared error middleware added
- shared validation helpers added
- shared response helpers added
- customers module extracted
- vehicles module extracted
- companies module extracted
- reservations module extracted
- marketplace module extracted
- site module extracted
- upload validation standardized
- auth/role middleware behavior documented
- critical booking flows covered by integration tests
Status Update
Completed in the workspace:
- phases 1 through 5 are implemented
- the originally listed phase 9 domains are modularized, and the remaining route surface was also cleared for:
teamoffersnotificationsanalyticssubscriptionspaymentsadminauth.*webhooks
apps/api/src/routes/has been cleared and route registration now points at module routers- shared HTTP helpers are in place under:
apps/api/src/http/errors/apps/api/src/http/validate/apps/api/src/http/respond/
- the Docker-backed API integration workflow is wired and passing
Current automated integration coverage includes:
- health
- customers
- vehicles
- reservations
- payments
- subscriptions
- notifications
- admin
Still intentionally open:
- upload policy standardization from section
5.2 - any remaining Priority 1 integration gaps not yet covered by the current suite, especially upload and license-approval paths if they are still required as dedicated HTTP flows
Completion Criteria
The refactor is considered successful when:
- route files are thin and mostly declarative
- business rules live in services, not route handlers
- Prisma access lives in repos or well-defined service boundaries
- request validation is explicit for
params,query, andbody - core booking, customer, and upload flows have automated coverage
- API error and success envelopes are consistent across modules