LoginSignup
15
12

More than 3 years have passed since last update.

Next.jsでクロスオリジンエラーとたたかう

Last updated at Posted at 2020-07-23

はじめに

30代未経験からエンジニア転職をめざすコーディング初学者のYNと申します。お読みいただきありがとうございます。
初学者が一度はつまづくと思われるCORSエラーについて、自分の学びを本記事にまとめました。

今回の問題

Next.jsを使って開発中、フロントエンドから外部API(今回は自作のAPI)にアクセスしようとするとクロスオリジンエラーが発生する。事象を簡単に図にすると以下の通り。
スクリーンショット 2020-07-23 11.27.03.png

クロスオリジンエラーとは

エラーについて正しく理解したい方はこちらの記事を参考にしていただくとして、自分の理解をかなりざっくり図にするとこんな感じです。
スクリーンショット 2020-07-23 11.08.28.png

ブラウザ自身が、ホスティングサーバー以外のドメインにアクセスすることを禁止しているため、フロントエンドから外部APIなどにアクセスしようとするとエラーになる、というのがCORSエラーの正体です。

解決策

上記を見ればお分かりいただけます(?)通り、解決策は2つあります。
* ブラウザちゃんに他の男(ドメイン)と直接会話してもらう
* ブラウザちゃんにバレないように、ホスティングサーバーの方で外部APIと会話して、その結果をブラウザちゃんに伝える。

本記事では、後者について対処法をまとめていきます。

対処法

ブラウザちゃんにバレないように、ホスティングサーバーの方で外部APIと会話して、その結果をブラウザちゃんに伝える。

この対処法を簡単に図にまとめると下記になります。
スクリーンショット 2020-07-23 11.40.31.png

ブラウザが特定のAPIにアクセスする代わりに、ホスティングサーバー側でAPIとやりとりして、結果をブラウザに返すようにルートを変更します。
これを実現するために、Nextにカスタムサーバーを導入することで対処することができます。

カスタムサーバーを設定する

やることは2つだけです。
まず、rootディレクトリにserver.jsをつくります。

server.js
const express = require("express");
const next = require("next");
const { createProxyMiddleware } = require("http-proxy-middleware");

const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();

app.prepare().then(() => {
  const server = express();

  server.use(
    "/endPoint",
    createProxyMiddleware({
      target: "https://example.amazonaws.com",
      changeOrigin: true,
    })
  );

  server.all("*", (req, res) => {
    return handle(req, res);
  });

  server.listen(port, (err) => {
    if (err) throw err;
    console.log(`> Ready on http://localhost:${port}`);
  });
});


これにより、ブラウザからhttps://example.amazonaws.com/endPointにアクセスすることができます。

次に、package.jsonを変更します。

package.json
"scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "NODE_ENV=production node server.js"
  }

一応補足ですが、ブラウザからアクセスするときは、https://example.amazonaws.com/endPointでなく/endPointにアクセスします。つまり、ブラウザとしてはホスティングサーバーにアクセスしているつもりです。

TestComponent.js
import axios from "axios";

export default function TestComponent() {
  useEffect(() => {
    const opt = {
      method: "get",
      url: "/endPoint",
    };
    axios(opt).then((res) => {
      console.log(res.body);
    });
  }, []);
}

最後に

いかがでしたでしょうか。
間違っている点などご指摘いただければ幸いです。

参考にさせていただいた記事

https://qiita.com/att55/items/2154a8aad8bf1409db2b
https://qiita.com/lelouch99v/items/3dc11676bb9c23457d41

15
12
2

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
15
12