# Data flow — immich-app/immich

> Canonical page: https://codevetter.com/unpack/immich/data-flow

# Data flow — immich-app/immich

Scanned commit d6d094738366aaf4d2fcefdfe2702f50c72ba5b0 (2026-09-22). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Data enters through Express controllers guarded by a global AuthGuard that produces an AuthDto (user, session, shared link, or API key context) via AuthService.authenticate. Uploads stream through a custom multer storage engine into StorageCore-managed folders under IMMICH_MEDIA_LOCATION, then asset rows are written through repositories over Kysely and BullMQ jobs drive metadata extraction (exiftool), thumbnail generation (sharp), transcoding (ffmpeg), and ML inference via the FastAPI service. All cross-service coordination goes through a typed EventRepository (emit/onEvent) and websocket pushes to clients; mobile/web pull changes via the sync endpoints and socket.io.
- Every HTTP request passes the global AuthGuard: @Authenticated metadata (public/setup/admin/sharedLink/permission) is read via Reflector, then AuthService.authenticate resolves credentials from headers (Authorization bearer, x-api-key), cookies (ImmichCookie), or query params (shared-link key/slug) into request.user. — [server/src/middleware/auth.guard.ts#L99-123](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/middleware/auth.guard.ts#L99-L123) [server/src/services/auth.service.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/auth.service.ts) [server/src/app.module.ts#L53](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/app.module.ts#L53)
- Upload path: FileUploadInterceptor runs a custom multer storage (_handleFile/_removeFile) that writes assetData/sidecarData fields to a per-user upload folder computed by AssetMediaService.getUploadFolder (StorageCore.getNestedFolder(StorageFolder.Upload, userId, uuid)) with sanitized uuid filenames. — [server/src/middleware/file-upload.interceptor.ts#L38-80](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/middleware/file-upload.interceptor.ts#L38-L80) [server/src/services/asset-media.service.ts#L91-115](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/asset-media.service.ts#L91-L115) [server/src/cores/storage.core.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/cores/storage.core.ts)
- After a successful upload, AssetMediaService.uploadAsset creates the asset row (sha1 checksum column for dedupe), upserts sidecar/exif records, queues JobName.AssetExtractMetadata on the metadataExtraction queue, and emits the AssetCreate event; a Postgres checksum-constraint violation is converted into a DUPLICATE response. — [server/src/services/asset-media.service.ts#L125-227](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/asset-media.service.ts#L125-L227) [server/src/utils/database.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/utils/database.ts)
- Job transport is BullMQ: services call jobRepository.queue/queueAll (Redis), the microservices worker's per-queue bullmq Worker re-emits each job as a 'JobRun' event, and EventRepository dispatches to the method decorated with matching @OnJob({name, queue}). — [server/src/repositories/job.repository.ts#L90-140](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/job.repository.ts#L90-L140) [server/src/repositories/event.repository.ts#L62-71](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/event.repository.ts#L62-L71) [server/src/services/queue.service.ts#L78-91](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/queue.service.ts#L78-L91)
- Metadata extraction (MetadataService.handleMetadataExtraction) reads the asset, calls exiftool via media/metadata repositories, reverse-geocodes GPS through mapRepository when enabled, upserts exif/tags, and emits AssetMetadataExtracted which downstream services use to queue thumbnail/smart-search/face jobs. — [server/src/services/metadata.service.ts#L228-260](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/metadata.service.ts#L228-L260) [server/src/repositories/media.repository.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/media.repository.ts)
- ML inference flows server→HTTP: MachineLearningRepository builds typed pipeline requests (ClipVisualRequest, FacialRecognitionRequest, OcrRequest) and POSTs multipart to the FastAPI /predict endpoint, where get_entries parses the pipeline and ModelCache loads ONNX models. — [server/src/repositories/machine-learning.repository.ts#L40-100](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/machine-learning.repository.ts#L40-L100) [machine-learning/immich_ml/main.py#L132-212](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/machine-learning/immich_ml/main.py#L132-L212)
- Database access is repository-per-domain (asset.repository, user.repository, ~55 repositories in repositories/index.ts) over a shared KyselyModule connection; services never hold a Kysely handle directly and reach repos through BaseService. — [server/src/services/base.service.ts#L68-124](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/base.service.ts#L68-L124) [server/src/repositories/index.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/index.ts) [server/src/app.module.ts#L60](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/app.module.ts#L60)
- System configuration is a JSON document stored in Postgres (system_metadata/system_config), loaded through utils/config.ts getConfig/updateConfig, and broadcast via ConfigInit/ConfigValidate/ConfigUpdate events that services consume with @OnEvent (e.g., QueueService.updateConcurrency). — [server/src/utils/config.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/utils/config.ts) [server/src/services/queue.service.ts#L44-76](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/queue.service.ts#L44-L76) [server/src/repositories/event.repository.ts#L30-38](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/event.repository.ts#L30-L38)
- Realtime output uses socket.io: WebSocketAdapter attaches a @socket.io/redis-adapter pub/sub pair so API and microservices processes can both emit; WebsocketRepository.setAuthFn authenticates socket handshakes through AuthService.authenticate. — [server/src/middleware/websocket.adapter.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/middleware/websocket.adapter.ts) [server/src/app.module.ts#L87-93](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/app.module.ts#L87-L93)
- Storage layout is owned by StorageCore: StorageFolder-based paths (upload, library, thumbs/thumbnails, encoded-video, profile, backups) under IMMICH_MEDIA_LOCATION, with file moves tracked through move.repository so DB paths stay consistent. — [server/src/cores/storage.core.ts#L1-60](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/cores/storage.core.ts#L1-L60) [server/src/repositories/move.repository.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/move.repository.ts) [server/src/services/storage.service.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/storage.service.ts)
- API contract flows outward: Nest swagger decorators (@Endpoint, @Authenticated, DTO zod schemas) generate immich-openapi-specs.json, which oazapfts turns into packages/sdk fetch-client consumed by web (@immich/sdk), @immich/cli, e2e, and the mobile generated openapi package. — [server/src/bin/sync-open-api.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/bin/sync-open-api.ts) [mise.toml](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/mise.toml) [web/package.json](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/web/package.json) [open-api/immich-openapi-specs.json](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/open-api/immich-openapi-specs.json)
- Mobile state sync is pull-based delta sync: sync.controller exposes stream/checkpoint endpoints backed by SyncRepository + SyncCheckpointRepository so the Flutter app's drift sqlite database can incrementally reconcile server state. (inferred) — [server/src/controllers/sync.controller.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/controllers/sync.controller.ts) [server/src/repositories/sync.repository.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/sync.repository.ts) [server/src/repositories/sync-checkpoint.repository.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/sync-checkpoint.repository.ts)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
