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のAPIルートでCSVファイルをHTTPリクエストする方法

Posted at

はじめに

APIルートでCSVファイルを送る必要があり、実装していたところ、APIルートでCSVが文字列で受け取ったり実装に苦労したため、備忘録として残します。

環境

csvファイルをパースするために、multipartyを使用します。

"next": "13.2.4",
"multiparty": "^4.2.3",

コード

HTTPリクエストにaxiosを使用していますが、何でも良いです。

filePost.ts
import axios from 'axios'

export const filePost = async (params: FormData) => {
  await axios.post('/api/post', params, {
    headers: {
      ContentType: 'multipart/form-data',
    },
  });
};
src/pages/api/post.ts
import axios from 'axios';

import { createFormDataFromMultipartyParsedFiles } from '@/utils/createFormDataFromMultipartyParsedFiles';
import { parseNextApiRequest } from '@/utils/parseNextApiRequest';

import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  if (req.method === 'POST') {
    (async () => {
      try {
        // リクエストで受けとったCSVファイルをパースする
        const formParsedData = await parseNextApiRequest(req);

        if ('error' in formParsedData) {
          return;
        }

        // パースしたデータから、formDataを生成
        const formData = createFormDataFromMultipartyParsedFiles(
          formParsedData.files,
        );

        // ファイルを送信
        await axios.post(<URL>, formData);

        return res.status(200).json(response.data);
      } catch (error: any) {
        return res.status(500).json(error.response.data);
      }
    })();
  }
}

// APIルートで受け取ったリクエストをパースしないようにする
export const config = {
  api: {
    bodyParser: false,
  },
};
src/utils/createFormDataFromMultipartyParsedFiles.ts
import fs from 'fs';

import type multiparty from 'multiparty';

type MultipartyParsedValues = {
  fields: Record<string, string[] | undefined>;
  files: Record<string, multiparty.File[] | undefined>;
};

export const createFormDataFromMultipartyParsedFiles = (
  files: MultipartyParsedValues['files'],
) => {
  const formData = new FormData();

  Object.values(files).forEach((files) => {
    if (!files) {
      return;
    }

    const file = files[0];

    // ファイルを読み込んで、ファイルオブジェクトを生成
    const fileContent = fs.readFileSync(file.path);
    const blob = new Blob([fileContent], {
      type: file.headers['content-type'],
    });
    const processedFile = new File([blob], file.originalFilename);

    formData.append(file.fieldName, processedFile);
  });

  return formData;
};
src/utils/parseNextApiRequest.ts
import multiparty from 'multiparty';

import type { NextApiRequest } from 'next';

type MultipartyParsedError = {
  error: Error | null;
};

type MultipartyParsedValues = {
  fields: Record<string, string[] | undefined>;
  files: Record<string, multiparty.File[] | undefined>;
};

export const parseNextApiRequest = async (req: NextApiRequest) => {
  const form = new multiparty.Form();

  const formParsedData = await new Promise<
    MultipartyParsedError | MultipartyParsedValues
  >((resolve, reject) => {
    form.parse(req, (error, fields, files) => {
      if (error) {
        reject({ error });
      }

      resolve({ fields, files });
    });
  });

  return formParsedData;
};

まとめ

APIルートで受け取ったリクエストをformDataに戻すのに苦労しました。外部モジュールの必要や処理が増えるため、中間APIが必要でなければ直接リクエストするのが良いかなと思います。

参考記事

最後に

GoQSystemでは一緒に働いてくれる仲間を募集中です!

ご興味がある方は以下リンクよりご確認ください。

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?