Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: split up routers to separate lambdas #8041

Merged
merged 37 commits into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
25fafc0
split up routers to separate lambdas
juliusmarminge Mar 31, 2023
6b719a2
fix responsemeta
juliusmarminge Mar 31, 2023
4ec8c9a
move
juliusmarminge Mar 31, 2023
70a34f7
add typeguards to make sure all endpoints are covered in the approuter
juliusmarminge Mar 31, 2023
0a993e5
prettier
juliusmarminge Mar 31, 2023
ed78cb2
move slotsrouter
juliusmarminge Mar 31, 2023
0dddf3c
split ssg/ssr
juliusmarminge Mar 31, 2023
6a4df55
make sure correct headers are sent on viewer.public
juliusmarminge Mar 31, 2023
13ed9f4
make sure correct headers are sent + use ctx.prisma
juliusmarminge Mar 31, 2023
b02b169
Merge branch 'main' into split-router
roae Apr 1, 2023
ae25067
Merge branch 'main' into split-router
emrysal Apr 4, 2023
fee7d26
Merged with main
emrysal Apr 5, 2023
176f5c8
Merge branch 'main' into split-router
emrysal Apr 5, 2023
775973c
Fixed new prefetch broken by merge
emrysal Apr 5, 2023
042729c
Merge branch 'main' into split-router
PeerRich Apr 7, 2023
c9b6a6d
Merge branch 'main' into split-router
keithwillcode Apr 11, 2023
201f978
Merge branch 'main' into pr/8041
zomars Apr 12, 2023
af5c7c5
Merge branch 'main' into pr/8041
zomars Apr 12, 2023
b12329d
Merge remote-tracking branch 'calcom/main' into split-router
keithwillcode May 1, 2023
3177ca4
Fixes after merge
keithwillcode May 1, 2023
7e2c348
Created separate API route for all tRPC routers
keithwillcode Apr 26, 2023
5188527
More fixes from refactor
keithwillcode May 1, 2023
970f6f1
Fixed tRPC query for slots
keithwillcode May 1, 2023
21db6cc
Put back extra line
keithwillcode May 1, 2023
2232d26
Fixed type checks
keithwillcode May 1, 2023
112f5fb
Removed Endpoint type check since it loads from client
keithwillcode May 1, 2023
d298846
Reverted change in getSchedule test
keithwillcode May 1, 2023
8befee5
Merge branch 'main' into split-router
keithwillcode May 2, 2023
eb3858e
Merge branch 'main' into split-router
keithwillcode May 2, 2023
656ce94
Merge branch 'main' into split-router
keithwillcode May 2, 2023
2d39cd9
Merge branch 'main' into split-router
keithwillcode May 2, 2023
d7fa176
Merge branch 'main' into split-router
keithwillcode May 2, 2023
e7b6109
Fix trpc routes in expectations
hariombalhara May 3, 2023
f8fcc12
Fix one more route test
hariombalhara May 3, 2023
4d1e31d
Merge branch 'main' into split-router
keithwillcode May 3, 2023
0fbba6f
Merge branch 'main' into split-router
keithwillcode May 3, 2023
86a3a7e
Merge branch 'main' into split-router
keithwillcode May 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 0 additions & 83 deletions apps/web/pages/api/trpc/[trpc].ts

This file was deleted.

4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/apiKeys/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { apiKeysRouter } from "@calcom/trpc/server/routers/viewer/apiKeys/_router";

