Open-Source Wikis

/

Cal.com

/

How to contribute

/

Patterns and conventions

calcom/cal.com

Patterns and conventions

Cal.diy has a thick layer of conventions captured in AGENTS.md and the agents/rules/ directory. This page collects the ones a new contributor needs to internalize.

Imports

  • No barrel imports. Import directly from source files: import { Button } from "@calcom/ui/components/button", not import { Button } from "@calcom/ui". This is enforced by code review and ESLint config.
  • Type-only imports. import type { X } from "..." — keeps emitted JavaScript clean.
  • apps/api/v2 re-exports. Modules from @calcom/features and @calcom/trpc cannot be imported directly into apps/api/v2 because that app's tsconfig.json lacks the path mappings. Re-export them from packages/platform/libraries/index.ts and import from @calcom/platform-libraries. This is documented at length in AGENTS.md.

Repository / Service split

Every domain in packages/features/<domain>/ follows the same shape:

<domain>/
├── repositories/        # Prisma queries only — never logic
├── services/ or service/ # Business logic, calls repositories
├── di/                  # Wires concrete implementations to interfaces
├── lib/                 # Domain helpers
└── components/          # UI (if applicable)

Repository rules:

  • Use select, never include. The team treats include as a security smell because it leaks every column.
  • Never expose credential.key. The encrypted-credentials column is highlighted in AGENTS.md Don'ts.
  • Repositories take a PrismaClient (or transaction) in their constructor.

Service rules:

  • Throw ErrorWithCode (packages/lib/errorCodes.ts) — not Error, not TRPCError.
  • One service typically owns one transactional unit (e.g., RegularBookingService orchestrates the whole booking creation).

tRPC and REST routers/controllers stay thin: they validate input, call a service, and return the result.

Errors

Layer Class to throw File
tRPC handler TRPCError @trpc/server
Service / repository / utility ErrorWithCode packages/lib/errorCodes.ts
HTTP (legacy pages/api) Throw, then catch + map packages/lib/http-error.ts
API v2 NestJS Standard NestJS exceptions or domain-specific filters apps/api/v2/src/filters/

Concrete error codes live in packages/lib/errorCodes.ts (e.g., NoAvailableUsersFound, BookingConflict). Adding a new error means: add the code, throw ErrorWithCode, add a translated user-facing string in packages/i18n/locales/en/common.json.

Type safety

  • Never as any. Use as unknown as Foo only when the cast is provably safe and document why.
  • Never re-declare types. Prefer types generated by Prisma (@prisma/client) and Zod. The repo wires zod-prisma-types for this purpose.
  • Strict tsconfig. tsconfig.json extends packages/tsconfig presets. strict: true is the rule; relaxed configs require justification.

Database conventions

  • Always use select in Prisma queries.
  • Wrap multi-write paths in transactions (prisma.$transaction). Booking creation is a canonical example.
  • Long-running operations should be queued via the tasker (packages/features/tasker) instead of run inline.
  • The Credential.key column is encrypted with CALENDSO_ENCRYPTION_KEY. Never select it into anything that could be returned to a client.

React conventions

  • App Router pages live in apps/web/app/. Permission checks belong in page.tsx, never layout.tsx (AGENTS.md).
  • Pages that wrap the booker use the (booking-page-wrapper) route group; authenticated dashboards use (use-page-wrapper).
  • Tailwind for styling. Component primitives come from packages/ui/components/....
  • Always use sentence case in headings.
  • Translations: every UI string should resolve via useLocale() against packages/i18n/locales/en/common.json.

Comments and code style

AGENTS.md is explicit:

Only add code comments that explain why, not what. Never add comments that simply restate what the code does.

The agents/rules/quality-code-comments.md file expands on this.

Generated files

The following must never be hand-edited:

  • packages/app-store/apps.metadata.generated.ts and friends (regenerate with yarn app-store:build)
  • packages/prisma/generated/** (regenerate with yarn prisma generate)
  • packages/prisma/zod/**
  • packages/kysely/types.ts
  • apps/api/docs/openapi.json (generated by Swagger)

Time and date

  • The codebase historically used Day.js everywhere. Newer code prefers date-fns or native Date when timezone awareness isn't needed (AGENTS.md).
  • The pre-configured Day.js bundle lives in packages/dayjs. Importing Day.js plugins ad hoc has bitten the team enough times that this single bundle is canonical.

Feature flags

packages/features/flags provides a database-backed feature-flag service. To gate a code path:

import { FeaturesRepository } from '@calcom/features/flags/features.repository';

const flags = new FeaturesRepository(prisma);
if (await flags.checkIfFeatureIsEnabledGlobally('my-feature')) {
  /* ... */
}

Don't ship hardcoded if (process.env.ENABLE_X) checks for product-facing toggles.

  • Prefer ast-grep if installed.
  • Else rg (ripgrep).
  • Avoid grep and find — they are slower and miss .gitignore rules.

PR hygiene

  • Keep diffs <500 lines / <10 code files.
  • Open as a draft.
  • Conventional Commits in the title.
  • No secrets, ever.

When you find yourself fighting these, you usually need to split the PR.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Patterns and conventions – Cal.com wiki | Factory