APIを公開する
最終更新: 2022年02月15日
APIとは
APIはコントロールの手段だと思ってください。テレビのリモコンのイメージです。テレビには様々な機能が用意されていますが、リモコンのスイッチを使って自由に操作することができます。
Webアプリやデータベースに対しAPIを作成して公開するということは、自分でリモコンを作ってみんなに配るイメージになります。
APIはデータベースに限らず開発したプラグインや拡張機能にも存在します。要するに何らかのデータやシステムに対するリモコンがAPIだと思ってください。
RESTful API(REST API)
APIの中でネットワーク通信を介するものをRESTful API(REST API)と呼びます。ネットワーク通信を介してAPIにアクセスすることをリクエストといいます。リクエストには主要な3つのタイプが存在します。(他にも存在します)
- GET - 取得
- POST - 投稿
- DELETE - 削除
たとえば決済システムであるStripeはAPIを用意しています。カスタマー(顧客)のAPIにGETのリクエストを行うことで、Stripe上の顧客データを取得することができます。リクエストを行う対象のURLをエンドポイントと呼びます。
Next.js でAPIを作る
Next.jsでは簡単にAPIを作ることができます。/pages/api/
の中に作成したファイルはファイル名をエンドポイントとするAPIとして振る舞うようになります。
たとえば /pages/api/hello.ts
というファイルを作成した場合、ローカル環境であれば以下のエンドポイントでアクセスできます。
http://localhost:3000/api/hello
使いたい場所から上記のURLを呼ぶとファイルの中身が実行され、結果を受け取ることができます。もちろん、npm run dev
でローカルサーバーを起動しておく必要があります。プロダクトの公開後はlocalhostではなくプロダクトのドメインからアクセスできます。
APIの構造
たとえばユーザーデータを追加、取得、削除するAPIを作るとします。以下のようになります。ユーザーデータを取得、削除など行う方法はプロジェクトごとに異なるので省略しています。
pages/api/users.tsimport { NextApiRequest, NextApiResponse } from 'next'; // 返却値の型 type Response = { id: string; name: string; } | string; const handler = async (req: NextApiRequest, res: NextApiResponse<Response>) => { try { switch (req.method) { // 追加 case 'POST': const newUser = req.body; // ユーザー情報を追加する処理... break; case 'UPDATE': const user = req.body; // ユーザー情報を更新する処理... break; case 'GET': // データベースからユーザー情報を取得して返却 const userId = req.query.id; const user = '...'; res.status(200).send(user); return; break; case 'DELETE': // データベースからユーザーを削除する処理 break; } res.status(200).send('成功'); } catch (error) { console.log(error); res.status(500).send('error'); } }; export default handler;
まず、以下の部分が大枠の構造です。
// リモコン const handler = async (req: NextApiRequest, res: NextApiResponse<Response>) { // 何が起きるのか } // APIとして使えるように提供 export default handler;
上記の中で、引数には req
と res
があります。 req
にはリクエストの詳細情報が入っています。テレビのリモコンで言うとどのボタンが押されたのか、音量をいくつに設定したいのか、などの情報が入っているイメージです。
res
は操作した人に結果を返すために使われます。たとえば自動販売機のAPIだとしたら、お金と商品名を渡せばドリンクが返ってきます。
たとえば以下の場合、APIにアクセスすると「何もしません」という文字が単に返ってきます。
/pages/api/test.ts// リモコン const handler = async (req: NextApiRequest, res: NextApiResponse<string>) { res.status(200).send('何もしません'); } // APIとして使えるように提供 export default handler;
reqとresにはNext.jsが用意する型を指定しています。NextApiResponse<Response>
において<>
の中身は返却値の型で、ここは自分で定義する必要があります。上記の例の場合単に文字列を返すので NextApiResponse<string>
としています。
リクエストのタイプ
req: NextApiRequest
の引数にリクエストの内容が入っています。たとえばリクエストのタイプはreq.method
に入っています。今回の例ではリクエストタイプによってswitch
で処理を切り分けています。
リクエストデータを受け取る
一般的にリクエストの際に処理に必要な情報をあわせて渡します。たとえばユーザーを取得したいのであれば「どのIDのユーザーが欲しいのか」伝える必要があります。また、ユーザーを追加したい場合追加したいユーザーの情報をまるごと渡す必要があります。
情報の渡し方には大きく2パターンあり、受け取り方もそれぞれ異なります。
bodyで受け取る
req.body
の中にリクエストデータが入っています。ボリュームが多い場合や個人情報などのデリケートなデータはこの形式でデータを受け取ります。
クエリパラメーターでデータを受け取る
エンドポイントを呼ぶ際、?xxx=yyy
の形式でキーと値をURLの末尾に加えることで値を渡せます。これの値をクエリ(クエリパラメーター)と呼びます。クエリは&
で連結することで複数指定できます。
たとえばエンドポイントにクエリパラメーターを付けてアクセスしたとします。
http://localhost:3000/api/user?id=aaa&name=taro
するとAPIでは以下のように値を受け取ることができます。
const handler = async (req: NextApiRequest, res: NextApiResponse<string>) { const userId = req.query.id; // IDを受け取る const userId = req.query.name; // IDを受け取る }
この形式はデータがURLに付属する形になるので過去のアクセス履歴からURLを辿ることでデータが漏洩するリスクがあります。クエリでデータを渡す(受け取る)場合は十分に注意しましょう。
たとえばYouTubeやGoogleは検索結果にクエリがついています。そのクエリに基づいてアプリが検索結果を表示しています。これはURLのクエリを介して検索キーワードをアプリに渡すことで実現しています。検索キーワードは特にデリケートな内容でもないのでクエリでやり取りするのに向いています。
APIにリクエスト(fetch)する
今度は先ほど作ったAPIにリクエストしてみましょう。最近は特別なライブラリがなくてもブラウザに標準で備わっている Fetch を使うことで簡単にリクエストできます。
const url = 'http://localhost:3000/api/user'; const data = { name: 'Taro Yamada', } const response = await fetch(url, { method: 'POST', // *GET, POST, PUT, DELETE headers: { 'Content-Type': 'application/json', // もしくは // 'Content-Type': 'application/x-www-form-urlencoded', }, body: JSON.stringify(data), // 本文のデータ型は "Content-Type" ヘッダーと一致する必要があります }); response.json(); // レスポンスの JSON を解析
fetchの構造はシンプルです。
const result = await fetch(APIのエンドポイント, オプション) result.json() // 結果を取得
クエリパラメーターでデータを渡したい場合、エンドポイントにクエリパラメータをつけるだけです。以下の例の場合、idがxxx
のユーザーを取得するイメージです。
// id=xxxをクエリとして付与 const url = 'http://localhost:3000/api/user?id=xxx'; // 上記エンドポイントを使ってfetchする const result = await fetch(url, {method: 'GET'})
bodyでデータを渡したい場合、データの形式を指定した上でその形式にデータを変換して渡します。前述の例の場合
headers: { 'Content-Type': 'application/json', // もしくは // 'Content-Type': 'application/x-www-form-urlencoded', },
の部分がデータ形式になります。ほとんどの場合 application/json
ですが、APIによってはapplication/x-www-form-urlencoded
など別の形式でデータを受け取る仕様になっています。APIの仕様書に従って正しく設定しましょう。自分でAPIを作る際は application/json
で良いでしょう。
fetchのデータは基本的に文字列である必要があります。そのため、JSON(オブジェクト)形式のデータを文字列に変換してから渡しています。Content-Type
が application/json
の場合基本的にbody
はこの形式になります。
body: JSON.stringify(data)
以上がAPIの作成と呼び出しに関する解説となります。
同一オリジンポリシーに注意する
WebサイトからAPIを呼ぶ場合、APIは同じWebサイトと同じドメインである必要があります。これを同一オリジンポリシーと言います。ただしAPI側が異なるドメインからのリクエストを許可していた場合はリクエストが成功します。
Next.jsの場合APIはWebアプリと同じドメインに公開されるため問題ありませんが、別のアプリのAPIを呼ぶ際などはこのポリシーに引っかかっていないか注意しましょう。引っかかった場合
Access-Control-Allow-Origin ~
のようなエラーがブラウザのコンソールに表示されます。その場合jsonp(非推奨なので解説しません)というアプローチを使うか、API開発者にオリジンポリシーの変更を相談してください。