学習向けに Next.js の SandBox をローカルに構築する
こんにちはフロントエンドエンジニアの茶木です。
最近は学習用の Next.js の ローカルの SandBox を育てています。
学習用の開発環境の重要性
実際のプロジェクトのコードは、プロジェクトの進行に合わせて蓄積されます。
あるコードを理解するとき、プロジェクトが成熟しているほど、そのコードは学習難度は高い傾向にあります。その理由はいくつかあります。
- プロジェクト特有の機能に対応したため
- やむを得ず標準的でない方法で実装したため
- 周辺のコードが増えたため
そこで、実際のプロジェクトとは切り離された、学習用の SandBox を育てておくと、軽いフットワークで学習や実験ができると考えます。
今回作る SandBox の概要
- TypeScript を標準装備の Next.js 環境を作ります。
- APIのコール周りを準備します。Next.js の API route 機能を利用します。
(具体的にはGETしたデータを使い、ページ生成を想定した構成にします)
セットアップ
TypeScript を構成に含めた Next.js のセットアップは簡単です。
yarn create next-app --typescript
https://nextjs.org/docs/basic-features/typescript
以上です!簡単ですね。
APIコール
API route
公式のチュートリアル の API route Support を参考にして記載しています。
チュートリアルは JavaScript なので、 型を与えて TypeScript に書き直しています。
pages/api
配下で(たとえば hello.ts
)ファイルを作成し、下記のフォーマットでコードを記述すると、 /api/hello
にアクセスしたときにAPIとして機能し、JSONが取得できます。サンプルでは handler
というメソッド名になっていますが export default
していれば任意の名前でOKです。
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
export interface HelloRes {
name: string;
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<HelloRes>
) {
res.status(200).json({ name: 'John Doe' })
}
https://nextjs.org/docs/api-routes/introduction
汎用的な getAPI の受け取り hook を作る
API route 機能でエンドポイントはできました。
次は、アプリケーション側でAPIをコールして、利用しやすい形でデータを受け取るようにします。以下コードです。(ちょっと独自実装色があるので標準的な手法は研究中です。情報お待ちしてます)
import axios, { AxiosError, AxiosResponse } from "axios";
// MEMO: 標準手法は研究中
export const api = async <Res>(
method: string,
query?: unknown,
): Promise<AxiosResponse<Res>> => {
const instance = axios.create({ baseURL: "/api/" });
return await instance.get(method, {
params: query,
});
api メソッドの解説
クエリを渡してAPIをコールします。結果を非同期(Promise
)で return
します。
useGetApi メソッドの解説
ページ表示時(初回 コンポーネント render 直後 )に APIをコールし、取得したデータでページを描画するケースは一般的だと思います。そのケースでは React フックを用いると便利です。
以下、apiメソッドを React フックでラップした useGetApi
です。
APIから情報を取得する前は、 undefined
を返却し取得後は取得データを返します。
import { AxiosResponse } from "axios";
import { useEffect, useState } from "react";
import { api } from "../libs/middleware";
const useGetApi = <Res>(
getApi: () => Promise<AxiosResponse<Res>>
): Res | undefined => {
const [data, setData] = useState<Res | undefined>(undefined);
useEffect(() => {
getApi().then((res) => setData(res.data));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return data;
};
実際にページで使う
export const useHello = (): HelloRes | undefined => {
const getApi = useCallback(() => {
return api<HelloRes>("hello");
}, []);
return useGetApi(getApi);
};
実際に、個別のAPIをコールする際は、さらにフックでラップします。
export default function HelloPage() {
const data = useHello();
if( !data ) return <p>Loading...</p>
return <p>{ data.name }</p>
}
ラップしたフックから取得した data
で、取得前と取得後の処理を書き分けて完成です。
アドバンス
YAGNI の原則に従い、省略していますが、apiメソッドはいくつか改良の余地があります。
エラーハンドリング
instance.interceptors.response.use(
(res: any) => res,
(error: AxiosError) => hundleError?.(error)
);
instance
の箇所に interceptors
を挟むことでエラーハンドリングが可能になります。オプショナルの hundleError
メソッドを渡せるようにすれば良いでしょう。
クエリではなくURLパスを使った変数の受け渡し
/api/[user_id]/[date]/news
前説:api 以下に作ったディレクトリとファイルの階層構造は APIエンドポイントを生成します。このとき、各ディレクトリ名に []
で囲ったものを使うと変数名として機能します。APIコール側は、ディレクトリ構造に合わせたURLでコールすると、変数を渡せます。
const url = [...(pathes || [], method)].join("/");
return await instance.get(url, {
params: query,
});
ふまえて、api メソッドにディレクトリの階層構造に対応した変数の配列( ここでは pathes
)を渡し、エンドポイントを作成する方法でも値をAPIに渡すよう拡張ができます。
おわりに
繰り返しになりますが、SandBox で気軽に試せる環境を作っておくことで学習や実験が捗るようになりました。
Gaji-Laboでは、React経験が豊富なフロントエンドエンジニアを募集しています
弊社ではReactの知見で事業作りに貢献したいフロントエンドエンジニアを募集しています。大きな制作会社や事業会社とはひと味もふた味も違うGaji-Laboを味わいに来ませんか?
もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください。お仕事お問い合わせや採用への応募、共に大歓迎です!
求人応募してみる!