mcpField
U
uploadthing-nextjs

uploadthing-nextjs

github.com/flohhhhh開発249 回インストール32 週間前に更新
リポジトリ

uploadthing-nextjs について

Next.js と TypeScript アプリで構造化されたファイルアップロードを実装するための UploadThing 統合スキルです。ファイルルート、ミドルウェアでの認可、アップロード後の処理、サーバー側 API を扱います。

主な機能

  1. 01ルートごとのファイル形式と制限設定
  2. 02アップロード前の認証と認可
  3. 03アップロード後の処理
  4. 04サーバー側でのアップロード・削除・一覧取得
  5. 05プライベートファイル用署名付き URL の生成

活用シーン

  1. 01権限確認付きアバターアップロードの実装
  2. 02添付ファイルごとのルール設定
  3. 03サーバージョブでのファイルのアップロードや移行
  4. 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
});

データベース統合パターン

アップロード完了後、アプリケーションでは次の処理を行います。

  1. UploadThing のファイル識別子を保存する
  2. 公開 URL または署名付きファイル URL を保存する
  3. 所有者のメタデータを保存する
  4. ファイルをドメインエンティティ(投稿、プロフィール、アセットなど)に関連付ける
.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 };
})

アップロード戦略

クライアントからのアップロード(推奨)

手順:

  1. クライアントがアップロード認可を要求する
  2. サーバーが署名付きアップロード URL を返す
  3. クライアントがストレージに直接アップロードする
  4. アップロード完了ハンドラーがサーバー側の処理を実行する

サーバーからのアップロード(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 に記述します。認可はミドルウェアで行います。ファイルのメタデータはデータベースに保存します。


References

コメント