https://zenn.dev/takepepe/articles/nextjs-service-layer
本稿は、Next.js で「getServerSideProps や API Routes」を利用するアプリケーション向け内容になります。重厚な作りになるので、要件に適合する・しないはあると思いますので、あしからず。
Next.js は SPA 配信の最適化にフォーカスしており、Backend の機能面が十分とは言えません。pages の Page コンポーネントや API Routes は、controller としての機能を提供するのみです。ドキュメントを見てもわかるとおり、一連処理はあらかじめ middleware やラッパー関数を用意するのが常套手段かと思います。
Node.js Backend フレームワークとして、NestJS は有力な候補かと思います。レイヤーやモジュール・DI の構成がフレームワークで定められており、プロジェクトごとに構成が大きく異なるということがありません。NestJS にはリクエストの入口として、「controller / resolver」が「service」を利用して、データ永続層と接続します。この Service 層があることで「controller / resolver」から、データ永続層の実装詳細が分離されます。
Next.js には Service 層相当の機構がありません。getServerSidePorps
などのデータ取得関数は通常、以下の様な実装詳細となります。データ取得・データ整形の詳細が、getServerSidePorps
内に露呈しています。
export const getServerSideProps: GetServerSideProps<Props> = async () => {
const { data, err } = await fetch("<https://api.example.com/api/users>").then(
async (res) => {
const data = await res.json();
if (!res.ok) return { err: data };
return { data };
}
);
return { props: { data, err } };
};
筆者のいう Service 層とは、以下の様な「getUsers
関数」として作り込まれたモジュールを指し、みてのとおりgetServerSidePorps
が端的になります。Http リクエストの結果は{ data, err }
に内部で整形されているため、ダイレクトに props として返却できます。この非同期関数を、当稿では「Service 非同期関数」と呼びます。
import { getUsers } from "@/services/api.example.com/users";
export const getServerSideProps: GetServerSideProps<Props> = async () => {
return { props: await getUsers() };
};
「Service 非同期関数」をモジュール分割しておくと、データの取得先が ORM であろうとも、実装詳細が露呈することはありません。「データ取得後は{ data, err }
に整形するのがおすすめ」という記事を以前書きましたが、プロジェクト定型規格を設けるメリットが、こういったところにも現れます。
import { getUsers } from "@/services/some.orm.client/users";
export const getServerSideProps: GetServerSideProps<Props> = async () => {
return { props: await getUsers() };
};
この Service 非同期関数はもちろん API Routes でも再利用できます。レスポンスに{ status }
を含めていた理由も自明ですね。
src/pages/api/users/index.ts
import { getUsers } from "@/services/some.orm.client/users";
export default async function handler(req, res) {
if (req.method === 'GET') {
const { data, err, status } = await getUsers();
if (data) res.status(status).json(data)
if (err) res.status(status).json(err)
} else {
}
}
NestJS の service には、validation モジュール(DTO)を中継し、入力値チェックを行う機構があります(ドキュメント中、CreateUserDto の件)本稿で紹介している「Service 非同期関数」も同様に、この仕組みを導入します。この入力値チェックが備わることで、不正な入力値はリクエストを送らない という制御弁を設けられます。