Home · Apps · rl-main-infra · todo_api · todo_mobile
GraphQL API exposed by NestJS (@nestjs/apollo) with auto-generated schema (autoSchemaFile: true).
/graphql)Authorization: Bearer <jwt>@UseGuards(GqlAuthGuard) applied at resolver class level (lists/items/sync)sub; email and name are optional| Query | Args | Returns | Notes |
|---|---|---|---|
todoLists |
none | [TodoList!]! |
Owner-scoped by JWT sub, sorted by order, then createdAt |
todoItems |
listId?: String |
[TodoItem!]! |
Owner-scoped; optional list filter; sorted by listId, position, createdAt |
pullChanges |
since?: String |
PullChangesResult! |
Incremental sync stream based on createdAt cursor |
| Mutation | Args | Returns | Behavior |
|---|---|---|---|
createTodoList |
input: CreateListInput! |
TodoList! |
Creates list under authenticated owner |
updateTodoList |
input: UpdateListInput! |
TodoList! |
Optional optimistic concurrency via expectedVersion |
deleteTodoList |
id: String! |
Boolean! |
Deletes owner list, records sync tombstone |
createTodoItem |
input: CreateItemInput! |
TodoItem! |
Validates parent list ownership, appends position |
updateTodoItem |
input: UpdateItemInput! |
TodoItem! |
Partial update with optional version check |
moveTodoItem |
input: MoveItemInput! |
TodoItem! |
Cross-list/in-list move with new position + version bump |
deleteTodoItem |
id: String! |
Boolean! |
Deletes owner item, records sync tombstone |
pushChanges |
operations: [SyncOperationInput!]!, idempotencyKey?: String |
SyncPushResult! |
Batched sync apply; per-op conflict capture; idempotent replay |
TodoListid: ID!ownerId: String!title: String!description: Stringorder: number (default 0)version: number (default 1)createdAt: Date!updatedAt: Date!TodoItemid: ID!ownerId: String!listId: String!title: String!description: Stringcompleted: Boolean!labels: [String!]!dueDate: Datereminders: [Date!]!position: numberversion: numbercreatedAt: Date!updatedAt: Date!SyncOperationInput: { op, entityId, entityType, payload, expectedVersion? }SyncPushResult: { accepted, conflicts[] }PullChangesResult: { cursor, changes[] } where changes are serialized JSON stringsCreateListInput.title: string, max 120 charsCreateListInput.description: optional, max 512 charsUpdateListInput.order: optional int >= 0UpdateListInput.expectedVersion: optional int >= 1CreateItemInput.title: max 160 charsCreateItemInput.description: optional, max 1000 charsCreateItemInput.dueDate: optional ISO date stringUpdateItemInput.completed: optional booleanUpdateItemInput.expectedVersion: optional int >= 1MoveItemInput.toPosition: int >= 0pullChanges(since)createdAt > since (if provided)createdAtsince or current timestamppushChanges(operations, idempotencyKey)idempotencyKey exists and has prior response for same owner, returns saved response immediatelyLIST:UPSERT → create or update listITEM:UPSERT → create or update itemITEM:MOVE → move itemconflicts; successful operations still commitaccepted = true only when conflicts.length === 0sequenceDiagram
autonumber
participant C as Client
participant G as GraphQL Resolver
participant S as Domain Service
participant M as MongoDB
participant Y as SyncService
C->>G: Mutation + Bearer JWT
G->>G: GqlAuthGuard + User decorator
G->>S: create/update/move/delete
S->>M: Persist list/item document
S->>Y: record(ownerId, type, entityId, payload)
Y->>M: Insert change_log entry
G-->>C: Updated entity / Boolean / SyncPushResult