【第25回】消えないデータの置き場所を持つ|データベースの基礎(嫁子が教えるTS講座)

今回のゴール

前回(APIを作る)まで、データはコードに直接書いていました。今回は消えない置き場所の話です。全体の流れは講座の目次へ。


嫁子: 今日の内容はね、第13回でやった type とほとんど同じ話なの。

データの形を先に決めて、それに合わないものは受け付けない。置き場所がコードの中からファイルに変わっただけだと思って読んでみて。


1. なぜファイルに保存しないのか

「データを保存したいだけなら、JSONファイルに書けばいいのでは」と思うかもしれません。実際、1人で使うメモ程度ならそれで足ります。

困るのは、次のような場面です。

やりたいこと ファイルだと データベースだと
1件だけ探す 全部読んで探す 探す仕組みが最初からある
2人が同時に書き込む 片方の変更が消える 順番に処理される
途中で電源が落ちた ファイルが壊れることがある 中途半端な状態で残らない
項目を1つ増やす 全データを書き換える 定義だけ変える

同時に書き込まれたときの事故が、いちばん大きな理由です。夫婦2人で同じ買い物メモを開いているだけでも起こりえます。

嫁子: 1人用の小さいツールなら、正直ファイルでもいいと思うよ。

でも「あとで人に使ってもらうかも」が少しでもあるなら、最初からデータベースにしたほうが安いの。後から移すほうが面倒だからね。


2. テーブル・行・列

データベースの中身は、表計算ソフトの表とほぼ同じ形をしています。

id name done createdAt
1 牛乳 false 2026-09-11 08:00
2 卵 true 2026-09-11 08:01

第5回で作った「オブジェクトの配列」を思い出してください。あれがそのままテーブルになります。1つのオブジェクトが1行、オブジェクトのキーが列です。

列には型がある

列ごとに「何が入るか」を決めます。ここも第12回の型注釈と同じです。

データベースの型 TypeScriptだと 例
TEXT / VARCHAR string 品名
INTEGER number 個数
BOOLEAN boolean 買ったかどうか
DATETIME Date 登録日時

型を決めておくと、違うものを入れようとしたときに拒否されます。第11回で「型は実行前に止める」と書きましたが、データベースの型は実行時に止める役目を持ちます。守る場所が違うだけで、考え方は同じです。

主キー(id)

どの行も、他と重ならない番号を1つ持ちます。これが主キーで、たいてい id という名前です。

第19回で key に index を使うなと書きましたが、あの話がここにつながります。id は最初から行に付いているので、画面側でも安心して key に使えます。


3. SQLは4つ読めれば足りる

データベースへの命令はSQLという言葉で書きます。この講座では、書けるようになる必要はありません。AIが書いたものを読んで、何をしているか分かれば十分です。

覚えるのは4つだけです。

-- 読む
SELECT * FROM items WHERE done = false;

-- 足す
INSERT INTO items (name, done) VALUES ('牛乳', false);

-- 変える
UPDATE items SET done = true WHERE id = 1;

-- 消す
DELETE FROM items WHERE id = 1;
命令 意味 JavaScriptで言うと
SELECT 読む filter
INSERT 足す push
UPDATE 変える 該当要素の書き換え
DELETE 消す splice

WHERE が「どれに対して」の指定です。ここを書き忘れると全部が対象になります。

嫁子: WHERE の付け忘れは、この世界でいちばん有名な事故なの。

DELETE FROM items; って打つと、全部消えるからね。しかも第3回のGitでは戻せないの。データベースの中身はGitの管理外だから。

AIが書いたSQLを見るときは、UPDATE と DELETE に WHERE があるかだけ確認して。それだけで最悪の事故は防げるよ。


4. 開発と本番で、違うものを使う

データベースにはいくつも種類があります。この講座で関係するのは2つです。

SQLite PostgreSQL
実体 ファイル1つ サーバーとして動く
準備 何も要らない 用意が必要
向いている場面 手元での開発、小さなツール 本番、複数人が同時に使う

手元ではSQLite、本番ではPostgreSQLという組み合わせが定番です。SQLiteはファイルが1つできるだけなので、登録も設定も要りません。

次回のPrismaを使うと、この2つをほぼ同じコードで扱えます。切り替えは設定1行です。

嫁子: だから練習はSQLiteで十分だよ。本番用のサービスに登録するのは、公開すると決めてからでいいの。

先に登録してしまうと、使ってないのに料金が発生したりするからね。必要になってから、が基本。


5. スキーマを先に決める

テーブルの定義(どんな列があるか)をスキーマと呼びます。

ここが第13回で言った「型を先に書いてAIに渡す」とまったく同じ構造になります。

先にこう決めておきます。

items テーブル
  id        INTEGER  主キー・自動採番
  name      TEXT     必須
  done      BOOLEAN  既定 false
  createdAt DATETIME 既定 現在時刻

そのうえで「この形で作って」と頼みます。決めずに丸投げすると、AIが勝手に列を足したり、名前を変えたりします。

あとから列を変えるのは、コードを直すより面倒です。すでに入っているデータをどうするか、という問題が毎回ついてくるからです。

決めておくこと 決めないとどうなるか
列の名前と型 表記がばらつく(name と title が混在)
必須かどうか 空のまま登録できてしまう
既定値 毎回書かないといけない

嫁子: スキーマは、後で変えるのがいちばん高くつく決定なの。だから最初に時間を使う価値があるよ。

逆に言うと、画面の見た目は後でいくらでも変えられるでしょ。順番としては、データの形を決めてから画面を作るほうが手戻りが少ないよ。


つまずきポイント

データベースの中身はGitに入りません。コードだけ戻しても、データは戻りません。消したものは消えたままです。

「とりあえず全部TEXTにする」をやらないでください。日付や数値までTEXTにすると、並び替えや計算ができなくなります。

テーブル名は複数形が慣習です。items、users のように書きます。決まりではありませんが、揃っていると読みやすくなります。


今回のまとめ


次にやること

次回はPrismaです。SQLをほとんど書かずに、TypeScriptからデータベースを読み書きします。今日決めたスキーマが、そのままコードになります。

今回の宿題:買い物メモに「値段」を足すとしたら、列の名前と型をどう決めるか書いてみてください。price INTEGER なのか price TEXT なのか、必須かどうか。次回、それをそのままPrismaの書き方に写します。

嫁子: 紙に書くだけでいいよ。3行で終わるからね。

でもこの3行を決めずに進むと、あとで全部やり直しになるの。今日いちばん大事なのはそこだと思う。