Upstash と Next.js を使用して SaaS 向けに信頼性の高いクォータ システムを構築する
この投稿では、Upstash を使用して、Next.js で作成された SaaS アプリケーション用のクォータ システムを作成する方法を説明します。 そしてプリズマ 。 Next.js API ルートを使用して、単純な API を作成します。
多くの SaaS アプリケーションでは、クォータ システムに遭遇したことがあるかもしれません。これは、ユーザーが一定時間内に実行できるアクションの数を制限するシステムです。
この例では、 ユーザーは毎月 1,000 件の API リクエストしか送信できません。 「無料」 計画。ユーザーが 1,000 を超える API リクエストを送信しようとすると、アプリケーションはユーザーによるそれ以上のリクエストの送信をブロックします。
これらの API リクエストは、スプレッドシートのコンテンツを取得し、それを JSON に変換するために使用されます。これが fastsheet の機能であり、Google スプレッドシートを JSON API に変換します。

データベース スキーマの定義
前に述べたように、データベースと対話するための ORM として Prisma を使用します。 User の例を次に示します。 クォータ システムを実装するモデルとその Spreadsheet スプレッドシートを保存するモデル :
model User {
id Int @id @default(autoincrement())
planId String @default("FREE")
email String @unique
quota Int @default(0)
spreadsheets Spreadsheet[]
}
model Spreadsheet {
id Int @id @default(autoincrement())
userId Int
user User @relation(fields: [userId], references: [id])
content String
}
planIdフィールドは、ユーザーが加入しているプランです。quotaフィールドは、ユーザーが当月に実行した API リクエストの数です。Spreadsheetモデルは、Userに接続されているスプレッドシートを表します。 .- A
User複数のSpreadsheetを含めることができます
にリンクされています。
物事を簡単にするために、"FREE" のみを処理します。 プランでは毎月 1,000 件の API リクエストが可能です。 .
ユーザーの割り当てを増やす
ユーザーが API リクエストを行うたびに、ユーザーの割り当てを増やしたいと考えています。
一見すると、これは簡単で簡単に実行できるように思えます。ユーザーが API リクエストを行うたびにユーザーの割り当てを 1 ずつ増やすだけです。 Next.js API ルート:
// pages/api/spreadsheets/[id]/index.ts
import type { NextApiRequest, NextApiResponse } from "next";
const MAX_FREE_TIER_QUOTA = 1000;
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
// Get the ID of the spreadsheet from the URL.
const { id } = req.params;
// Get the spreadsheet and its associated user from the database.
const spreadsheet = await prisma.spreadsheet.findUnique({
where: { id: Number(id) },
include: { user: true },
});
// Verify that the spreadsheet exists.
if (!spreadsheet) {
return res.status(404).json({ error: "Spreadsheet not found" });
}
// Verify that the user is not over the quota.
if (spreadsheet.user.quota >= MAX_FREE_TIER_QUOTA) {
return res.status(429).json({ error: "Quota exceeded" });
}
// Increase the user's quota by 1.
await prisma.user.update({
where: { id: spreadsheet.user.id },
data: { quota: { increment: 1 } },
});
// Return the spreadsheet's content.
return res.json({ content: spreadsheet.content });
} ただし、このアプローチには問題があり、必要な速度ではありません。
実際、すべての API リクエストでユーザーの割り当てを増やすデータベース トランザクションを作成しています。 .
データベースにアクセスしてユーザーのクォータを取得し、クォータを 1 ずつ増やすトランザクションを作成します。 をクリックして、スプレッドシートのコンテンツを送信します。
これは多くの理由から最適とは言えません。
- データベースが遅い可能性があり、応答時間が遅くなります。
- データベースがサーバーから遠く離れている可能性があります。これにより、応答時間が再び遅くなります。
- このコードをEdge 互換にするのはより困難です。 、世界中に複数のデータベース レプリカを作成する必要があります(簡単な作業ではありません!) .
Upstash を使用してユーザーの割り当てを増やす
この問題を解決するには、Upstash Redis® を使用してユーザーのクォータを増やします。 Upstash Redis® は高速で信頼性の高い Redis® データベースであり、サーバーレス環境向けにクラウドでホストされ、エッジ(ユーザーの近くでコードを実行する) に対応します。 .
INCR を使用します。 ユーザーのクォータを 1 ずつ増やす Redis® コマンド。このコマンドはアトミックです。つまり、実行されるのは 1 回だけであり、高速に実行されます。
以下は前と同じコードですが、Upstash Redis® を使用してユーザーのクォータを管理しています。
// pages/api/spreadsheets/[id]/index.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { Redis } from "@upstash/redis";
const MAX_FREE_TIER_QUOTA = 1000;
// Generate an Upstash Redis instance using environment variables.
// Make sure those are defined in your .env file.
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
// Get the ID of the spreadsheet from the URL.
const { id } = req.params;
// Get the spreadsheet and its associated user from the database.
const spreadsheet = await prisma.spreadsheet.findUnique({
where: { id: Number(id) },
include: { user: true },
});
// Verify that the spreadsheet exists.
if (!spreadsheet) {
return res.status(404).json({ error: "Spreadsheet not found" });
}
// Redis quota key, unique for each user by its ID.
const quotaKey = `user:${spreadsheet.user.id}:quota`;
// Retrieve the user's quota from Redis.
const quota = await redis.incr(quotaKey);
// If the key did not exist before, the returned value will be 1
// and we set the expiration to 1 day.
if (quota === 1) {
await redis.expire(quotaKey, 60 * 60 * 24);
}
// Verify that the user is not over the quota.
if (quota > MAX_FREE_TIER_QUOTA) {
return res.status(429).json({ error: "Quota exceeded" });
}
// Return the spreadsheet's content.
return res.json({ content: spreadsheet.content });
} Upstash Redis® を使用すると、Upstash Redis® Edge の機能を活用できるため、コードを簡単にスケーラブルにすることができます。 ユーザーの近くでコードを実行します。
結論
Upstash Redis® を使用すると、サーバーレス環境用のキャッシュ システムを非常に簡単に実装できます。
クォータ システムを実装するためにこれを使用しましたが、元のアイデアは、より多くのデータベース クエリをキャッシュしたり、API リクエストの結果をキャッシュしたりするなど、さらに多くのユースケースに適用できます。
キャッシュの最終目標は、API を高速化し、消費するリソース (Planetscale データベースの読み取り/書き込みクエリなど)を削減することです。 .
今後の記事では、Upstash QStash を使用して Redis® からユーザーのクォータを取得し、それをデータベースに保存して、ユーザーのクォータが常に最新であり、失われないようにする方法を説明します。
ライブで見てください
この種の最適化をライブで確認したい場合は、ファーストシートをチェックしてください。 、数回クリックするだけで Google スプレッドシートを API に変える API サービスです。十分な無料枠があり、無料で使用できます。
-
Redis LLEN –Redisデータストアでリストの長さを取得する方法
このチュートリアルでは、キーに格納されているリスト値の長さを取得する方法について学習します。このために、Redis LLENを使用します コマンド。 LLENコマンド このコマンドは、キーに格納されているリスト値の長さ(要素数)を返します。キーがredisデータストアに存在しない場合、そのキーは空のリストとして解釈され、0が返されます。 redis LLENコマンドの構文は次のとおりです:- 構文:- redis host:post> LLEN <key name> 出力:- (integer) value, representing the number of
-
リストの最後の要素を削除して返す方法– Redis RPOP | BRPOP
このチュートリアルでは、redisデータストアのキーに保存されているリスト値の最後の要素を削除して返す方法について学習します。このために、redis RPOPを使用します およびBRPOP コマンド。 RPOPコマンド このコマンドは、指定されたキーに格納されているリスト値の最後の要素を削除して返すために使用されます。キーが存在しない場合はNilが返され、キーは存在するがキーに格納されている値がリストデータ型ではない場合はエラーが返されます。 RedisRPOPコマンドの構文は次のとおりです。- 構文:- redis host:post> RPOP <keyname>