Deno Deploy 上で動く Web アプリのスターターテンプレートです。 ブラウザから使えるメモ帳が最初から動きます。 中身は Deno.serve の REST API と Postgres で、ここを土台に自分のアプリへ作り替えていきます。
前提知識は Web 中級 (HTML / CSS / JavaScript と fetch が分かる) と、SQL は最低限 (select と
insert が読める) です。 Postgres も Deno Deploy も初めてで構いません。
main.ts サーバの入口。URL とハンドラの対応を書く場所
db.ts DB への接続と query()。基本は触りません
routes/http.ts レスポンスの組み立てと入力チェックの補助
routes/memos.ts メモの CRUD。API を増やすときのお手本
migrations/ テーブル定義の履歴 (SQL)
public/ ブラウザに返す HTML / CSS / JS
compose.yml ローカル開発用の Postgres
deno.json タスクと依存の宣言
CLAUDE.md Claude Code 向けの前提。消さないでください
Deno 以外のインストールは要りません。 依存は deno.json の imports
に書いてあり、初回実行時に自動で取得されます。
flowchart LR
browser[ブラウザ] -->|"/"| static["public/ の静的ファイル"]
browser -->|"/api/*"| routes["routes/ のハンドラ"]
routes --> db["db.ts の接続プール"]
db --> pg[(Postgres)]
static -.- main[main.ts]
routes -.- main
main.ts が受けたリクエストのうち、/api/ で始まるものは routes/ のハンドラへ、それ以外は
public/ の静的ファイルとして返されます。
Docker Desktop または Rancher Desktop が必要です。 用意できない場合は、次の節の「Deploy 上だけで開発する」に進んでください。
-
Postgres を起動します。
docker compose up -d
-
接続情報を環境変数に入れます。 ターミナルを開き直すたびに必要なので、シェルの起動ファイルに書いても構いません。
export PGHOST=localhost PGPORT=5432 PGUSER=app PGPASSWORD=app PGDATABASE=app -
テーブルを作ります。
deno task migrate
-
サーバを起動します。
deno task dev
http://localhost:8000/ を開くとメモ帳が表示されます。 public/
を編集したらブラウザを再読み込みするだけで反映されます。 main.ts や routes/ を編集した場合は
deno task dev が自動で再起動します。
ローカルに Postgres を用意しない選択もできます。 ブランチを push すると、そのブランチ専用の URL と
DB が自動で作られるためです。 git push してから、Deploy の画面に出る Git Branch の URL
を開いて確認します。
反映までに毎回ビルドの待ち時間が入るので、動かせるならローカルの方が速く回せます。
作業は https://console.deno.com で行います。 dash.deno.com
は停止済みの旧サービスなので、間違えて開かないでください。
- console.deno.com を開き、組織 (organization) を作ります。 組織の名前と slug は後から変更できません。
- 組織のページで
+ New Appを押し、このリポジトリを選びます。 一覧に出てこない場合はConfigure GitHub App permissionsから対象リポジトリへのアクセスを許可します。 - ビルドの設定を次のように入れます。
- Install command:
deno install - Build command: 空のまま (このテンプレートにビルド工程はありません)
- Dynamic Entrypoint:
main.ts
- Install command:
- 組織のページの
Provision Databaseから Postgres を作ります。 - 作った DB をこのアプリに割り当てます (assign)。
割り当てると、環境ごとに別々の DB が用意されます。 本番は {app-id}-production、ブランチごとは
{app-id}--{ブランチ名}、プレビューは {app-id}-preview という名前になります。
本番のデータを壊さずにブランチで試せるのはこの仕組みのおかげです。
アプリの設定で Pre-Deploy Command に次を入れます。
deno task migrate
Pre-Deploy Command は、ビルドが終わってから新しいバージョンが公開される直前に、環境ごとに 1
回だけ実行されます。 つまり migrations/ に SQL を足して push すれば、その環境の DB
にだけ自動で適用されます。 手作業で psql をつなぐ必要はありません。
デプロイが終わると、production の URL とブランチごとの URL が Deploy の画面に並びます。 審査に出すのは production の URL です。
まず https://<あなたのURL>/api/health を開いてください。 {"ok":true,"db":"up"}
が返れば、アプリも DB も正常です。 db: "down" なら DB の割り当てか Pre-Deploy Command
の設定を見直します。
このリポジトリのどこにも DB のパスワードは書かれていません。 db.ts
は次のように、接続先を指定せずにプールを作っています。
export const pool = new Pool({ max: 3 });Deno Deploy は PGHOST / PGPORT / PGDATABASE / PGUSER / PGPASSWORD
を環境変数として自動で注入し、npm:pg がそれを読みます。 だから .env
に接続文字列を書く手順は不要です。
逆に new Pool({ host: "...", password: "..." }) と書いてしまうと、本番とブランチが同じ DB
を指すようになり、ブランチでの実験が本番のデータを壊します。 パスワードが GitHub
に残る問題もあります。 接続情報はコードに書かず、環境変数に任せてください。
GET /api/health: DB に届いているかを返しますGET /api/memos: 一覧を新しい順に最大 100 件返しますGET /api/memos/:id: 1 件返しますPOST /api/memos: 作成します。ボディは{"title": "必須", "body": "省略可"}PATCH /api/memos/:id: 更新します。渡したキーだけ書き換わりますDELETE /api/memos/:id: 削除します
エラーは {"error": "理由"} の形で返ります。 ステータスコードは、入力の誤りが 400、対象が無ければ
404、サーバ側の不具合が 500 です。
curl で試すときはこうします。
curl http://localhost:8000/api/memos
curl -X POST -H 'content-type: application/json' \
-d '{"title":"買い物","body":"牛乳"}' \
http://localhost:8000/api/memosメモ帳を消して、自分のアプリにしてください。 順番はこの通りが楽です。
- テーブルを決めます。
deno task migrate:new create-postsでmigrations/に SQL ファイルが作られるので、-- Up Migrationの下にcreate tableを書きます。-- Down Migrationの下には元に戻す SQL を書きます。 一度 push したファイルは編集せず、変更は新しいファイルを足して表現します。 routes/memos.tsをコピーして、テーブル名と列名を置き換えます。Route[]を export してmain.tsのroutesに並べれば、それで API が増えます。public/を書き換えて画面を作ります。public/app.jsは fetch で API を叩いているだけなので、React などを使わずにここから広げられます。- 使わなくなった
memosは、migrations/にdrop tableのマイグレーションを足して消します。
テーブルは 1 つか 2 つに収めるのがおすすめです。 1 週間で審査まで持っていくには、機能を絞って動くところまで作る方が有利です。
/api/health が db: "down" を返す。 DB がアプリに割り当てられていない可能性が高いです。 Deploy
の画面で DB の assign を確認してください。
テーブルが無いと言われる (relation "memos" does not exist)。 マイグレーションが走っていません。
ローカルなら deno task migrate、Deploy なら Pre-Deploy Command の設定を確認します。
ローカルで接続を拒否される。 docker compose ps でコンテナが up になっているか、export PGHOST=...
を実行したターミナルで作業しているかを確認します。
デプロイが warmup フェーズでタイムアウトする。 deno.land/std の古い serve() を使うと起こります。
Deno.serve() を使ってください。
原因が分からないとき。 Deploy の画面のログか、deno deploy logs でサーバ側のログを見ます。
console.error の出力はここに出ます。
- Deno Deploy のドキュメント: https://docs.deno.com/deploy/
deno deployコマンド: https://docs.deno.com/runtime/reference/cli/deploy.md- node-pg-migrate: https://salsita.github.io/node-pg-migrate/
- node-postgres (
npm:pg): https://node-postgres.com/