0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Next.js】 Route Handlersでファイルアップロードしたい

Last updated at Posted at 2025-04-05

はじめに

  • Next.js App Routerの機能である、Route Handlersと標準のfetch APIを使ってファイル送信を実装した
  • 調査に苦労したので備忘録としてまとめた
  • Next.jsのバージョンは15.2.4。App Routerを使う
    ※これは、Pages Routerの時はAPI Routesと呼ばれていた機能に当たる

やりたいこと

  • フロントエンドをNext.js、バックエンドをC#で実装し、ファイルの送信を実装する
  • ユーザー入力部分からNext.jsのRoute Handlersに向かってリクエストを投げる
    → Route HandlersからC#に向けて再度リクエストを投げる、という流れ
  • ブラウザ標準のfetch APIを使用する
  • 狙い:Route Handlersを中間APIとして使用することで、セキュリティ強化したい

実装方法

  • フロントエンドのリクエスト先をRoute Handlersにする

  • nextUrlの引数にNext.jsの中間APIを叩くURLを渡すイメージ

  • Jsonを送信する場合はContent-typeを指定する

    page.tsx
    export async function uploadFile(
      nextUrl: string,
      fileList: FileList | null
    ): Promise<Response> {
      if (!fileList || fileList.length === 0) {
        return Response.error();
      }
    
      const formData = new FormData();
      for (const file of fileList) {
        formData.append("upfiles", file, file.name);
      }
    
      // formDataを送信するときはContent-typeを指定しない
      const config: RequestInit = {
        method: "POST",
        body: formData,
      };
    
      return await fetch(nextUrl, config);;
    }
    
  • Route Handlersにバックエンドへfetchするように記述する

    • このとき、FormDataで送りたい場合はnew Headers()する(ここがポイント)
    • そうではない場合、フロントエンドのリクエストのHeadersを流用する(ここもポイント)
    • フロントエンド用のリクエストボディを作成するときにCookie等の情報を足す
    src/api/backend/[...backendUrl]/route.ts
    export async function POST(request: NextRequest): Promise<Response> {
        const isMultiPartFormData = request.headers.get("content-type")?.includes("multipart/form-data") ?? false;
        const bodyForBackEnd = isMultiPartFormData ? await request.formData() : await request.text();
        // ファイル送信では新たにHeaderを作成し、その他ではそのまま流用する
        const headersForBackEnd = isMultiPartFormData ? new Headers() : request.headers;
    
        // ここでバックエンドに向けたURLを作成
        const targetUrl = getTargetUrl(request.url);
        const url = new URL(targetUrl, baseUrl).href;
        console.debug("headersForBackend:", headersForBackEnd)
    
        const config : RequestInit= {
            method: "POST",
            headers: headersForBackEnd,
            body: bodyForBackEnd,
            credentials : 'include' as RequestCredentials,
        }
    
        // バックエンドに向けてfetchする
        const backendResult = await fetch(url, config);
    
        // クライアント表示用のResponseをnewして返却
        // ここで足したい情報があれば足す
        const resForClient = new Response(backendResult.body)
        return resForClient;
    }
    

備考

  • Headersをnewする/しない、newするとしたらrequest.headersを渡すか渡さないかによって、UND_ERR_REQ_CONTENT_LENGTH_MISMATCHが発生する
  • どうもUndici側のエラーのようで、以前はバージョンアップで解決していた様子
  • 今回は最新バージョンのNext.jsを使用しているため、解決方法がわからず、色々試してこうなった

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?