Redis と NextAuth で Next.js ブログに安全なコメント機能を実装する方法
このチュートリアルでは、ブログにコメントセクションを実装する方法を解説します。使用する技術スタックは以下の通りです。
- Next.js 13(App ディレクトリ構成)
- NextAuth(認証)
- Upstash Redis(コメントの保存)
- SWR(コメントのキャッシュと再検証)
それでは始めましょう。
NextAuth で認証を処理する
まず、誰でも自由にコメントを投稿できる状態は避けなければなりません。スクリプトを使ってスパムコメントを大量投稿されてしまう恐れがあるからです。コメント投稿を許可する前に、まず認証システムを構築しましょう。ここでは NextAuth を使用します。
プロジェクトに next-auth をインストールします。
pnpm install next-auth
App ディレクトリ内のディレクトリ構成は以下のようになります。
.
├── app
│ ├── api
│ │ └── auth
│ │ └── [...nextauth]
│ │ └── route.ts
│ ├── blog
│ │ ├── page.tsx
│ │ └── [...slug]
│ │ └── page.tsx
│ ├── components
│ │ └── LoginButton.tsx
│ ├── favicon.ico
│ ├── globals.css
│ └── layout.tsx
認証用の API ルートをセットアップします。
// app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth";
import GithubProvider from "next-auth/providers/github";
const handler = NextAuth({
providers: [
GithubProvider({
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
}),
],
callbacks: {
async session({ session, token }) {
if (session && session.user && token.sub) {
session.user.sub = token.sub;
}
return session;
},
},
});
export { handler as GET, handler as POST };
次に、GitHub OAuth アプリケーションを作成し、GITHUB_CLIENT_ID と GITHUB_CLIENT_SECRET を取得します。

NextAuth を使えば、任意のクライアントコンポーネントから next-auth が提供する signIn および signOut 関数でサインイン・サインアウトが行えます。ただしその前に、アプリケーション全体をラップする Context Provider をセットアップしておく必要があります。
// app/layout.tsx
"use client";
import "./globals.css";
import { SessionProvider } from "next-auth/react";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<SessionProvider>
<body>{children}</body>
</SessionProvider>
</html>
);
}
SessionProvider を導入すると、アプリケーション内のどのクライアントコンポーネントからでもセッション情報へアクセスできるようになります。
セッションには、next-auth の useSession フックを使ってアクセスできます。ログインボタンの実装例は以下の通りです。
"use client";
import { signIn, signOut, useSession } from "next-auth/react";
export default function LoginButton() {
const { data: session } = useSession();
if (session) {
return (
<div>
{session.user?.name} さんとしてログイン中({session.user?.email}) <br />
<button onClick={() => signOut()}>Sign out</button>
</div>
);
}
return (
<div>
ログインしていません <br />
<button onClick={() => signIn()}> Sign in </button>
</div>
);
}
これで認証システムの準備が整いました。続いて、Redis データベースにコメントを保存するためのサーバーサイドの API ルートを作成していきます。
Redis データベースのセットアップ
- Upstash にアクセスして Redis データベースを作成します。
- ユーザーから近いリージョンを選択し、TLS 暗号化を有効にしておきましょう。
@upstash/redisパッケージをインストールします。
pnpm install @upstash/redis

