Skip to content

Commit

Permalink
perf: SubAndCalls in one query
Browse files Browse the repository at this point in the history
  • Loading branch information
amaury1093 committed Dec 12, 2023
1 parent 0c16b28 commit bac416e
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 68 deletions.
10 changes: 6 additions & 4 deletions src/pages/api/v0/check_email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ const POST = async (
return;
}

const { user, sentResponse } = await checkUserInDB(req, res);
const { userId, sentResponse } = await checkUserInDB(req, res);
if (sentResponse) {
return;
}

const d1 = performance.now() - startTime;
console.log(`[🐢] checkUserInDB: ${Math.round(d1)}ms`);

Expand Down Expand Up @@ -76,7 +77,7 @@ const POST = async (
.from<SupabaseCall>("calls")
.insert({
endpoint: "/v0/check_email",
user_id: user.id,
user_id: userId,
backend: output.debug?.server_name,
domain: output.syntax.domain,
verification_id: verificationId,
Expand All @@ -99,7 +100,7 @@ const POST = async (

// Cleanup
await Promise.all([
updateSendinblue(user).then(() => {
updateSendinblue(userId).then(() => {
const d8 = performance.now() - startTime;
console.log(
`[🐢] updateSendinblue: ${Math.round(d8)}ms`
Expand All @@ -116,9 +117,10 @@ const POST = async (
}),
]);

res.status(200).json(output);
const d9 = performance.now() - startTime;
console.log(`[🐢] Final response: ${Math.round(d9)}ms`);
res.status(200).json(output);
res.end();
}
},
{
Expand Down
39 changes: 29 additions & 10 deletions src/util/api.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { User } from "@supabase/supabase-js";
import Cors from "cors";
import { addMonths, differenceInMilliseconds, parseISO } from "date-fns";
import { NextApiRequest, NextApiResponse } from "next";
import { RateLimiterRes } from "rate-limiter-flexible";

import { subApiMaxCalls } from "./subs";
import { SupabaseSubscription, SupabaseUser } from "./supabaseClient";
import { getSubAndCalls, getUserByApiToken } from "./supabaseServer";
import { SupabaseSubscription } from "./supabaseClient";
import { supabaseAdmin } from "./supabaseServer";

// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
Expand Down Expand Up @@ -42,16 +41,19 @@ export const cors = initMiddleware(

type CheckUserReturnType =
| {
user?: undefined;
userId?: undefined;
subAndCalls?: undefined;
sentResponse: true;
}
| {
user: SupabaseUser;
userId: string;
subAndCalls: SubAndCalls;
sentResponse: false;
};

/**
* Checks the user's authorization token and retrieves user information.
* Also checks the user's subscription status and sets the rate limit headers.
*
* @param req - The NextApiRequest object.
* @param res - The NextApiResponse object.
Expand All @@ -68,13 +70,19 @@ export async function checkUserInDB(
throw new Error("Expected API token in the Authorization header.");
}

const user = await getUserByApiToken(token);
if (!user) {
res.status(401).json({ error: "User not found" });
const { data, error } = await supabaseAdmin
.from<SubAndCalls>("sub_and_calls")
.select("*")
.eq("api_token", token);
if (error) {
throw error;
}
if (!data?.length) {
res.status(401).json({ error: "Invalid API token." });
return { sentResponse: true };
}

const subAndCalls = await getSubAndCalls(user.id);
const subAndCalls = data[0];

// Set rate limit headers.
const now = new Date();
Expand Down Expand Up @@ -110,7 +118,18 @@ export async function checkUserInDB(
return { sentResponse: true };
}

return { user, sentResponse: false };
return { userId: subAndCalls.user_id, subAndCalls, sentResponse: false };
}

interface SubAndCalls {
user_id: string;
subscription_id: string | null;
product_id: string | null;
email: string;
current_period_start: string | Date;
current_period_end: string | Date;
number_of_calls: number;
api_token: string;
}

/**
Expand Down
31 changes: 23 additions & 8 deletions src/util/sendinblue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { format } from "date-fns";

import { sentryException } from "./sentry";
import { SupabaseUser } from "./supabaseClient";
import { supabaseAdmin } from "./supabaseServer";

export const sendinblueApi = new ContactsApi();

Expand All @@ -21,18 +22,32 @@ function sendinblueDateFormat(d: Date): string {
/**
* Update the LAST_API_CALL field on Sendinblue.
*/
export async function updateSendinblue(user: SupabaseUser): Promise<void> {
if (!user.sendinblue_contact_id) {
sentryException(
new Error(`User ${user.id} does not have a sendinblue_contact_id`)
);
return;
export async function updateSendinblue(
userId: string,
sendinblueContactId?: string
): Promise<void> {
let sibId = sendinblueContactId;
if (!sibId) {
const res = await supabaseAdmin
.from<SupabaseUser>("users")
.select("sendinblue_contact_id")
.eq("id", userId);
if (res.error) {
throw res.error;
}
if (!res.data?.length || !res.data[0].sendinblue_contact_id) {
throw new Error(
`User ${userId} does not have a sendinblue_contact_id.`
);
}

sibId = res.data[0].sendinblue_contact_id;
}

return sendinblueApi
.updateContact(user.sendinblue_contact_id, {
.updateContact(sibId, {
attributes: {
SUPABASE_UUID: user.id, // This should be set already, but we re-set it just in case.
SUPABASE_UUID: userId, // This should be set already, but we re-set it just in case.
LAST_API_CALL: sendinblueDateFormat(new Date()),
},
})
Expand Down
46 changes: 1 addition & 45 deletions src/util/supabaseServer.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { createClient, User } from "@supabase/supabase-js";

import {
getUsageStartDate,
SupabaseCall,
SupabaseSubscription,
SupabaseUser,
} from "./supabaseClient";
import { SupabaseSubscription } from "./supabaseClient";

export const supabaseAdmin = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL as string,
Expand All @@ -22,21 +17,6 @@ export const getUser = async (jwt: string): Promise<User | null> => {
return data;
};

export const getUserByApiToken = async (
apiToken: string
): Promise<SupabaseUser | null> => {
const { data, error } = await supabaseAdmin
.from<SupabaseUser>("users")
.select("*")
.eq("api_token", apiToken);

if (error) {
throw error;
}

return data?.[0];
};

export async function getActiveSubscription(
user: User
): Promise<SupabaseSubscription | null> {
Expand All @@ -51,27 +31,3 @@ export async function getActiveSubscription(

return data?.[0];
}

interface SubAndCalls {
user_id: string;
subscription_id: string | null;
product_id: string | null;
email: string;
current_period_start: string | Date;
current_period_end: string | Date;
number_of_calls: number;
}

export async function getSubAndCalls(userId: string): Promise<SubAndCalls> {
const { data, error } = await supabaseAdmin
.from<SubAndCalls>("sub_and_calls")
.select("*")
.eq("user_id", userId)
.single();
if (error) {
console.log("getSubAndCalls error", error);
throw error;
}

return data;
}
3 changes: 2 additions & 1 deletion supabase/migrations/20231212094633_performance.sql
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ AS
s.id AS subscription_id,
s.current_period_start,
s.current_period_end,
COUNT(c.id) AS number_of_calls,
pro.id as product_id,
COUNT(c.id) AS number_of_calls
u.api_token
FROM
users u
LEFT JOIN
Expand Down

1 comment on commit bac416e

@vercel
Copy link

@vercel vercel bot commented on bac416e Dec 12, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.