by jax

Cal.diy is a fully MIT-licensed, self-hosted scheduling platform built on Next.js, tRPC, and Prisma. It supports calendar sync, embeds, payment integrations, audit trails, and a rich app store for developers and self-hosters.
This block is the full Cal.diy monorepo: a self-hosted, MIT-licensed scheduling platform built on Next.js, tRPC, Prisma, and NestJS. It includes a user-facing booking app, a REST API (v1 proxy + v2 NestJS), and a large shared package ecosystem. The typical buyer is a backend or full-stack engineer who wants to embed or extend Cal.diy's scheduling infrastructure inside their own product.
.changeset/ - Changesets config and pending release notes.claude/ - Claude AI assistant settings for the repo.github/ - CI workflows, issue templates, and GitHub Actions.opencode/ - OpenCode AI config.snaplet/ - Snaplet database seed/snapshot config.vscode/ - Workspace editor settings.well-known/ - Well-known static files.yarn/ - Yarn Berry plugin and release configagents/ - AI agent definitions and promptsapps/ - Deployable applications (web, API v1 proxy, API v2 NestJS, etc.)deploy/ - Deployment configuration and infrastructure scriptsdocs/ - Developer documentationexample-apps/ - Reference integration appspackages/ - Shared internal packages (UI, prisma, lib, trpc, etc.)biome.json - Biome linter/formatter configurationdocker-compose.yml - Local development container setuppackage.json - Root workspace manifestplaywright.config.ts - End-to-end test configurationturbo.json - Turborepo pipeline definitionvitest.workspace.ts - Vitest workspace configurationnpm install connect http-proxy-middleware dotenv @nestjs/common @nestjs/core @nestjs/config @nestjs/platform-express nest-winston winston qs
npm install --save-dev typescript @types/node @types/express
Native / build steps: This is a Yarn Berry (PnP or node-modules) monorepo. Use Yarn, not npm, for workspace linking:
corepack enable yarn installPostgreSQL 13+ must be running before Prisma migrations. Run (or ) inside after setting .
Spin up an isolated sandbox and run it server-side — no local setup.
Tetrees AI Review of this version
This Express backend / api completed archive review. Structure, dependency manifests, documentation, functional source, and common risk patterns were checked by the Tetrees verification pipeline; runtime phases are stated separately.
Deterministic AVCP artifact review
Pipeline avcp-2026-08-04.1 · SHA-256 7bed76b1bcf50ef6…
This version-scoped review deterministically inspects the submitted archive for structure, dependencies, documentation, functional source, and common malicious or high-risk signals. Build and test phases are reported as passed only after an isolated sandbox audition. It is not a guarantee of perfect security.
Reviewed Aug 4, 2026
Push this product straight into your AI IDE, web builder or cloud IDE.
Connect Tetrees to a compatible AI IDE, list products you own, and request the verified ZIP without exposing seller upload controls.
No reviews yet.
Sign in to join the discussion
Loading discussion…
yarn db:migratenpx prisma migrate deploypackages/prismaDATABASE_URLClone / copy source into your project root:
cp -r source/ ./calcom
Or set source/ as your project root if adopting the full monorepo.
Install dependencies from the workspace root:
corepack enable
yarn install
Configure environment variables. Copy .env.example to .env in apps/web and apps/api/v2:
cp apps/web/.env.example apps/web/.env
cp apps/api/v2/.env.example apps/api/v2/.env
Minimum required vars:
DATABASE_URL=postgresql://user:pass@localhost:5432/calcom
NEXTAUTH_SECRET=<random-32-char-string>
NEXTAUTH_URL=http://localhost:3000
NEXT_PUBLIC_WEBAPP_URL=http://localhost:3000
Run database migrations:
cd packages/prisma && npx prisma migrate deploy
Wire tsconfig paths if consuming packages directly in a TypeScript host project. Add to your tsconfig.json:
{
"compilerOptions": {
"paths": {
"@calcom/*": ["./calcom/packages/*/src"]
}
}
}
Start the platform locally:
yarn dev
http://localhost:3000http://localhost:3002http://localhost:3004import { sha256Hash } from "./apps/api/v2/src/lib/api-key";
function sha256Hash(token: string): string;
Hashes an API key token with SHA-256. Use this when storing or comparing API keys server-side — never store raw tokens. Input is any string; output is a lowercase hex digest.
import { isApiKey } from "./apps/api/v2/src/lib/api-key";
function isApiKey(authString: string, prefix: string): boolean;
Returns true if authString starts with the given prefix (defaults to "cal_"). Use this in auth middleware to distinguish Cal API keys from OAuth bearer tokens before attempting to hash and look up the key.
import { stripApiKey } from "./apps/api/v2/src/lib/api-key";
function stripApiKey(apiKey: string, prefix?: string): string;
Removes the "cal_" prefix (or a custom prefix) from an API key string. Use this to extract the raw token before hashing, so storage is consistent regardless of whether callers include the prefix.
// apps/api/v2/src/main.ts
class NestServer {
public static async getInstance(): Promise<Express>;
}
Singleton that boots the NestJS application once per container lifecycle, runs the bootstrap() configuration (pipes, interceptors, CORS), and returns the underlying Express instance. Use getInstance() to embed the NestJS API inside a Vercel serverless function or an existing Express server.
An Express gateway needs to authenticate requests using Cal API keys before proxying to downstream services.
import express, { Request, Response, NextFunction } from "express";
import { isApiKey, stripApiKey, sha256Hash } from "./calcom/apps/api/v2/src/lib/api-key";
const app = express();
async function apiKeyAuth(req: Request, res: Response, next: NextFunction) {
const authHeader = req.headers.authorization ?? "";
const token = authHeader.replace(/^Bearer\s+/, "");
if (!isApiKey(token, "cal_")) {
return res.status(401).json({ error: "Not a Cal API key" });
}
const rawToken = stripApiKey(token, "cal_");
const hashed = sha256Hash(rawToken);
// Compare hashed against your DB record
// const stored = await db.apiKey.findUnique({ where: { hashedKey: hashed } });
// if (!stored) return res.status(403).json({ error: "Invalid key" });
console.log("Hashed key for lookup:", hashed);
next();
}
app.use("/api", apiKeyAuth, (req, res) => {
res.json({ message: "Authenticated" });
});
app.listen(4000);
Mount the Cal NestJS app as a Vercel handler without running a separate process.
// api/calcom.ts (Vercel edge/serverless file)
import type { VercelRequest, VercelResponse } from "@vercel/node";
// NestServer is the singleton exported from main.ts logic
// Replicate its pattern directly:
import "dotenv/config";
import { NestFactory } from "@nestjs/core";
import type { NestExpressApplication } from "@nestjs/platform-express";
import type { Express } from "express";
import { AppModule } from "../calcom/apps/api/v2/src/app.module";
import { bootstrap } from "../calcom/apps/api/v2/src/bootstrap";
let server: Express;
async function getServer(): Promise<Express> {
if (!server) {
const app = await NestFactory.create<NestExpressApplication>(AppModule, { bufferLogs: true });
bootstrap(app);
await app.init();
server = app.getHttpAdapter().getInstance();
}
return server;
}
export default async function handler(req: VercelRequest, res: VercelResponse) {
const expressApp = await getServer();
expressApp(req as any, res as any);
}
Spin up the connect-based proxy that routes /v2 to NestJS and everything else to the legacy v1 API.
// proxy.js
const http = require("node:http");
const connect = require("connect");
const { createProxyMiddleware } = require("http-proxy-middleware");
const apiProxyV1 = createProxyMiddleware({ target: "http://localhost:3003" });
const apiProxyV2 = createProxyMiddleware({ target: "http://localhost:3004" });
const app = connect();
app.use("/v2", apiProxyV2); // must be registered before the catch-all
app.use("/", apiProxyV1);
http.createServer(app).listen(3002, () => {
console.log("API proxy listening on :3002");
});
Run with node proxy.js. Matches the logic in apps/api/index.js exactly.
apps/ - Contains all deployable apps: web (Next.js booking UI), api (connect proxy + NestJS v2 REST API).apps/api/index.js - Lightweight connect server that proxies /v2 to NestJS on :3004 and everything else to the v1 handler on :3003.apps/api/v2/src/main.ts - NestJS application entry point; defines the NestServer singleton and local-dev startup path.apps/api/v2/src/lib/api-key/index.ts - Pure utility functions for API key hashing, prefix detection, and stripping.apps/api/v2/src/modules/auth/guards/or-guard/index.ts - Re-exports the Or guard for composing multiple NestJS auth guards.apps/api/v2/src/platform/event-types/event-types_2024_06_14/transformed/index.ts - Re-exports event-type transformation helpers for the 2024-06-14 API version.packages/ - Shared workspace packages: Prisma schema, UI components, tRPC routers, config helpers, i18n.docker-compose.yml - Defines Postgres and the web/api services for local container-based development.turbo.json - Turborepo task graph; defines build, dev, lint, test pipelines with caching.biome.json - Biome linter/formatter rules applied across the monorepo in lieu of ESLint + Prettier.npm install instead of yarn install breaks Yarn Berry workspace resolution. Fix: corepack enable && yarn install.DATABASE_URL: Prisma throws a cryptic engine error at startup. Fix: ensure DATABASE_URL is set in every app's .env before running migrations or starting the server.NEXTAUTH_SECRET not set in production: NextAuth silently falls back to an insecure value. Fix: always set a strong random value; generate with openssl rand -hex 32.bootstrap not called before app.init(): CORS, global pipes, and validation decorators will be absent. Fix: always call bootstrap(app) before await app.init() as shown in main.ts.connect and http-proxy-middleware: Using import syntax in the proxy file causes a runtime error because connect is CJS. Fix: keep apps/api/index.js as a .js CommonJS file or add "type": "commonjs" to that package's package.json.turbo run build --force or clear .turbo/ after any schema.prisma edit.I have a copy of the Cal.diy open-source scheduling monorepo (upstream package: user@example.com)
in the `source/` directory of this project. I also have a USAGE.md that documents the real exports
and file layout.
Please help me integrate Cal.diy into my existing Node.js / TypeScript project by doing the following
steps one at a time, confirming with me after each:
1. Read USAGE.md and source/apps/api/v2/src/lib/api-key/index.ts to understand the API key utilities.
2. Add the required npm dependencies listed in USAGE.md to my package.json.
3. Create an Express middleware file that uses `isApiKey`, `stripApiKey`, and `sha256Hash` from
source/apps/api/v2/src/lib/api-key/index.ts to authenticate requests.
4. Wire the NestJS API v2 (source/apps/api/v2/src/main.ts bootstrap pattern) into my existing
Express app as a sub-router under /calcom.
5. Set up the required environment variables (DATABASE_URL, NEXTAUTH_SECRET, NEXTAUTH_URL) in my
.env file.
6. Confirm the proxy routing logic from source/apps/api/index.js and replicate it if I need
separate v1/v2 routing.
Use only the real exports visible in USAGE.md. Do not invent function names or module paths.
Cal.diy is released under the MIT License (see source/LICENSE). It is a community fork of Cal.com with enterprise features removed. Upstream repository: https://github.com/calcom/cal.diy. Upstream npm package identifier: user@example.com.
This product’s unique contribution is clean room implementation of valuable code block into AVCP compatible standard.
The full install guide and integration prompts unlock after purchase.
CRM, ERP, Admin & Internal Tools
$7