export default createNextApiHandler(apiKeysRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/appRoutingForms/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import appRoutingForms from "@calcom/app-store/routing-forms/trpc-router";
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";

export default createNextApiHandler(appRoutingForms);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/apps/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { appsRouter } from "@calcom/trpc/server/routers/viewer/apps/_router";

export default createNextApiHandler(appsRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/auth/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { authRouter } from "@calcom/trpc/server/routers/viewer/auth/_router";

export default createNextApiHandler(authRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/availability/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { availabilityRouter } from "@calcom/trpc/server/routers/viewer/availability/_router";

export default createNextApiHandler(availabilityRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/bookings/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { bookingsRouter } from "@calcom/trpc/server/routers/viewer/bookings/_router";

export default createNextApiHandler(bookingsRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/deploymentSetup/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { deploymentSetupRouter } from "@calcom/trpc/server/routers/viewer/deploymentSetup/_router";

export default createNextApiHandler(deploymentSetupRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/eth/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import ethRouter from "@calcom/app-store/rainbow/trpc/router";
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";

export default createNextApiHandler(ethRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/eventTypes/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { eventTypesRouter } from "@calcom/trpc/server/routers/viewer/eventTypes/_router";

export default createNextApiHandler(eventTypesRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/features/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { featureFlagRouter } from "@calcom/features/flags/server/router";
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";

export default createNextApiHandler(featureFlagRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/insights/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { insightsRouter } from "@calcom/features/insights/server/trpc-router";
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";

export default createNextApiHandler(insightsRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/payments/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { paymentsRouter } from "@calcom/trpc/server/routers/viewer/payments/_router";

export default createNextApiHandler(paymentsRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/public/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { publicViewerRouter } from "@calcom/trpc/server/routers/publicViewer/_router";

export default createNextApiHandler(publicViewerRouter, true);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/saml/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { ssoRouter } from "@calcom/trpc/server/routers/viewer/sso/_router";

export default createNextApiHandler(ssoRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/slots/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { slotsRouter } from "@calcom/trpc/server/routers/viewer/slots/_router";

export default createNextApiHandler(slotsRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/teams/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { viewerTeamsRouter } from "@calcom/trpc/server/routers/viewer/teams/_router";

export default createNextApiHandler(viewerTeamsRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/users/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { userAdminRouter } from "@calcom/features/ee/users/server/trpc-router";
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";

export default createNextApiHandler(userAdminRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/viewer/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { loggedInViewerRouter } from "@calcom/trpc/server/routers/loggedInViewer/_router";

export default createNextApiHandler(loggedInViewerRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/webhook/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { webhookRouter } from "@calcom/trpc/server/routers/viewer/webhook/_router";

export default createNextApiHandler(webhookRouter);
4 changes: 4 additions & 0 deletions apps/web/pages/api/trpc/workflows/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { workflowsRouter } from "@calcom/trpc/server/routers/viewer/workflows/_router";

export default createNextApiHandler(workflowsRouter);
2 changes: 1 addition & 1 deletion apps/web/playwright/availability.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ test.describe("Availablity tests", () => {
});

await test.step("Date override is displayed in troubleshooter", async () => {
const response = await page.waitForResponse("**/api/trpc/viewer.availability.schedule.update?batch=1");
const response = await page.waitForResponse("**/api/trpc/availability/schedule.update?batch=1");
const json = await response.json();
// @ts-expect-error trust me bro
const date = json[0].result.data.json.schedule.availability.find((a) => !!a.date);
Expand Down
2 changes: 1 addition & 1 deletion apps/web/playwright/booking-pages.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ testBothBookers.describe("pro user", () => {
await page.goto("/bookings/unconfirmed");
await Promise.all([
page.click('[data-testid="confirm"]'),
page.waitForResponse((response) => response.url().includes("/api/trpc/viewer.bookings.confirm")),
page.waitForResponse((response) => response.url().includes("/api/trpc/bookings/confirm")),
]);
// This is the only booking in there that needed confirmation and now it should be empty screen
await expect(page.locator('[data-testid="empty-screen"]')).toBeVisible();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ test.describe("Routing Forms", () => {

await page.goto(`/apps/routing-forms/reporting/${routingForm.id}`);
// Can't keep waiting forever. So, added a timeout of 5000ms
await page.waitForResponse((response) => response.url().includes("viewer.appRoutingForms.report"), {
await page.waitForResponse((response) => response.url().includes("appRoutingForms/report"), {
timeout: 5000,
});
const headerEls = page.locator("[data-testid='reporting-header'] th");
Expand Down
76 changes: 66 additions & 10 deletions packages/trpc/react/trpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,58 @@ import type { TRPCClientErrorLike } from "../react";
import type { inferRouterInputs, inferRouterOutputs, Maybe } from "../server";
import type { AppRouter } from "../server/routers/_app";

/**
* We deploy our tRPC router on multiple lambdas to keep number of imports as small as possible
* TODO: Make this dynamic based on folders in trpc server?
*/
Comment on lines +15 to +18
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to make sure this is deployed on different lambdas maybe we should have a slightly different configuration on each path as discussed in slack.

Here an example, this changes must be added to the vercel.json

cc @emrysal

b803c61

imagen

const ENDPOINTS = [
"apiKeys",
"appRoutingForms",
"apps",
"auth",
"availability",
"bookings",
"deploymentSetup",
"eth",
"eventTypes",
"features",
"insights",
"payments",
"public",
"saml",
"slots",
"teams",
"users",
"viewer",
"webhook",
"workflows",
] as const;
export type Endpoint = (typeof ENDPOINTS)[number];

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const resolveEndpoint = (links: any) => {
// TODO: Update our trpc routes so they are more clear.
// This function parses paths like the following and maps them
// to the correct API endpoints.
// - viewer.me - 2 segment paths like this are for logged in requests
// - viewer.public.i18n - 3 segments paths can be public or authed
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (ctx: any) => {
const parts = ctx.op.path.split(".");
let endpoint;
let path = '';
if (parts.length == 2) {
endpoint = parts[0] as keyof typeof links;
path = parts[1];
} else {
endpoint = parts[1] as keyof typeof links;
path = parts.splice(2, parts.length - 2).join('.');
}

return links[endpoint]({ ...ctx, op: { ...ctx.op, path } });
};
};

/**
* A set of strongly-typed React hooks from your `AppRouter` type signature with `createTRPCReact`.
* @link https://trpc.io/docs/v10/react#2-create-trpc-hooks
Expand Down Expand Up @@ -41,17 +93,21 @@ export const trpc = createTRPCNext<AppRouter, NextPageContext, "ExperimentalSusp
}),
splitLink({
// check for context property `skipBatch`
condition: (op) => {
return op.context.skipBatch === true;
},
condition: (op) => !!op.context.skipBatch,
// when condition is true, use normal request
true: httpLink({ url }),
// when condition is false, use batching
false: httpBatchLink({
url,
/** @link https://github.com/trpc/trpc/issues/2008 */
// maxBatchSize: 7
}),
true: (runtime) => {
const links = Object.fromEntries(
ENDPOINTS.map((endpoint) => [endpoint, httpLink({ url: url + "/" + endpoint })(runtime)])
);
return resolveEndpoint(links);
},
// when condition is false, use batch request
false: (runtime) => {
const links = Object.fromEntries(
ENDPOINTS.map((endpoint) => [endpoint, httpBatchLink({ url: url + "/" + endpoint })(runtime)])
);
return resolveEndpoint(links);
},
}),
],
/**
Expand Down
74 changes: 74 additions & 0 deletions packages/trpc/server/createNextApiHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { z } from "zod";
import type { AnyRouter } from "@trpc/server";
import * as trpcNext from "@calcom/trpc/server/adapters/next";
import { createContext as createTrpcContext } from "@calcom/trpc/server/createContext";

/**
* Creates an API handler executed by Next.js.
*/
export function createNextApiHandler(router: AnyRouter, isPublic: boolean = false) {
return trpcNext.createNextApiHandler({
router,
/**
* @link https://trpc.io/docs/context
*/
createContext: ({ req, res }) => {
return createTrpcContext({ req, res });
},
/**
* @link https://trpc.io/docs/error-handling
*/
onError({ error }) {
if (error.code === "INTERNAL_SERVER_ERROR") {
// send to bug reporting
console.error("Something went wrong", error);
}
},
/**
* Enable query batching
*/
batching: {
enabled: true,
},
/**
* @link https://trpc.io/docs/caching#api-response-caching
*/
responseMeta({ ctx, paths, type, errors }) {
const allOk = errors.length === 0;
const isQuery = type === "query";
const noHeaders = {};

// We cannot set headers on SSG queries
if (!ctx?.res) return noHeaders;

const defaultHeaders: Record<"headers", Record<string, string>> = {
headers: {},
};

const timezone = z.string().safeParse(ctx.req?.headers["x-vercel-ip-timezone"]);
if (timezone.success) defaultHeaders.headers["x-cal-timezone"] = timezone.data;

// We need all these conditions to be true to set cache headers
if (!(isPublic && allOk && isQuery)) return defaultHeaders;

// No cache by default
defaultHeaders.headers["cache-control"] = `no-cache`;

if (isPublic && paths) {
const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
const cacheRules = {
"session": `no-cache`,
"i18n": `no-cache`,
// Revalidation time here should be 1 second, per https://github.com/calcom/cal.com/pull/6823#issuecomment-1423215321
"slots.getSchedule": `no-cache`, // FIXME
"cityTimezones": `max-age=${ONE_DAY_IN_SECONDS}, stale-while-revalidate`,
} as const;

const matchedPath = paths.find((v) => v in cacheRules) as keyof typeof cacheRules;
if (matchedPath) defaultHeaders.headers["cache-control"] = cacheRules[matchedPath];
}

return defaultHeaders;
},
});
};
Loading