基本無料縛りの個人開発技術スタック
こんにちは、初投稿です。
最近Zenn上で無料縛りの個人開発技術スタックの記事がいくつか挙がっており、個人的にこの辺には一家言あるので便乗します。3パターン紹介します。
前提
以下の思想を採用しています。
- 特にこだわりがなければフロントエンドはReact/TypeScriptを使うべき
- AIが得意なので
- ランタイム上の制約がない限り、フルスタックなフレームワークを使うべき
- フロントエンドはReact/TypeScriptを使うので、Next.jsを使うのが良い
- FreshやReact Routerもあるが、認証系ライブラリのサポートにがっつり乗れるNext.jsの方が、個人開発の場合は良さげ
案1: Cloudflare にがっつり寄せる
フレームワーク
以下の理由で Next.js がお勧めです。
- レスポンスの遅さを誤魔化す機能が豊富
- 後で紹介するレイテンシの大きいDBが採用しやすい
- サーバー側の機能も豊富
- レスポンスの遅さを誤魔化す機能(要するにServer ComponentsとServer Actions)があって、尚且つAPI RoutesとそのMiddlewareが書けるフレームワークは有名どころだとNext.jsだけ
詳しくは述べませんが、API RoutesやMiddlewareをがっつり書くならHonoを併用すると綺麗に書けます。
Next.js は、そのままではVercelやNode.jsランタイム等でしか動かないため、何かしらのライブラリを用いてCloudflareで動かせるようにする必要があります。前述の記事で紹介されているのは@cloudflare/next-on-pagesですが、現在はCloudflare側でPagesからWorkersへの推奨環境の移行が進んでいるようで、@cloudflare/next-on-pagesのREADMEにもある通り@opennextjs/cloudflareの利用が推奨されています。デプロイにはこれを使ってください。
DB
前述の通り、Next.jsを使う場合には遅いDBを採用しやすいです。最寄りがシンガポールなNeonも、堂々と採用して良いと思います。とはいえ、あえて遅くする必要もないので、日本リージョンがあり、尚且つ無料枠もNeonより大きい以下のどれかを採用するのが良いと思います。Supabaseは無料枠が小さめなので、Supabaseに寄せる構成にしない場合は大きなメリットは無いでしょう。
-
D1
- SQLite互換で、専用のライブラリもある
- ストレージサイズの無料枠が5GBまでと大きめ
-
Prisma Postgres
- Postgres互換
- ストレージサイズの無料枠が1GBまでだが、それを超えても追加分を払うだけで済むので破産しづらい
-
TiDB Cloud Serverless
- MySQL互換
- ストレージサイズの無料枠が25GiBもある
D1だとCloudflareのエコシステムに寄せられるので、インフラ管理はかなりラクになりますが、一方でベンダーロックインは気になります。Prisma PostgresやTiDB Cloud Serverlessは、PostgresやMySQL互換なので複雑なクエリや特殊なデータ型を扱う場合に便利で、AWS Database Migration Service等の移行サービスで簡単に(アプリケーション側はおそらく環境変数を変えるだけで)別DBに移行できるので、スケールした際に安いDBに移行しやすいメリットがあります。
前述の記事でも言及されていた通り、D1はクセがあり若干慣れるのに時間がかかるかもしれませんが、いくら悩んでも倒産しないのが個人開発のメリットだと個人的には思っているので、是非D1にチャレンジしたいところです。
DBクライアント/ORM
冒頭に挙げた記事はどちらもDrizzle推しでした。個人的なDBクライアント/ORMの思想として、
- 複雑なクエリが不要な際にはPrisma等のORMを用いて簡潔に書きたい
- 複雑なクエリが必要な際にはSQLを直接書きたい
というものがあり、Drizzleの
- SQLライクなクエリをTypeScriptで書ける
という思想は、Prismaほど簡潔ではなくSQLほどの自由度もないため、個人的にはどっちつかずであまり好みではありません。逆に言えば両方のユースケースにバランス良く対応できるということでもあるので、上手く使えば効率は良いのでしょうが、Drizzleがハマるプロダクトかどうかを判定するのはかなり難しいと思います。
個人的には、Prismaを使うのが良いと思います。典型的な型安全なORMとして使えることもさることながら、TypedSQLを使うことで、SQLを直接(しかも型安全に)書くことができます。Prismaはエラーがたくさん出た、と冒頭の記事で書かれていましたが、最近までCloudflareの無料プランではそもそもPrismaはバンドルサイズの問題で事実上使えませんでした。最近のリリースで使えるようになったので、特に問題なく使えると思います。学習コストは個人的な感覚では全く高くないと思います。
リアルタイム
チャットシステムのようなリアルタイムな機能を実装する場合、WebSocketを使うのが一般的です。Cloudflareでも、例えばDurable Objectsを使うことでWebSocketを実装できます。とはいえ制限もいくつかあるので、この部分だけCloud FirestoreやAppSync Eventsも選択肢に入るかもしれません。アプリケーションの要件ごとに何とでも言えてしまうので、ここはあまり深く考えないことにします。
オブジェクトストレージ
Cloudflareに寄せるのであればR2で良いと思います。S3やCloud Storageはストレージ単価が高く、Wasabiは1TBからの契約(約1000円/月)が必要な上、このような制約もあります。無料で始めるのであればCloudflareのことを抜きにしてもR2が良いと思います。冒頭の記事ではR2への接続にaws4fetchを使っていましたが、せっかくWorkersを使うのであれば公式ドキュメント通りの方法で接続するとシンプルに書けて良いと思います。
ちなみに、画像のアップロード機能をつける場合はほぼ確実に画像の圧縮やリサイズ機能が欲しくなるはずです。冒頭に挙げた記事でも困っていました。Edge RuntimeはCPUバウンドな処理が苦手なので、Workers上で処理しようとすると困ると思います。解決法として、Cloudflare Imagesが月5000種類の変換が無料なので、これを使うのが良いと思います。
認証/認可
IDaaSを使う方法として、Auth0が有名です。なんと2万5千人のアクティブユーザーまで無料なのでほとんど困らないと思います。Passkeyにも対応しており、多要素認証(多段階認証ではない)が簡単に導入できます。Next.js統合のためのSDKも用意されており、フレームワークレベルのサポートもバッチリです。基本的にはこれを使っておけば間違いないです。
一方で、せっかくCloudflareに寄せているのだから、Auth0のような外部IDaaSを使いたくないという思想もあるかもしれません。僕もどちらかというとそちら派なので、個人的にはAuth.js (next-auth)が良いと思っています。Auth.js (next-auth)はNext.jsのサーバーサイドで動く認証ライブラリで、こちらもPasskeyに対応しています。
案1まとめ
- フレームワーク: Next.js
- デプロイ: Cloudflare Workers
- DB: D1
- リアルタイム: Durable Objects
- DBクライアント/ORM: Prisma
- オブジェクトストレージ: R2
- 認証/認可: Auth.js (next-auth)
案1のメリット
- Cloudflareのエコシステムに寄せられるので、インフラ管理が楽
- 制限を超えても課金額が急に高くならない
案1のデメリット
- Workers(エッジランタイム)に寄せるので、CPUバウンドな処理が苦手
- 画像処理はCloudflare Imagesに任せられるので、他の圧縮・展開処理が入ると簡単に制限を超える
- 日時のバッチ処理は一応Queuesを使えば軽いものならできる
- 将来的にマイクロサービスアーキテクチャ化したい場合に、分割が難しい
- Cloudflare内で完結させようとすると、Pub/Subがまだprivate betaなので、どうしても同期的な連携になってしまいがち
- ランタイム制限も厳しいので、他の言語で書くことも難しい
- 基本的にTypeScriptで書く必要があるので、TypeScript特有の欠点が気になる
- 個人的には、サーバーサイドの言語にはResult型やOption型が欲しい
案2: Supabaseに寄せる
サーバーサイドをまるっとSupabaseに寄せる案です。
先に料金について書いてしまいますが、DBとオブジェクトストレージはそれぞれ500MB/1GBまで無料とやや小さめですが、認証は5万人まで無料なので、短いテキストベースのアプリケーションであればほとんど困らないと思います。リアルタイム機能のサポートも優秀なので、リアルタイムチャット機能が基盤のアプリケーションであればかなり優秀です。また、無料枠を超えても次は$25/月でかなり枠が広がるので、スケールを考えた場合にも悪くありません。セルフホスト版に移行することで価格を抑えながらスケールすることもできます。
フロントエンドフレームワーク
サーバーサイドはSupabaseに寄せるので、フロントエンドはサーバー機能を持たないシンプルなSPAにした方が良いと思います。候補としては、React RouterのSPAモードやViteのreact-tsテンプレートがあるでしょうか。好みですが、個人開発の場合はファイルシステムベースのルーティングが最初から用意されているReact RouterのSPAモードを使うのが良いと思います。
デプロイ先としては色々ありますが、無料枠で送信料制限のないCloudflare Pagesにしておくのが良いと思います。
DB/リアルタイム/DBクライアント/ORM
シンプルにSupabaseのDatabaseとJavaScript Clientに乗っかっておけば、勝手にリアルタイムな機能も付いてくるのでお勧めです。PrismaやDrizzleも使えますが、サーバーサイドの管理が必要になるため、せっかくSupabaseに寄せるのであれば、SupabaseのJavaScript Clientを使うべきでしょう。
ちなみに他の選択肢としてGraphQL APIを自動で生やす機能を使うこともできます。リアルタイム機能の不足等があるので、今回は単純にSupabaseのJavaScript Clientを使うことをお勧めしますが、アプリケーション要件次第では(例えばリアルタイムはいらないがネイティブアプリが必要な場合などは)十分ありだと思います。
オブジェクトストレージ
SupabaseのStorageを使うのが良いと思います。Postgresの権限機能をうまく使って、サーバーサイドコードを書かずにバケットやオブジェクトの読み書きの権限を制御できます。R2やS3を使うとサーバーサイドが必要になるので、Supabaseに寄せるのであればSupabaseのStorageを使うのが良いでしょう。
認証/認可
SupabaseのAuthを使いましょう。これを使わないと、Supabaseのサーバーサイド無しの認可システムを利用することが難しいです。Auth0以上の5万人のアクティブユーザーまで無料なので、ほとんど困らないと思います。Passkey対応はまだですが、Magic LinkやTOTP等の様々な認証方式をサポートしています。
その他
どうしてもサーバーサイド側で処理をしたい場合は、Edge Functionsを使いましょう。Denoをサポートするエッジランタイムで、Deno特有のimport方式とエッジ用関数のスコープの狭さがマッチして、なんとも言えない開発体験の良さがあります。
案2まとめ
- フロントエンドフレームワーク: 何でも(React系SPA推奨)
- デプロイ: Cloudflare Pages
- DB: Supabase Database
- リアルタイム: Supabase Realtime
- DBクライアント/ORM: Supabase JavaScript Client
- オブジェクトストレージ: Supabase Storage
- 認証/認可: Supabase Auth
- その他: Supabase Edge Functions
案2のメリット
- Supabaseのエコシステムに寄せられるので、インフラ管理が楽
- スケールした際に、セルフホスト版に移行することでコストを抑えられる
- リアルタイム機能や認証認可機能など、Firebase Alternativeならではの機能が豊富
案2のデメリット
- 無料枠が小さめで、データを多めに蓄える必要がある場合はすぐに課金が必要になる
- Supabaseのエコシステムに寄せるので、他のサービスに移行する際に手間がかかる
- ビジネスロジックをできるだけ小さくして、Edge Functionsに寄せる必要がある
- ビジネスロジックが大きくなると、開発体験は悪化する
案3: AWSに寄せる
フレームワーク
やはり個人開発ですので、Next.jsを使うことから考えたいです。AWSに寄せるのであれば、AWS Amplifyを使いたいところですが、これの無料枠が使えるのは12ヶ月間のみです。
ではどうしようもないのかというとそんなことはなく、Cloudflare Workersのところで紹介した@opennextjs/cloudflareのAWS版である@opennextjs/awsがあり、これを使えば簡単にAWS LambdaへNext.jsをデプロイできます。Lambdaは毎月100万リクエストが永年無料です。
DB/リアルタイム/認証/認可
実はAWSには、無料で使えるRDBはありません。Prisma PostgresやTiDBを使っても良いですが、せっかくならAWSに寄せ切ってDynamoDBについて考えてみましょう。25GBの無料枠があるお得なNoSQLです。世間で言われているほどではありませんが、やはりRDBに比べると複雑なクエリが書けないことには注意する必要があります。
また、DynamoDBと連携するAppSyncを使うことで、GraphQL APIを自動で作成できます。AppSyncは厳密には無料ではないのですが、最初の12ヶ月は無料枠があり、それを超えても100万クエリあたり4ドルなのでほぼ無料です。Next.jsの優秀なキャッシュ機能を活用して、クエリ数を頑張って減らしましょう。
ちなみにAppSync Eventsを使うことで、リアルタイム機能も自動で生える他、Cognitoとの連携で認可も行えるため、この辺りのサービスの使用も確定で良いでしょう。
CognitoはAuth0と同じく認証機能を提供するサービスです。他サービスと比較すると無料枠の月間アクティブユーザー数は1万人とやや少なめですが(個人開発で超えることはほぼ無いと思いますが)、Passkeyのサポートもあり、十分な機能を持っています。
DBクライアント
見出しを統一したかったのでDBクライアントとしましたが、GraphQLクライアントについて考えます。一般にRSCとGraphQLは相性が悪いとよく言われます。RSCとGraphQLのどちらも解決したい課題が同じだからです。
RSCとGraphQLの相性が悪い理由
簡単に言うと、旧来のREST APIのエンドポイントは、
- パフォーマンスを優先して、クライアントで必要なデータを全て返す
- 責務が大きなエンドポイントになるため、変更容易性が低下する
- 設計を優先して、そのエンドポイントの最小責務なデータを返す
- クライアントは複数のエンドポイントを叩く必要がある
- データフェッチのウォーターフォールが発生し、パフォーマンスが悪化しやすい
のどちらかを選択する必要がありました。Reactの視点では、前者のエンドポイントではバケツリレーやグローバルな状態管理が発生するし、後者ではコンポーネントごとに必要なエンドポイントを叩けそうですが、複数のコンポーネントが同じエンドポイントを叩いてしまうなどやはりパフォーマンスが気になります。RSCはサーバー側で動き、複数のエンドポイントを叩いてもパフォーマンスが悪化しづく、重複するリクエストはキャッシュされるため、堂々と後者のアプローチを取ることができます。GraphQLはFragmentという機能を用いて、各コンポーネントが必要なデータを定義し、1クライアントにまとめてから送信することでこのパフォーマンス劣化を防ぐことができました。このように、RSCもGraphQLもどちらも解決したい課題が同じであり一緒に使う意味があまり無く、RSC内でGraphQLクライアントを使うようなライブラリ対応が進んでいないのです。
とはいえ実は、AWSでできるだけ節約したい場合には、RSCとGraphQLの相性は悪くはありません。AppSyncの課金体系は前述の通りクエリ回数単位なので、GraphQLエンドポイントはできるだけでかーいものを1ページにつき1つだけ叩くのがコスパが良いです。そうするとバケツリレーになりそうですが、単純にRSCのキャッシュ機能を使って各コンポーネントから全く同じクエリを叩けば問題ありません(直接叩かずに、API RoutesでラップしてGETメソッドで叩けるようにする必要があります)。
そうなると、nitrogqlのような.graphqlファイルを直接インポートするアプローチであれば、全く同じクエリを使い回せて筋が良いように思えます。クライアントもRelqyのような高機能なものは不要で、例えばurqlのようなシンプルなものが使えます。
オブジェクトストレージ
S3を使いましょうと言いたいところですが、無料枠は12ヶ月のみです。そんなに高くはならないとは思いますが、オブジェクトストレージを多用する場合には、R2やWasabiの方が良いでしょう。
とはいえ、AWSのエコシステムを考えるとS3を使うのと使わないのとでは大きな差があるため、よほどのことがない限りは使った方が良いと思います。
案3まとめ
- フレームワーク: Next.js
- デプロイ: AWS Lambda
- DB: DynamoDB
- リアルタイム: AppSync (Events)
- DBクライアント: urql
- オブジェクトストレージ: S3
- 認証/認可: Cognito
案3のメリット
- AWSのエコシステムに寄せられるので、インフラ管理が楽
- 紹介しなかった他のAWSサービスとの連携が可能
- SQS+Lambdaで一部処理を非同期化したり
- SNSで通知を送ったり
- Step Functionsでバッチ処理を多段化したり
- などは無料でできる
- スケールした際に、AWSの他のサービスに移行することでコストを抑えられる
案3のデメリット
- そこそこの使用量でもおそらく10円程度の課金が発生する
- 設計難易度は若干高い
まとめ
個人開発の技術スタックを3パターン紹介しました。もちろん他にもいくらでも選択肢はあります。また、キャッシュストアやCI/CDや監視系のサービスは紹介しきれませんでした。
さて、僕ならどれを選ぶかというと、僕個人としては無料にこだわりは無いのでどれも選びません。自宅サーバーやVPSにKubernetesクラスタを1度立ててしまえば、あとはデプロイし放題なのでそれを愛用しています。KagoyaのVPS、安すぎるのにストレージが大量に付いてくるのでオススメです。Hasuraやself-hostedなSupabase等の他、ホンモノのPostgresをアプリケーションサーバーと同じサーバー内で使えるのが嬉しいです。
さあ、あなたもおうちKubernetesを始めましょう!
以上です。
Discussion