uploadthing-nextjs について
Next.js と TypeScript アプリで構造化されたファイルアップロードを実装するための UploadThing 統合スキルです。ファイルルート、ミドルウェアでの認可、アップロード後の処理、サーバー側 API を扱います。
主な機能
- 01ルートごとのファイル形式と制限設定
- 02アップロード前の認証と認可
- 03アップロード後の処理
- 04サーバー側でのアップロード・削除・一覧取得
- 05プライベートファイル用署名付き URL の生成
活用シーン
- 01権限確認付きアバターアップロードの実装
- 02添付ファイルごとのルール設定
- 03サーバージョブでのファイルのアップロードや移行
- 04プライベートファイルの期間限定リンク作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ flohhhhh/ skills --skill uploadthing-nextjsインストール後、Claude Code で/uploadthing-nextjsを入力して読み込まれたか確認します。
詳細
UploadThing の統合(Next.js)
UploadThing は、最新の TypeScript ウェブアプリ向けに設計された、型安全なストレージへの直接ファイルアップロードシステムを提供します。
使用する場面
アプリケーションで次のことが必要な場合は、UploadThing を使用してください。
- ユーザーによる安全なファイルアップロード
- 型安全なアップロードワークフロー
- 機能ごとのアップロード権限と検証
- React/Next.js に適したアップロードコンポーネント
- 独自のアップロードパイプラインを構築せずにストレージを抽象化すること
使用しない場面
次の場合は、使用を避けるか、再検討してください。
- ストレージに保存する前に、複雑な複数段階の処理パイプラインが必要な場合
- サーバー側でのストリーミングバイナリデータを完全に制御する必要がある場合
- データベースではなく、ストレージプロバイダーの API を主要なデータソースとして使用する必要があるアーキテクチャの場合
主な概念
File Router
アプリケーションのすべてのアップロードルートを定義します。
各ルートでは、次の項目を定義します。
- 許可するファイルタイプ
- ファイル数の上限とサイズのルール
- 認証チェック
- メタデータの処理
- アップロード後の処理
File Route
単一のアップロード用途を表します。
例:
- ユーザーのアバター画像のアップロード
- 車両掲載画像
- メッセージの添付ファイル
- ドキュメントのアップロード
Middleware
アップロード開始前に実行されます。
用途:
- 認証
- 認可
- メタデータのタグ付け
- 無効なアップロードの拒否
アップロード完了ハンドラー
ファイルのアップロードが成功した後に実行されます。
用途:
- データベースへのメタデータの保存
- ファイルとドメインエンティティの関連付け
- 非同期処理の開始
- 通知の送信
UTApi
サーバー側の UploadThing SDK です。
用途:
- プログラムからのファイルのアップロード
- ファイルの削除
- ファイルの一覧表示(管理者/デバッグ用途のみ)
- 非公開ファイルへのアクセスに使用する署名付き URL の生成
インストール
npm install uploadthing @uploadthing/react
環境変数
# .env
UPLOADTHING_SECRET=sk_live_...
UPLOADTHING_APP_ID=your_app_id
推奨ファイル構成
app/
api/
uploadthing/
core.ts # File router definition
route.ts # API route handlers
src/
utils/
uploadthing.ts # Typed components
components/
upload-button.tsx
.env # UploadThing credentials
File Router
場所: app/api/uploadthing/core.ts
import { createUploadthing, type FileRouter } from "uploadthing/next";
import { auth } from "@/lib/auth";
const f = createUploadthing();
export const uploadRouter = {
imageUploader: f({ image: { maxFileSize: "4MB", maxFileCount: 1 } })
.middleware(async ({ req }) => {
const user = await auth();
if (!user) throw new Error("Unauthorized");
return { userId: user.id };
})
.onUploadComplete(async ({ metadata, file }) => {
console.log("Upload complete for userId:", metadata.userId);
console.log("File URL:", file.url);
// Save to database here
return { uploadedBy: metadata.userId };
}),
} satisfies FileRouter;
export type OurFileRouter = typeof uploadRouter;
Route Handler
場所: app/api/uploadthing/route.ts
import { createRouteHandler } from "uploadthing/next";
import { uploadRouter } from "./core";
export const { GET, POST } = createRouteHandler({
router: uploadRouter,
});
型付きアップロードコンポーネント
場所: src/utils/uploadthing.ts
import {
generateUploadButton,
generateUploadDropzone,
} from "@uploadthing/react";
import type { OurFileRouter } from "@/app/api/uploadthing/core";
export const UploadButton = generateUploadButton<OurFileRouter>();
export const UploadDropzone = generateUploadDropzone<OurFileRouter>();
アップロードコンポーネントの使用
"use client";
import { UploadButton } from "@/utils/uploadthing";
export function MyUploadButton() {
return (
<UploadButton
endpoint="imageUploader"
onClientUploadComplete={(res) => {
console.log("Files: ", res);
alert("Upload Completed");
}}
onUploadError={(error: Error) => {
alert(`ERROR! ${error.message}`);
}}
/>
);
}
Tailwind の統合(任意)
import { withUt } from "uploadthing/tw";
export default withUt({
// your tailwind config
content: ["./src/**/*.{ts,tsx}"],
theme: { extend: {} },
plugins: [],
});
任意 — SSR アップロード設定の最適化
// next.config.js
import { withUT } from "uploadthing/nextjs";
export default withUT({
// your Next.js config
});
データベース統合パターン
アップロード完了後、アプリケーションでは次の処理を行います。
- UploadThing のファイル識別子を保存する
- 公開 URL または署名付きファイル URL を保存する
- 所有者のメタデータを保存する
- ファイルをドメインエンティティ(投稿、プロフィール、アセットなど)に関連付ける
.onUploadComplete(async ({ metadata, file }) => {
await db.insert(uploads).values({
userId: metadata.userId,
fileKey: file.key,
fileUrl: file.url,
fileName: file.name,
fileSize: file.size,
});
return { success: true };
})
アップロード戦略
クライアントからのアップロード(推奨)
手順:
- クライアントがアップロード認可を要求する
- サーバーが署名付きアップロード URL を返す
- クライアントがストレージに直接アップロードする
- アップロード完了ハンドラーがサーバー側の処理を実行する
サーバーからのアップロード(UTApi)
次のような場合に使用します。
- サーバージョブからファイルをアップロードする場合
- ファイルを移行する場合
- 管理者向けツールを扱う場合
- サーバーアクションを通じてファイルを受け付ける場合
import { UTApi } from "uploadthing/server";
const utapi = new UTApi();
export async function uploadFromServer(file: File) {
const response = await utapi.uploadFiles(file);
return response.data;
}
非公開ファイルへのアクセスパターン
非公開ファイルには、署名付き URL を介してアクセスしてください。
import { UTApi } from "uploadthing/server";
const utapi = new UTApi();
export async function getPrivateFileUrl(fileKey: string) {
const url = await utapi.getSignedURL(fileKey, { expiresIn: "1h" });
return url;
}
セキュリティに関するベストプラクティス
- アップロードミドルウェア内で必ず認証する
- 所有者情報とファイルのメタデータをデータベースに保存する
- 用途に応じた複数のアップロードルートを作成する
- 「あらゆるものをアップロード」できるグローバルルートを避ける
- アップロード完了時にドメインとの関連付けを検証する
運用に関するベストプラクティス
- ファイルキーとストレージ URL を記録する
- データベースへの書き込みを冪等にする
- ストレージプロバイダーに問い合わせるのではなく、メタデータをローカルに保存する
- ルートごとにファイルサイズとファイル数を制限する
- 公開用と非公開用のアップロードルートを分ける
よくある間違い
- 型付けされていない UploadThing コンポーネントを使用する
- クライアントコンポーネントのディレクティブを忘れる
- データベースへのメタデータ保存を省略する
- 許可範囲が広すぎるアップロードルートを許可する
- ストレージプロバイダーの API を主要なデータソースとして使用する
まとめ
UploadThing は、ファイルのアップロードとストレージを管理します。
Next.js は、ルーティングと統合のための機能を提供します。
アップロードルールは file router に記述します。認可はミドルウェアで行います。ファイルのメタデータはデータベースに保存します。