はじめに
T3 Stackとは?
simplicity(シンプルさ), modularity(モジュール性), and full-stack typesafety(フルスタックの型安全) に重点をおいて、以下の技術要素で構成されています。
- Next.js
- tRPC
- Tailwind CSS
- Typescript
- Prisma
- NextAuth.js
tRPCは最近目にするようになってきましたが、その他はポピュラーな構成だと思います。
npx create-t3-app@latest
を実行して、必要なモジュールを選択するとプロジェクトの雛形が作成されます。
Fly.ioとは?
いわゆる PaaS です。同様のサービスでは Heroku が有名ですね。
無料でアプリをデプロイできて、必要であれば追加のリソースを従量課金で購入できます。
永続ボリュームストレージが使用できるため、 sqlite を配置してデータベースを利用したアプリ開発もできます。
T3 Stackのアプリを作成する
create-t3-app コマンドを実行し、質問に回答します。
$ npx create-t3-app@latest
# プロジェクト名を決めます
? What will your project be called?
t3-app-example
# TypeScriptしか勝たん
? Will you be using JavaScript or TypeScript?
o JavaScript
* TypeScript
# 今回は認証を実装しないのでnextAuthを外しました
? Which packages would you like to enable?
o nextAuth
* prisma
* tailwind
* trpc
# git repositoryは自分で用意するのでNoを選択
? Initialize a new git repository?
No
# Yesでnpm installも実行
? Would you like us to run npm install?
Yes
# install成功ログ
Using: npm
✔ t3-app-example scaffolded successfully!
Installing packages...
✔ Successfully installed prisma
✔ Successfully installed tailwind
✔ Successfully installed trpc
✔ Successfully installed envVariables
Next steps:
cd t3-app-example
npx prisma db push
npm run dev
次に、上記の Next steps に表示されたコマンドを実行します。
$ cd t3-app-example
$ npx prisma db push
$ npm run dev
http://localhost:3000/ にアクセスすると画面が表示されました。
あとは生成されたコードを修正して、お好きなアプリを作ってみてください!
開発Tips
Prismaで新しいテーブルを作成する
schema.prisma に model を追加して、 npx prisma db push
を実行すると新しいテーブルがDBに作成されます。
model Counter {
id String @id
count Int
}
$ npx prisma db push
Environment variables loaded from .env
Prisma schema loaded from prisma\schema.prisma
Datasource "db": SQLite database "db.sqlite" at "file:./db.sqlite"
Your database is now in sync with your Prisma schema. Done in 42ms
✔ Generated Prisma Client (4.3.1 | library) to .\node_modules\@prisma\client in 111ms
tRPCに新しいクエリを作成する
まずはサーバーサイドから実装します。src/server/router 配下に新しいルーティングを追加します。
今回は Prisma を使用して、アクセスカウンターを実装してみました。
.query("countup", {
async resolve({ ctx }) {
const pv = await ctx.prisma.counter.upsert({
where: { id: 'pv' },
create: { id: 'pv', count: 1 },
update: { count: {increment: 1}},
});
return pv.count;
},
})
次に、クライアントサイドを実装します。作成したクエリを呼び出す処理を追加しました。
const pv = trpc.useQuery(["example.countup"]);
console.log(pv.data);
これでページリロードする度に、pv.data がカウントアップされるようになりました。
Fly.ioにデプロイする
CLI tool(flyctl)のインストール
ローカル環境に合わせてインストールコマンドを実行します。公式サイトをご確認ください。
> iwr https://fly.io/install.ps1 -useb | iex
アカウントにログイン
ログインコマンドを実行するとWebブラウザにログインページが表示されます。
Sign Inに成功すると、以降そのアカウントに対して操作を実行します。
$ fly auth login
Githubアカウントがある方は連携するとすぐにログインできます。
無料枠内で使用する場合には、クレジットカードの登録などは必要ないようです。
デプロイの初期設定
fly launch
を実行して質問に回答します。
回答が終わると Dockerfile と fly.toml(サーバー構成定義)ファイルが出力されます。
$ fly launch
Creating app in t3-app-example
Scanning source code
# 自動でNextJSアプリとして検出されました
Detected a NextJS app
# デプロイするアプリ名を入力します
? App Name (leave blank to use an auto-generated name):
t3-app-example
# リージョンは近い方が良いので東京を選択
? Select region:
nrt (Tokyo, Japan)
# すぐにデプロイするか問われますが、 Dockerfileを編集したいのでNoを選択
? Would you like to deploy now?
No
Your app is ready. Deploy with `flyctl deploy`
作成されたDockerfileを修正します。
- 個人的にnpmを使いたいので、yarnコマンドはコメントアウトして、npmコマンドのコメントアウトを解除
- 環境変数の PORT を fly.toml の internal_port と同じポート番号(8080)に修正
#RUN yarn install --frozen-lockfile
RUN npm ci
#RUN yarn build
RUN npm run build
#ENV PORT 3000
ENV PORT 8080
#CMD ["yarn", "start"]
CMD ["npm", "run", "start"]
デプロイ
fly deploy
コマンドを実行すると、先ほど作成した fly.toml の内容に従ってデプロイされます。
# remoteのbuilderが調子が悪かったので、localでビルドするオプションを追加
$ fly deploy --local-only
管理画面にホスト名が表示されているので、こちらをクリックしてデプロイされたアプリを確認します。
トラブルシューティング
Error failed to fetch an image or build from source: error connecting to docker: failed building options: failed probing "personal": context deadline exceeded
fly deploy
実行時にエラーになったので下記ページを参考に fly wireguard websockets enable
を実行しました。
Error failed to fetch an image or build from source: error connecting to docker: remote builder app unavailable
何度かデプロイしているとリモートの builder が動かなくなりましたが、管理画面から builder を一度削除することで解決しました。また、--local-only
オプションでローカルビルドできたので、途中からはこちらを使用しました。
Failed due to unhealthy allocations - no stable job version to auto revert to and deploying as v1
何度デプロイしてもヘルスチェックでエラーになっていましたが、しばらく時間をおくとヘルスチェックが通るようになりました。原因は不明です…
DBのデータを永続ボリュームに保存する
マニュアルを参考に、永続ボリュームを storage という名前でアプリと同じ東京リージョンに1GBで作成しました。
$ fly volumes create storage --region nrt --size 1
ID: vol_xxxxxxxxxxx
Name: storage
App: t3-app-example
Region: nrt
Zone: d8a3
Size GB: 1
Encrypted: true
Created at: 22 Sep 22 07:30 UTC
続けて fly.toml に永続ボリュームのマウント設定と、 sqlite の保存場所を追記します。
[mounts]
source="storage"
destination="/storage"
[env]
DATABASE_URL="file:/storage/db.sqlite"
アプリ起動時にマイグレーションが走るようにします。
# マイグレーションファイルを作成
$ npx prisma migrate dev
"prestart": "prisma migrate deploy",
再デプロイし、アプリを再起動してもデータが永続化されていることを確認しました。
# 再デプロイ
$ fly deploy --local-only
# 再起動
$ fly restart t3-app-example
おわりに
- 最初デプロイが上手くいかずに困りましたが、最終的にはデプロイできるようになったので良かったです。
- 永続ボリュームに直接アクセスする方法が見つけられなかったので、 sqlite のダウンロードは自分で実装する必要があるかもしれません。
- Prisma, tRPCは初めて触りましたが、使いやすそうでした。
- アクセスが少ないサイトであれば無料で始められそうなので、今後活用していきたいです。
- 今回作成したコードのサンプルです。https://github.com/proyuki02/t3-flyio-sqlite-example