コース一覧
    Node.jsバックエンド実践
    購入トランザクション

    Node.jsバックエンド実践

    入力検証、ミドルウェア、エラー処理、データベース接続を学び、実用的なバックエンドAPIを安全に組み立てられるようにします。

    1
    リクエスト処理と検証
    01. クエリパラメータ15分
    02. 並び替えとページング15分
    03. 入力検証15分
    04. ミドルウェア15分
    05. エラーハンドリング15分
    06. つくる:検索API完成20分
    07. 第5章クイズ10分
    2
    データベース接続
    01. DBにつなぐ15分
    02. 一覧・詳細をDB化15分
    03. 出品をDB化15分
    04. SQLインジェクション20分
    05. 更新・削除をDB化15分
    06. 購入トランザクション20分
    07. つくる:DB版fleama20分
    08. 第6章クイズ10分

    購入トランザクション

    購入は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} を返す

    ヒント

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

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

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

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

    関連レッスン

    • つくる:DB版fleama

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

    • 第6章クイズ

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

    • 更新・削除をDB化

      編集と取下げをDB化し、変更系を移行できるようになります。

    • SQLインジェクション

      文字列連結の危険を再現してから直し、プレースホルダが必要な理由を説明できるようになります。

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

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

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

    メモ

    購入トランザクション

    ⌘S で保存