Home · Apps · rl-main-infra · todo_api · todo_mobile
todo_api is a monolithic NestJS GraphQL service with modular boundaries for auth, list/item domain logic, and synchronization support.
ListsService, ItemsService)graph TD
A[AppModule] --> B[AuthModule]
A --> C[SyncModule]
A --> D[ListsModule]
A --> E[ItemsModule]
D --> C
E --> C
E --> D
B --> F[JwtStrategy + Passport JWT]
C --> G[SyncService]
D --> H[ListsResolver + ListsService]
E --> I[ItemsResolver + ItemsService]
flowchart LR
Client -->|Bearer JWT| GraphQLResolver
GraphQLResolver --> Guard[GqlAuthGuard]
Guard --> Strategy[JwtStrategy.validate]
Strategy -->|user.sub| ResolverMethod
ResolverMethod --> DomainService
DomainService --> Mongo[(MongoDB)]
DomainService --> SyncService
SyncService --> Mongo
ResolverMethod --> Response
JWT_SECRET (dev-secret fallback in local/dev)HS256, RS256JwtStrategy.validate requires payload.sub; otherwise unauthorized@User() decorator extracts req.user and enforces authenticated principal@UseGuards(GqlAuthGuard)ownerId always derived from JWT subject)expectedVersionLIST_UPSERTLIST_DELETEmove operationcreate validates parent list ownership and auto-appends positionmove supports list transfer and position update in one mutationexpectedVersionITEM_UPSERTITEM_MOVEITEM_DELETEpullChanges(since) returns owner-specific chronological change log recordscreatedAt of latest returned change)pushChanges applies each operation independentlyaccepted flag becomes false if any conflict occursentityId and textual reasonexpectedVersion provided) throws conflict exceptionspushChanges optionally accepts idempotencyKey:
(ownerId, key)$setOnInsert)This guarantees replay safety for mobile retries/timeouts without duplicate effects.
sequenceDiagram
autonumber
participant C as Client
participant R as SyncResolver
participant S as SyncService
participant L as ListsService
participant I as ItemsService
C->>R: pushChanges(operations, idempotencyKey)
R->>S: resolveIdempotent(ownerId, key)
alt key exists
S-->>R: previous response
R-->>C: return cached response
else no record
loop each operation
R->>R: parse payload JSON
alt LIST:UPSERT
R->>L: create/update (with expectedVersion)
else ITEM:UPSERT
R->>I: create/update (with expectedVersion)
else ITEM:MOVE
R->>I: move (with expectedVersion)
else unsupported
R->>R: append conflict
end
end
R->>S: saveIdempotent(ownerId, key, response)
R-->>C: SyncPushResult
end