1. 正常な購入は両方が起きる
  2. 名前が空なら売切れの更新も取り消される
  3. すでに売り切れなら何も起きない
  4. 商品が無ければ何も起きない
コース一覧
Node.js入門
購入トランザクション

Node.js入門

Node.jsの実行モデル、npm、Expressによるルーティング、REST API設計、データベース接続、トランザクションを使った購入処理、トークン認証まで、バックエンド開発の基礎を、毎レッスン実際にコードを書き、フリマアプリのAPIを自分の手で作りながら学びます。

1
Node.jsとは
01. Node.jsとは15分
02. スクリプトを実行する15分
03. npmとは15分
04. package.json15分
05. npmスクリプト15分
06. つくる:プロジェクト始動20分
07. 第1章クイズ10分
2
モジュールと非同期の復習
01. import/export15分
02. ファイルを読み書きする15分
03. async/await復習15分
04. JSONの取り回し15分
05. つくる:データ層20分
06. 第2章クイズ10分
3
はじめてのサーバー
01. サーバーとリクエスト15分
02. Expressを導入する15分
03. ルーティング15分
04. JSONを返す15分
05. 開発の回し方15分
06. つくる:サーバー始動20分
07. 第3章クイズ10分
4
REST APIのきほん
01. RESTの考え方15分
02. 一覧を返す15分
03. 1件を返す15分
04. 登録を受ける15分
05. 更新と削除15分
06. ステータスコード15分
07. つくる:商品CRUD完成20分
08. 第4章クイズ10分
5
リクエスト処理と検証
01. クエリパラメータ15分
02. 並び替えとページング15分
03. 入力検証15分
04. ミドルウェア15分
05. エラーハンドリング15分
06. つくる:検索API完成20分
07. 第5章クイズ10分
6
データベース接続
01. DBにつなぐ15分
02. 一覧・詳細をDB化15分
03. 出品をDB化15分
04. SQLインジェクション20分
05. 更新・削除をDB化15分
06. 購入トランザクション20分
07. つくる:DB版fleama20分
08. 第6章クイズ10分
7
認証のきほん
01. 認証と認可15分
02. パスワードハッシュ15分
03. ログインとトークン15分
04. 保護ルート15分
05. 本人のものだけ15分
06. つくる:認証完備20分
07. 第7章クイズ10分
8
総合制作
01. fleama API完成20分
02. Reactと接続する20分
03. サーバーで常駐させる20分
04. 自由拡張20分
05. 完成と次のステップ15分

購入トランザクション

購入は1つの操作ではない

フリマで商品が買われたとき、やることは 2 つあります。

  1. その商品を売り切れにする
  2. 購入の記録を残す

素直に書くと、こうなります。

JavaScript

db.prepare("UPDATE items SET sold = 1 WHERE id = ?").run(id); db.prepare("INSERT INTO purchases (item_id, buyer) VALUES (?, ?)").run(id, buyer);

動きます。ただし1 行目と 2 行目のあいだで何かが起きたらどうなるでしょうか。

途中で止まると壊れる

サーバーが落ちる、電源が切れる、2 行目でエラーになる。何であれ、途中で止まると次の状態が残ります。

プレーンテキスト

商品は売り切れ。だが購入記録は無い。

誰も買っていないのに、誰も買えない商品ができあがります。どちらか片方だけが起きた状態が、いちばん困ります。

全部か、何もしないか

トランザクションは、複数の操作をまとめて 1 つとして扱う仕組みです。

JavaScript

db.exec("BEGIN"); try { db.prepare("UPDATE items SET sold = 1 WHERE id = ?").run(id); db.prepare("INSERT INTO purchases (item_id, buyer) VALUES (?, ?)").run(id, buyer); db.exec("COMMIT"); } catch (error) { db.exec("ROLLBACK"); throw error; }
命令意味
BEGINここからをひとまとまりにする
COMMIT全部を確定する
ROLLBACK全部を無かったことにする

ROLLBACK を呼ぶと、BEGIN の後にした変更がすべて消えます。1 つ目の UPDATE が成功していても戻ります。

途中の状態が残らない。これがトランザクションの値打ちです。

売り切れを先に確かめる

もう 1 つ必要なことがあります。すでに売れている商品は、買えません。

JavaScript

const item = db.prepare("SELECT id, sold FROM items WHERE id = ?").get(id); if (!item) { return res.status(404).json({ error: "商品が見つかりません" }); } if (item.sold === 1) { return res.status(409).json({ error: "すでに売り切れです" }); }

409 は「いまの状態と矛盾する依頼」を表すコードです。商品は存在するので 404 ではなく、依頼の書き方も正しいので 400 でもありません。状態が合わないという失敗です。

手を動かす

演習では、購入の処理をトランザクションで書きます。途中で失敗したときに、何も起きていない状態へ戻せることを確かめてください。

要件

  1. 商品が無ければ {status:"not_found", sold:0, purchases:0} を返す
  2. すでに売り切れなら {status:"conflict", sold:1, purchases:0} を返す
  3. 買う人の名前が空なら ROLLBACK して {status:"invalid", sold:0, purchases:0} を返し、正常なら COMMIT して {status:"ok", sold:1, purchases:1} を返す

入出力例

buy(1, "田中", false) → {"purchases":1,"sold":1,"status":"ok"} buy(1, "", false) → {"purchases":0,"sold":0,"status":"invalid"} buy(1, "田中", true) → {"purchases":0,"sold":1,"status":"conflict"} buy(999, "田中", false) → {"purchases":0,"sold":0,"status":"not_found"}

ヒント

まず売り切れの判定を足します。`item.sold === 1` なら `conflict` を返します

`db.exec("BEGIN")` で始めて、`try` の中に 2 つの書き込みを入れます

名前が空のときは `throw` して `catch` に落とし、そこで `db.exec("ROLLBACK")` を呼びます

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/08/10

関連レッスン

  • つくる:DB版fleama

    全APIをDBで動かし、データ層を丸ごと移行できるようになります。

  • 第6章クイズ

    データベース接続とトランザクションの理解を4択で確認します。

  • 認証と認可

    未ログインで出品APIを叩いて課題を確かめ、認証と認可を区別できるようになります。

  • fleama API完成

    第4章で作った一覧表と突き合わせ、仕様を満たし切れるようになります。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • サーバークライアント(ブラウザなど)がリクエストを送り、サーバーがレスポンスを返す。
  • トランザクション「全部成功 or 全部なかったことに」をまとめる単位
  • 処理計算や代入を表す長方形
main.js
学習モード
エディタを読み込んでいます

メモ

購入トランザクション

⌘S で保存