Nextjs+Hono+Supabaseでアプリを作ってみた
またもお久しぶりです。前回の記事からどれぐらい経ったのでしょうか...(数えたくない)
最近個人開発のモチベが高くてちょっと作ってみました。
世ではちょうどHonoが熱い(炎だけにね!)とのことで面白そうさので勉強がてらアプリを作ってみました。
https://habit-12i0l66k7-junki-itagakis-projects.vercel.app/
使用技術
- Nextjs
- v15を使用。useCachとか、あとはexperimentalな機能はあまり使いませんでした(BE側が結構初めてなので知らないものをあまり増やしたくなかった)
- shadcn
- 最近のUI実装は全部これにしてる。最初は使うコンポーネントをいちいち追加しなきゃいけないのめんどくさいって思ったけど慣れるとそうでもない。v0対応しているし見た目もいけてるしで今のところ文句なし
- Hono
- 噂のすごいやつ。Route Handler部分をHonoで実装。RPCが凄すぎてやばい
- Supabase
- 認証諸々を全て任せる。認証メールとか最初こなくて焦った。
- Drizzle
- ORM。昔はPrismaを使っていたけどモダンなものに。seedファイルの読み込みが簡単で幸せ(今のPrismaはまだseed読み込むのめんどくさいのだろうか...)
- Stripe
- 決済まわり。決済で他のものを知らない。
みんなで課金してね
- 決済まわり。決済で他のものを知らない。
- OpenAI
- LLMのモデルはgpt-4o-miniを使用
こだわり
All TypeScript(JavaScript)
僕はもともとPythonから始まった人でして。APIもよくDjango REST fremeworkを使っていたのですが、
今回はhonoを使うということでモノレポライクに全てTypeScriptで実装しました。
するとバリデーションはzodで統一できるしfetchで困ることも減って開発者体験はすごいよかったです。
Hono
今回のアプリを作るきっかけです。HonoのRPCが便利だよときいてやってみましたが、便利なんて言葉じゃ足りないです。個人的には今後必須と言いたいぐらいよかったです。
const route = new Hono()
.basePath("/api")
.route("/users")
export const GET = route.fetch;
export const POST = route.fetch;
export const PUT = route.fetch;
export const PATCH = route.fetch;
export const DELETE = route.fetch;
export type HonoAppType = typeof route;
export const honoClient = hc<HonoAppType>(baseUrl)
const updateUser = async ({
userId,
username,
}: {
userId: string;
username: string
}): Promise<ApiResult<boolean>> => {
try {
const response = await honoClient().api.users[":id"].$patch({
json: { name: username },
param: { id: userId },
});
if (!response.ok) {
const { message } = await response.json();
return new Failure({ errorMessage: message });
}
return new Success(true);
} catch {
return new Failure({ errorMessage: "サーバーエラーが発生しました" });
}}
こんなよくある通信でもpatchからのresponseに型推論が動いているのが素晴らしい。
okじゃないときはmessageというのが返ってきてokの時は...というふうに実装ができる。普段だとanyやasを使って無理やり型をつけるところを安全に実装できるのは素晴らしい。
詰まったところ
stripeの本番環境
stripeの本願環境の申請がなかなか通らなかった。ただメールをよくみたら特定商取引法に基づく表記のページを作れってちゃんと書いてあった...(stripeのサポートさん、何回も無駄に申請してすいませんでした...)
Cookie
Server Actionを使っているとCookieがブラウザに反映されない。これNext側でどうにかしてもらえないですか?
今回はこんなふうにfetchをしたタイミングでCookieをブラウザに反映させた。
import { cookies } from "next/headers";
import setCookieParser from "set-cookie-parser";
export const setResponseCookie = async (response: Response) => {
const setCookie = response.headers.get("Set-Cookie");
if (setCookie) {
const splitCookieHeaders = setCookieParser.splitCookiesString(setCookie);
const cookieObjects = setCookieParser.parse(splitCookieHeaders);
for (const { name, value, sameSite, ...rest } of cookieObjects) {
(await cookies()).set(name, value, {
sameSite: sameSite === "strict" ? "strict" : "lax",
...rest,
});
}
}
};
最後に
今回初めて個人開発したアプリを誰かに使ってもらえるような形で世に放ちました。今後はより多くの方々に使ってもらえるよう改良したり、またさらに別のアプリ開発にもチャレンジしていきたいと思います