Introduction

Cal.com is structured as a monorepo using Turborepo. Cal.com is built on NextJS with Typescript, PostgreSQL database and the interactions with the database are carried out using Prisma. We use TRPC for end-to-end typesafe procedure calls to Prisma and utilize Zod for validation and type-safe parsing of API calls.

Setting Up the Development Environment

Prerequisites

Here is what you need to be able to run Cal.com.

  • Node.js (Version: >=18.x)
  • PostgreSQL
  • Yarn (recommended)

Cal.com can be set up and run on your machine locally. Please take a look at this quick guide for instructions on setting it up.

Understanding the Project Structure

Cal.com makes use of Turborepo for a monorepo structure. This allows us to manage multiple code projects (like libraries, apps, services) and share code and resources effectively.

You can look at turbo.json to see how we use it. In simple terms,

  • Each entry in the pipeline section corresponds to a different part of the project. Understanding these will help in navigating the codebase.
  • Knowing which tasks depend on others is crucial for understanding the build process.
  • The env entries under each task highlight the need for specific configuration settings, which you will need to set up in your development environment.
  • You should focus on the tasks related to the area you are contributing to. For example, if working on the Prisma schema, look at tasks prefixed with @calcom/prisma
  • The various packages can be found in the /packages folder from the root.
  • The various pages and components for the web can be found in the /apps/web folder from the root.
  • The public API related codebase can be found in the /apps/api folder from the root.
  • The Cal.ai related codebase can be found in the /apps/ai folder from the root

How we use TRPC at cal.com

Cal.com makes use of the TRPC calls for CRUD operations on the database by utilizing the useQuery() & useMutation() hooks. These are essentially wrappers around @tanstack/react-query and you can learn more about them here, respectively: queries, mutations.

Here’s an example usage of useQuery at cal.com:

  const { data: webhooks } = trpc.viewer.webhook.list.useQuery(undefined, {
    suspense: true,
    enabled: session.status === "authenticated",
  });

Here’s an example usage of useMutation at cal.com:

  const createWebhookMutation = trpc.viewer.webhook.create.useMutation({
    async onSuccess() {
      showToast(t("webhook_created_successfully"), "success");
      await utils.viewer.webhook.list.invalidate();
      router.back();
    },
    onError(error) {
      showToast(`${error.message}`, "error");
    },
  });

Both the examples are taken from packages/features/webhooks/pages/webhook-new-view.tsx file. We use the useQuery hook for fetching data from the database. On the contrary, the useMutation hook is used for Creating/Updating/Deleting data from the database.

The path prior to the useQuery or useMutation hook represents the actual path of the procedure: in this case, packages/trpc/server/routers/viewer/webhook. You will find create.handler.ts in case of

trpc.viewer.webhook.create.useMutation()

& list.handler.ts in case of

trpc.viewer.webhook.list.useQuery()

in the path mentioned above. There is usually a schema file in the same directory for each handler as well (name.schema.ts). This is generally how we make use of the TRPC calls in the cal.com repo.

The Cal.com API V1

Our API V1 uses zod validations to keep us typesafe and give us extensive parsing control and error handling. These validations reside in /apps/api/lib/validations. We also have helper and utility functions which are used primarily within our API endpoints. Here’s an example usage of our zod validation in action with the respective API endpoint:

export const schemaDestinationCalendarBaseBodyParams = DestinationCalendar.pick({
  integration: true,
  externalId: true,
  eventTypeId: true,
  bookingId: true,
  userId: true,
}).partial();

const schemaDestinationCalendarCreateParams = z
  .object({
    integration: z.string(),
    externalId: z.string(),
    eventTypeId: z.number().optional(),
    bookingId: z.number().optional(),
    userId: z.number().optional(),
  })
  .strict();


export const schemaDestinationCalendarCreateBodyParams = schemaDestinationCalendarBaseBodyParams.merge(
  schemaDestinationCalendarCreateParams
);

And here’s the usage of this validation for parsing the request at the respective endpoint:

async function postHandler(req: NextApiRequest) {
  const { userId, isAdmin, prisma, body } = req;
  const parsedBody = schemaDestinationCalendarCreateBodyParams.parse(body);

This is the general approach of validation and parsing in our API submodule.

Building Apps for Cal.com

We’ve got an app store containing apps which complement the scheduling platform. From video conferencing apps such as Zoom, Google Meet, etc. to Calendar Apps such as Google Calendar, Apple Calendar, CalDAV, etc. and even payment, automation, analytics and other miscellaneous apps, it is an amazing space to improve the scheduling experience.

We have a CLI script in place that makes building apps a breeze. You can learn more about it here.

Running Tests

E2E-Testing

Be sure to set the environment variable NEXTAUTH_URL to the correct value. If you are running locally, as the documentation within .env.example mentions, the value should be http://localhost:3000.

In a terminal just run:

yarn test-e2e

To open the last HTML report run:

`yarn playwright show-report test-results/reports/playwright-html-report`

Resolving issues

E2E test browsers not installed

If you face the following error when running yarn test-e2e:

Executable doesn’t exist at /Users/alice/Library/Caches/ms-playwright/chromium-1048/chrome-mac/Chromium.app

You can resolve it by running:

npx playwright install

to download test browsers.

Contribution and Collaboration Guides