Next.jsのAPI呼び出しがよくわからない?内部APIと外部APIの使い分けを解説

nextjs icon
Next.js

こんにちは、とまだです。

みなさん、Next.jsでAPI呼び出しをしようとして「どうやるんだっけ?」と悩んだことはありませんか?

「API Routesとは?」 「内部APIと外部APIってどう違うの?」 「どこにコードを書けばいいの?」

実はNext.jsのAPI呼び出しには、いくつかの方法があるんです。

今回は現役のエンジニア、そして元プログラミングスクール講師としての経験から、Next.jsでのAPI呼び出しについて解説します。

Next.jsのAPI呼び出しは「お店」みたいなもの

Next.jsのAPI呼び出しって、お店の仕組みに似ています。

想像してみてください。

レストランで注文するとき。 お客さん(フロントエンド)が注文して。 厨房(バックエンド)が料理を作って。 ウェイター(API)が料理を運んでくれます。

Next.jsも同じです。

ブラウザがデータを欲しがって。 サーバーがデータを準備して。 APIがデータを届けてくれます。

でも、Next.jsの面白いところは...

自分のお店(内部API)も作れるんです。

なぜNext.jsはAPIも作れるの?

普通のReactだと、データが欲しいときは外のお店(外部API)に頼むしかありません。

でもNext.jsなら、自分のお店も開けます。

これがAPI Routesという仕組みです。

たとえば、こんなメリットがあります。

  • APIキーを隠せる(お客さんに見せたくない情報を守れる)
  • データベースに直接アクセスできる
  • 複雑な処理をサーバー側で済ませられる

つまり、セキュリティ面でも安心なんです。

API Routesを作ってみよう

Next.jsでAPIを作るのは簡単です。

pages/apiフォルダにファイルを置くだけ。

たとえば、ユーザー情報を返すAPIなら...

// pages/api/users.js
export default function handler(req, res) {
  const users = [
    { id: 1, name: "太郎" },
    { id: 2, name: "花子" },
  ];

  res.status(200).json(users);
}

これだけで/api/usersというAPIができます。

シンプルですよね。

もう少し実用的にしてみる

実際のAPIでは、HTTPメソッドで処理を分けることが多いです。

// pages/api/users.js
export default function handler(req, res) {
  if (req.method === "GET") {
    // ユーザー一覧を返す
    const users = [
      { id: 1, name: "太郎" },
      { id: 2, name: "花子" },
    ];
    res.status(200).json(users);
  } else if (req.method === "POST") {
    // 新しいユーザーを追加
    const newUser = req.body;
    // ここでデータベースに保存する処理など
    res.status(201).json(newUser);
  } else {
    // それ以外のメソッドは拒否
    res.status(405).json({ message: "許可されていません" });
  }
}

GETでデータを取得。 POSTでデータを追加。 それ以外は拒否。

RESTfulなAPIの基本形ですね。

フロントエンドからAPIを呼ぶ

作ったAPIをフロントエンドから呼んでみましょう。

一番シンプルなのはfetchを使う方法です。

import { useState, useEffect } from "react";

export default function Users() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch("/api/users")
      .then((res) => res.json())
      .then((data) => {
        setUsers(data);
        setLoading(false);
      })
      .catch((error) => {
        console.error("エラーが発生しました:", error);
        setLoading(false);
      });
  }, []);

  if (loading) return <div>読み込み中...</div>;

  return (
    <div>
      <h2>ユーザー一覧</h2>
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

ローディング表示も入れました。

ユーザーを待たせないって大事ですよね。

サーバーサイドでデータを取得する方法

Next.jsなら、サーバー側でデータを取得することもできます。

これがgetServerSidePropsです。

// pages/users-ssr.js
export async function getServerSideProps() {
  // サーバー側でAPIを呼ぶ
  const res = await fetch("http://localhost:3000/api/users");
  const users = await res.json();

  return {
    props: {
      users, // コンポーネントに渡す
    },
  };
}

export default function UsersSSR({ users }) {
  // すでにデータがあるのでローディング不要
  return (
    <div>
      <h2>ユーザー一覧(SSR版)</h2>
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

この方法のメリットは?

  • SEOに強い(HTMLに最初からデータが入ってる)
  • 初期表示が速い(ローディング画面が出ない)

でも、サーバーに負荷がかかるので注意です。

エラー処理とセキュリティ

APIを作るとき、忘れちゃいけないのがエラー処理です。

エラー処理の基本

// pages/api/users/[id].js
export default async function handler(req, res) {
  const { id } = req.query;

  try {
    // ユーザーを取得する処理
    const user = await getUserById(id);

    if (!user) {
      return res.status(404).json({
        message: "ユーザーが見つかりません"
      });
    }

    res.status(200).json(user);
  } catch (error) {
    console.error("エラー:", error);
    res.status(500).json({
      message: "サーバーエラーが発生しました"
    });
  }
}

適切なステータスコードを返す。 エラーメッセージは親切に。 でも詳細すぎる情報は出さない。

バランスが大事です。

セキュリティの注意点

APIキーなどの機密情報は、必ず環境変数で管理しましょう。

// pages/api/weather.js
export default async function handler(req, res) {
  // 環境変数からAPIキーを取得
  const apiKey = process.env.WEATHER_API_KEY;

  if (!apiKey) {
    return res.status(500).json({
      message: "設定エラー"
    });
  }

  // 外部APIを呼ぶ
  const weatherData = await fetch(
    `https://api.weather.com/data?key=${apiKey}`
  );

  // クライアントにはAPIキーを含まないデータだけ返す
  const data = await weatherData.json();
  res.status(200).json(data);
}

APIキーはサーバー側だけで使う。 クライアントには絶対に渡さない。

これ、本当に大事です。

実務でよくある使い方

実際の開発では、こんな使い方をすることが多いです。

認証を含むAPI

// pages/api/protected.js
import { verifyToken } from "@/lib/auth";

export default async function handler(req, res) {
  // トークンを確認
  const token = req.headers.authorization?.split(" ")[1];

  if (!token) {
    return res.status(401).json({
      message: "認証が必要です"
    });
  }

  try {
    const user = await verifyToken(token);

    // 認証済みユーザーのデータを返す
    res.status(200).json({
      message: "認証成功",
      user
    });
  } catch (error) {
    res.status(401).json({
      message: "無効なトークンです"
    });
  }
}

データベースとの連携

// pages/api/posts.js
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export default async function handler(req, res) {
  if (req.method === "GET") {
    // 投稿一覧を取得
    const posts = await prisma.post.findMany({
      orderBy: { createdAt: "desc" },
      take: 10,
    });

    res.status(200).json(posts);
  } else if (req.method === "POST") {
    // 新しい投稿を作成
    const { title, content } = req.body;

    const post = await prisma.post.create({
      data: { title, content },
    });

    res.status(201).json(post);
  }
}

データベースへのアクセスもサーバー側で安全に。

まとめ

Next.jsのAPI呼び出しについて見てきました。

押さえておきたいポイントは...

  • pages/apiにファイルを置けばAPIができる
  • クライアントからはfetchで呼び出せる
  • getServerSidePropsでサーバー側でも取得可能
  • エラー処理とセキュリティは必須

最初は内部APIから始めてみてください。

外部APIとの連携も、内部API経由にすれば安全です。

少しずつ慣れていけば、複雑な処理も書けるようになります。

共有:

著者について

とまだ

とまだ

フルスタックエンジニア

Learning Next の創設者。Ruby on Rails と React を中心に、プログラミング教育に情熱を注いでいます。初心者が楽しく学べる環境作りを目指しています。

著者の詳細を見る →