Chapter 07無料公開

1-5. Create React Appとの違いを知る(API)

API機能

Next.jsには簡易なAPI機能が存在してます。
一件フロントエンドの検証・素振りには必要なさそうに見えますが、CORSの回避などにとても便利に利用できます。

例として今回はnager.dateの祝日データを利用してみます。

// pages/index.tsx
// (動かない例)
import { useEffect, useState } from "react"

export default function Home() {
  const [holidays, setHolidays] = useState([])
  useEffect(() => {
    fetch("https://date.nager.at/api/v2/PublicHolidays/2020/JP")
      .then(r => r.json())
      .then(data => {
        setHolidays(data)
      })
  }, [])
  return (
    <div>
      {holidays.map(day => <div key={day.name}>{day.localName}</div>)}
    </div>
  )
}

このようにするとおそらくCORSが設定されてないAPIのため下記のようなエラーが出るでしょう

Access to fetch at 'https://date.nager.at/api/v2/PublicHolidays/2020/JP' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

APIを使って解決する

ではこれをAPIを作成することで回避してみましょうpages/api/holidays.tsとして作成してみます。

// /pages/api/holidays.ts 

import fetch from 'node-fetch' 

export default (req, res) => {
  res.statusCode = 200
  fetch("https://date.nager.at/api/v2/PublicHolidays/2020/JP")
    .then(r => r.json())
    .then(data => res.json(data)) 
}
// pages/index.tsx
// (動く例)

import { useEffect, useState } from "react"

export default function Home() {
  const [holidays, setHolidays] = useState([])
  useEffect(() => {
    fetch("/api/holidays") // ここをProxyしたAPIにする
      .then(r => r.json())
      .then(data => {
        setHolidays(data)
      })

  }, [])
  return (
    <div>
      {holidays.map(day => <div key={day.name}>{day.localName}</div>)}
    </div>
  )
}



今度は無事に表示されました。

このようにAPIを利用してProxyすればドメインを経由するため、CORSも問題ありません。またAPIキーを利用するような場合もクライアントに処理させず、API側にキーを隠蔽することもできます。

Tips: ServerSideでCORSを回避する

ちなみに、CORSを回避する方法としてgetServerSidePropsを利用する方法もあります。

https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering
import { GetServerSideProps } from "next"

// レンダリング前に実行される
export const getServerSideProps: GetServerSideProps = async () => {
  const holidays = await fetch("https://date.nager.at/api/v2/PublicHolidays/2020/JP")
    .then(r => r.json())
  return { props: { holidays } }
}

export default function Home({ holidays }) {
  return (
    <div>
      {holidays.map(day => <div key={day.name}>{day.localName}</div>)}
    </div>
  )
}