# Extension points — immich-app/immich

> Canonical page: https://codevetter.com/unpack/immich/extension-points

# Extension points — immich-app/immich

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

The codebase is organized around explicit registries: controllers/index.ts, services/index.ts, repositories/index.ts, commands/index.ts, plus decorator-driven registration (@OnJob, @OnEvent, @Endpoint, @Authenticated). Adding an endpoint means a controller method + zod DTO + index registration; adding a job means enum entries + @OnJob handler (enforced at startup). A WASM plugin system via @immich/plugin-sdk and workflows provide runtime extensibility, and the OpenAPI→SDK codegen pipeline propagates API changes to all clients.
- New HTTP endpoints plug in via a controller class added to the controllers array in controllers/index.ts; the ApiModule mounts every entry under the global /api prefix. — [server/src/controllers/index.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/controllers/index.ts) [server/src/app.module.ts#L105-110](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/app.module.ts#L105-L110)
- New background jobs require: a JobName enum member, a QueueName choice, and a method decorated @OnJob({name, queue}) on a service — jobRepository.setup validates complete coverage at boot and throws ImmichStartupError for missing or duplicate handlers. — [server/src/repositories/job.repository.ts#L40-88](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/job.repository.ts#L40-L88) [server/src/decorators.ts#L153-157](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/decorators.ts#L153-L157) [server/src/enum.ts#L850-909](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/enum.ts#L850-L909)
- New domain events plug in via the typed EventMap in event.repository.ts plus @OnEvent({name, priority, server, workers}) on service methods; BootstrapEventPriority documents ordering constraints for bootstrap handlers. — [server/src/repositories/event.repository.ts#L24-108](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/event.repository.ts#L24-L108) [server/src/decorators.ts#L142-151](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/decorators.ts#L142-L151) [server/src/enum.ts#L801-813](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/enum.ts#L801-L813)
- New services extend BaseService (which injects all ~55 repositories) and must be added to services/index.ts so queueService.setServices and eventRepository.setup can discover their decorated methods. — [server/src/services/base.service.ts#L68-198](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/base.service.ts#L68-L198) [server/src/services/index.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/index.ts) [server/src/app.module.ts#L85-95](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/app.module.ts#L85-L95)
- New repositories are classes added to repositories/index.ts and BASE_SERVICE_DEPENDENCIES so every service receives them positionally through BaseService's constructor. — [server/src/repositories/index.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/index.ts) [server/src/services/base.service.ts#L68-124](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/base.service.ts#L68-L124)
- WASM plugins are a first-class extension point: authors build against @immich/plugin-sdk (host-functions.ts, sdk.ts), plugins install under IMMICH_PLUGINS_INSTALL_FOLDER, are pooled/executed by PluginRepository via extism, and their methods/templates surface through plugin.controller and workflow compatibility checks. — [packages/plugin-sdk/src/sdk.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/packages/plugin-sdk/src/sdk.ts) [server/src/repositories/plugin.repository.ts#L208-261](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/plugin.repository.ts#L208-L261) [server/src/services/plugin.service.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/plugin.service.ts) [server/src/dtos/env.dto.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/dtos/env.dto.ts)
- Admin CLI commands plug in via nest-commander Command classes registered in commands/index.ts (commandsAndQuestions) and run under ImmichAdminModule when invoked as 'immich-admin'. — [server/src/commands/index.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/commands/index.ts) [server/src/main.ts#L171-180](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/main.ts#L171-L180) [server/src/app.module.ts#L153-163](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/app.module.ts#L153-L163)
- API versioning/documentation is decorator-driven: @Endpoint({summary, history: new HistoryBuilder().added('vX').beta('vY')}) emits custom swagger extensions (x-immich-history/state) that feed the generated spec — deprecations flow automatically via ApiTag.Deprecated. — [server/src/decorators.ts#L159-270](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/decorators.ts#L159-L270) [server/src/controllers/asset-media.controller.ts#L71-75](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/controllers/asset-media.controller.ts#L71-L75)
- Request/response validation is nestjs-zod: DTOs are zod schemas (e.g., EnvSchema, QueueNameSchema) validated globally by ZodValidationPipe and serialized by ZodSerializerInterceptor registered as APP_PIPE/APP_INTERCEPTOR. — [server/src/app.module.ts#L45-53](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/app.module.ts#L45-L53) [server/src/dtos/env.dto.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/dtos/env.dto.ts) [server/src/enum.ts#L837](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/enum.ts#L837)
- DB schema changes plug in through sql-tools: table classes in server/src/schema/tables with Column/trigger decorators (PrimaryGeneratedUuidV7Column, UpdatedAtTrigger), functions in schema/functions.ts, then 'pnpm migrations:generate' diffs into schema/migrations with an ORDER file. — [server/src/decorators.ts#L10-26](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/decorators.ts#L10-L26) [server/package.json#L30-38](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/package.json#L30-L38) [server/src/schema/migrations](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/schema/migrations) [docs/docs/developer/database-migrations.md](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/docs/docs/developer/database-migrations.md)
- Client surface extends via codegen: edit DTOs/controllers → run sync-open-api.ts to refresh immich-openapi-specs.json → mise task open-api-typescript regenerates packages/sdk fetch-client used by web/cli/e2e; mobile consumes a generated dart 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) [open-api/immich-openapi-specs.json](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/open-api/immich-openapi-specs.json) [mobile/pubspec.yaml](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/mobile/pubspec.yaml)
- Web features plug in as SvelteKit routes under web/src/routes/(user)/... with shared components/stores/managers in web/src/lib, all typed against @immich/sdk. — [web/src/routes/(user)/photos](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/web/src/routes/(user)/photos) [web/src/lib/index.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/web/src/lib/index.ts) [web/package.json](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/web/package.json)
- Bull queues self-register: ConfigRepository builds RegisterQueueOptions for every QueueName value (bull.queues) so adding a QueueName enum member automatically creates the queue without extra wiring. (inferred) — [server/src/repositories/config.repository.ts#L52-55](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/config.repository.ts#L52-L55) [server/src/app.module.ts#L64](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/app.module.ts#L64) [server/src/enum.ts#L815-835](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/enum.ts#L815-L835)

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)
