
Minral
公開2026年から稼働
Next.js / Supabase / Cloudflare Workers / Polar
鉱物のコレクションを記録し、他のコレクターと共有できる Web サービスです。 2026年1月から公開しています。 企画・設計・実装・インフラ・決済導入・リリースまで、すべて一人で行いました。
主な機能
- 会員登録・ログイン・パスワードリセット
- 投稿の作成と編集(写真のトリミングとアップロード)
- タイムライン、検索
- コレクション(投稿をまとめて管理)、ブックマーク
- フォロー / フォロワー
- 通知(アプリ内と Web Push)
- プロフィール(ユーザー名による URL)、統計ページ
- 鉱物図鑑・産地図鑑
- サブスクリプション課金
- 多言語対応(日本語・英語)
- PWA(ホーム画面に追加して利用できる)
技術スタック
- フレームワーク — Next.js 15(App Router)/ React 19 / TypeScript
- スタイリング — Tailwind CSS / Headless UI / Framer Motion
- バックエンド — Supabase(認証・PostgreSQL)
- ストレージ — S3 互換オブジェクトストレージ(署名付き URL で直接アップロード)
- 決済 — Polar(サブスクリプション)
- インフラ — Cloudflare Workers(OpenNext でデプロイ)
- 多言語化 — next-intl(
[locale]ルーティング) - PWA / 通知 — Serwist / Web Push
- 画像処理 — jSquash(WebAssembly・ブラウザ側で圧縮)/ sharp / react-easy-crop
- その他 — Zod / Resend / reCAPTCHA v3 / pdfmake / QR コード生成
- テスト — Jest / React Testing Library
実行環境の制約から逆算して設計した
写真を扱うサービスなので、画像の処理をどこで行うかが最初の分かれ道でした。
Cloudflare Workers にはリクエストあたりの CPU 時間に上限があります。 サーバー側で画像を縮小する設計は、この上限の前では現実的ではありません。
そこで、アップロードをサーバーに通さない構成にしました。 署名付き URL を発行し、ブラウザからストレージへ直接送ります。 送る前に WebAssembly(jSquash)でブラウザ側の圧縮とリサイズをかけ、 転送量そのものを小さくしています。
「軽くしたかったから」ではなく、その環境で成立する形が他に無かったからの設計です。 実行環境の制約は、後から回避するより先に読んで設計に織り込むほうが安く済みます。
同じ理由で、ライブラリの選定基準に「Edge ランタイムで動くか」を入れました。 OpenNext を使い、Node.js ランタイムを前提としないビルドにしています。
決済は「売れたあとに何が起きるか」で選んだ
多言語対応をしている以上、海外の利用者から課金される可能性があります。
Stripe を使う場合、越境販売にかかる税の登録と申告は事業者側の責任になります。 個人で引き受けるには負担が大きいと判断し、 Merchant of Record としてその手続きを代わりに負う Polar を選びました。
決済サービスは「つなぎやすさ」で比べられがちです。 ただ、実装は一度で終わりますが、税と申告は毎年続きます。 比べるべきはそこだと考えました。
あわせて、課金の状態を取得する処理と、機能の出し分けを分離しています。 決済サービスに依存する箇所を局所化しておけば、乗り換えが設計変更になりません。
多言語は「入れる」より「切り替わり続ける」ほうが難しい
最初から多言語構成で作っていました。それでも、 言語を切り替えたのに表示が変わらないという事象に何度も遭遇しました。
原因は、言語を決めている場所が複数あったことです。 URL に含まれる locale、保存された設定、キャッシュされたレスポンス。 この3つがそれぞれ別の言語を指しうる。
多言語対応とは、訳文を用意することではありませんでした。 「言語をどこで決めるか」を1か所に集約する設計のことでした。
いまは next-intl と [locale] セグメントで、言語を URL に含めています。
後から言語を足しても、ページ側の実装を変えずに済みます。
個人開発でもテストを書いた
Jest と React Testing Library を入れ、仕様が壊れやすい箇所を中心に置いています。
一人で作っていると、壊したことに気づけるのが自分だけになります。 レビューする人がいないぶん、テストの価値は個人開発のほうが高いと考えています。