Nextjs

Next.jsでローカルで動くAPIをルートハンドラーで作る【Next.jsのAPPルーターを使おう】

Next.jsのpagesルーターでは、APIルートが準備されている。pagesフォルダの配下にapiというフォルダを作り、その中にAPIを作ることができた。

Routing: API Routes | Next.js
Next.js supports API Routes, which allow you to build your API without leaving your Next.js app. Learn how it works here.

Appルーターでは少し作り方が変わっている。

以下はAppルーターのルートハンドラーについての公式ページ。

Routing: Route Handlers | Next.js
Create custom request handlers for a given route using the Web's Request and Response APIs.
File Conventions: route.js | Next.js
API reference for the route.js special file.

Appルーターでの設定方法と、簡単なテスト用のAPIをメモしておく。

あくまでテスト用なので、本番で使うことは想定していない。

Next.js Appルーターのroute.ts

作り方はすごく簡単で、フォルダ内にroute.tsを作るだけ。

例えばtest-apiフォルダにroute.tsを作ると、.com/test-apiにアクセスすることでAPIとして使える。

以下はとても簡単なテスト用のAPI。

アクセスすると、ランダムな数字をjson形式で返す。

//test-api/route.ts

import { NextResponse } from "next/server";

export async function GET() {
  console.log(">>> Called /test-api ");
  return NextResponse.json({ data: Math.random() });
}

上の例のMath.random()のところに、返したいjsonの内容をべた書き(変数でもよい)すれば、いろんなデータを返すことができる。

応用としてはtsファイルをインポートして、json形式で返したり、ローカルファイルを開いてデータを返したり、とかができる。

以下にローカルファイルを開く場合の例を記載する。

本番環境ではそんなことはしないだろうが、ローカルテストを想定し、テスト用のjsonファイルを準備しておいてそれを呼び出す形で作る。

開発中のサイトからfetchを使って、ローカルホストでnext devしているAPIを呼べばテストできる。

jsonファイルを開いて返すAPI

テスト用のデータを返すAPI。

今回はjsonを使っているが、少し修正すればcsvなど他のファイルでも対応できる。


import { NextResponse } from "next/server";

import fs from "fs";
import path from "path";

export async function GET() {
  const postsDirectory = path.join(process.cwd(), "test");
  const fullPath = path.join(postsDirectory, `test-data.json`);
  const fileContents = fs.readFileSync(fullPath, "utf-8");
  const data = JSON.parse(fileContents);

  console.log(`>>> Called test-data api);

  return NextResponse.json(data);
}

node.jsのファイル読み込みを使ってjsonファイルを読み込み、それをjson形式にして返すだけのAPI。

まずファイルがある場所のパスを取得するため、実行時のカレントディレクトリをprocess.cwd()で取得し、その配下のtestフォルダを指定するためpathを連結している。

postsDirectoryで作ったパスにファイル名をくっつけて、readFileSyncでファイルを読み込む。

fs.readFileSyncは、同期処理になる。要はファイルを開き終わってから後続の処理をスタートするということ。

あとは開いたファイルをjson形式にして、それを返すだけ。

多分本番のサーバーでこんな使い方はしないだろうけど、ローカルでテスト用に使うにはとても便利。

次はDynamic Routesを使ったAPIの例。

Dynamic Routesを使ってjsonを返す

やっていることは変わらないが、まず[]で囲ったフォルダを作り、名前を[id]としておく。

例えばtest-dynamic/aでアクセスがあった場合、idはaになる。

このaを取得してあれこれするのがDynamic Routesを使ったAPI。

idに対応するファイル名のjsonを用意しておいて、ファイル名を生成するときに、取得したidを使う。

ちなみに存在しないidの場合、ステータスコードは500、「このページは動作していません」とテキストが出る。本番で使うことは想定していないので、対応する処理は入れてない。

// test-dynamic/[id]/route.ts
import { NextResponse } from "next/server";

import fs from "fs";
import path from "path";

export async function GET(
  request: Request,
  { params }: { params: { id: string } },
) {{
  const postsDirectory = path.join(process.cwd(), "test");
  const id = params.id;
  const fullPath = path.join(postsDirectory, `${id}.json`);
  const fileContents = fs.readFileSync(fullPath, "utf-8");
  const data = JSON.parse(fileContents);

  console.log(`>>> Called test-data api ${id}`);

  return NextResponse.json(data);
}

開発環境とvercel本番環境の違い

next devで動く開発サーバーは、静的で作っていてもSSR的に動く。

そのため、vercelなど本番環境に上げる場合、ビルドした時にデータはキャッシュされる。

例えば、該当のurlにアクセスしたときの時間を返すAPIを静的にビルドしてvercelに上げると、ビルドしたときの時間を永遠に返すAPIになる。

そういう理由から、SSR的な挙動をさせたいAPIをvercelに上げてテストに使いたい、なんて時は注意が必要になる。

これの対処方法は、ファイルの中に、キャッシュしないでねと自分で記述すること。

Next.jsでは明示的に示さない限り、ビルドすると静的なサイトというか、データは全てキャッシュされる。

キャッシュしたくないところは、自身で明示する必要がある。

それが以下のexport const dynamic = "force-dynamic";のところ。


import ja from "date-fns/locale/ja";
import { format } from "date-fns";

import { NextResponse } from "next/server";

export const dynamic = "force-dynamic";

export async function GET() {
  const now = new Date(
    Date.now() + (new Date().getTimezoneOffset() + 9 * 60) * 60 * 1000,
  );

  const nowJa = format(now, "yyyy年M月d日(E) HH:mm:ss", { locale: ja });

  console.log(">>> Called api アクセスした日付を返すAPI");
  console.log(nowJa);
  return NextResponse.json({ data: nowJa });
}

dynamicの値を変えることで、キャッシュの動きを制御できる。

何も指定しない場合は基本的には全てキャッシュされる(一部例外あり)。

上の例のように、force-dynamicにすると、このAPIはキャッシュされずに、アクセスがあると再検証が走る。pageルーターのgetServerSidePropsと同じ動きになる。

詳細は以下で。

File Conventions: Route Segment Config | Next.js
Learn about how to configure options for Next.js route segments.

next devで使う分には問題ないし、普通はこれをvercelに上げるなんてことはないと思うけど。

vercel自体の動きをテスト確認したいとか、特殊な状況でテスト用のAPIを本番でも使いたいという時に、キャッシュの指定をしないと動かないよ、というだけの話。

まとめ

本番のAPIがまだできてないとか、APIへのアクセスが従量課金とか、様々な理由でAPIに接続できないけど、データ取得の部分は作っておきたいし、本番を想定したデータでデザイン確認したい、なんて時にNext.jsのAPI機能はとても便利。

Appルーターで新しくなったfetchでローカルのテストAPIにつなげておいて、本番APIが使える段階になったら、接続先を変えてテストをすればよい、というのは楽。

pageルーターの時のようにフォルダ名の縛りがないので、そこかしこに適当にroute.tsを作ってしまって管理が大変になりそうだなとは思う。

タイトルとURLをコピーしました