- 発行されたトークンを
.env.localにコピーします。
それでは、以下のディレクトリ構成で API エンドポイントを記述していきましょう。
.
├── app
│ ├── api
│ │ ├── auth
│ │ │ └── [...nextauth]
│ │ │ └── route.ts
│ │ ├── comment
│ │ │ ├── delete
│ │ │ │ └── route.ts
│ │ │ ├── get
│ │ │ │ └── route.ts
│ │ │ └── post
│ │ │ └── route.ts
│ │ └── lib
│ │ ├── getUser.ts
│ │ └── redis.ts
- Redis クライアントのインスタンスを作成します。
// app/api/lib/redis.ts
import { Redis } from "@upstash/redis";
/*
process.env を通じて、環境変数から
UPSTASH_REDIS_REST_URL と UPSTASH_REDIS_REST_TOKEN を読み込みます
*/
const redis = Redis.fromEnv();
export default redis;
これだけで完了です。Redis クライアントのセットアップが済んだので、あとはアプリケーションに API ルートを組み込むだけです。
コメントの保存には Redis リストを使用します。リストはスタックとして動作するため、最新のコメントを常に先頭に表示できます。もちろんソート処理をクライアント側で実装することも可能ですが、Redis がすでにこのデータ構造を提供しているのであれば、それを活用しない手はありません。
コード全体はこちらから確認できます。要点は以下の通りです。
-
コメントの作成:
redis.lpush(referer, comment)。キーがrefererのリストにコメントをプッシュします。 -
全コメントの取得:
redis.lrange(referer, 0, -1) -
コメントの削除:
redis.lrem(referer, 0, comment)。キーがrefererのリスト内に存在する該当コメントをすべて削除します。
これで API の準備が整いました。次はフロントエンドとバックエンドの統合に取り掛かりましょう。
クライアントサイドのセットアップ
それほど難しくありません。作成したエンドポイントに対して fetch でリクエストを送るだけです。コメントのキャッシュと再検証には swr というライブラリを使用しますが、React Query などの他のライブラリを使ってもまったく問題ありません。
リクエストを送信するための onSubmit() ハンドラーと onDelete() ハンドラーを持つ useComment() カスタムフックを作成します。
"use client";
import { useState } from "react";
import type { Comment } from "@/app/interfaces/interfaces";
import useSWR from "swr";
const fetcher = (url: string) => fetch(url).then((res) => res.json());
const useComment = () => {
const [text, setText] = useState("");
const { data: comments, mutate } = useSWR<Comment[]>(
"/api/comment/get",
fetcher,
{
fallbackData: [],
},
);
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
await fetch("/api/comment/post", {
method: "POST",
body: JSON.stringify({ text }),
headers: {
"Content-Type": "application/json",
},
});
setText("");
await mutate();
} catch (error) {
console.log(error);
}
};
const onDelete = async (comment: Comment) => {
try {
await fetch("/api/comment/delete", {
method: "POST",
body: JSON.stringify({ comment }),
headers: {
"Content-Type": "application/json",
},
});
await mutate();
} catch (error) {
console.log(error);
}
};
return { text, setText, comments, onSubmit, onDelete };
};
export default useComment;
これでコメントセクションは完成です。コメントの取得・投稿・削除がすべて行えるようになりました。次のステップとしては、コメント入力ボックスやコメント一覧を表示する UI コンポーネントを作成することになります。完全なサンプルコードは以下をご覧ください。
-
Redisのハッシュテーブルスキャン徹底解説――内部メカニズムをソースコードで読み解く
執筆:Ehud Tamir ソフトウェア開発者にとって大きな課題の一つが「他人のコードを読むこと」です。本記事では、私がこれまで知らなかった興味深いC言語のコードを読み解き、その内容を皆さんに紹介します。取り上げるのはRedisデータベースの一部として実装されているコードです。 Redisはキーバリュー型データベースであり、データベース内のすべてのエントリはキーから値へのマッピングになっています。値には整数、リスト、ハッシュテーブルなど複数の型があります。そして実は、データベース本体も内部的にはハッシュテーブルで構成されています。この記事では、RedisのSCANコマンドについて掘り下げていき
-
UpstashとNext.jsで実現するSaaS向けの信頼性の高いクォータシステム構築ガイド
この記事では、Next.jsとPrismaで構築されたSaaSアプリケーション向けに、Upstashを活用したクォータシステムの実装方法を解説します。APIの作成にはNext.jsのAPIルートを使用します。 クォータシステムとは 多くのSaaSアプリケーションでは、クォータシステムが採用されています。これは、一定期間内にユーザーが実行できるアクションの回数を制限する仕組みです。 本記事の例では、「Free」プランのユーザーは月に1,000リクエストまでAPIを利用できます。1,000件を超えるAPIリクエストを送信しようとすると、アプリケーションはそれ以上のリクエストをブロックします。 これ