ウェブサイト検索

Ubuntu 22.04でNode.jsとSQLiteを使用する方法


著者はWrite for DOnationsプログラムの一環として、寄付を受け取るために/dev/colorを選択しました。

紹介

SQLiteはデータを保存するための人気のあるオープンソースのSQLデータベースエンジンです。サーバーレスであるため、実行するためにサーバーを必要とせず、代わりにコンピュータのディスク上のファイルにデータを読み書きします。また、SQLiteは設定を必要としないため、携帯性が高く、組み込みシステム、デスクトップ/モバイルアプリ、プロトタイピングなど、さまざまな用途に人気のある選択肢となっています。

Node.jsでSQLiteを使用するには、SQLiteデータベースに接続し、アプリケーションからデータベースにSQL文を送信して実行するためのデータベースクライアントが必要です。人気の選択肢の一つは、SQLite 3の非同期バインディングを提供するnode-sqlite3パッケージです。

このチュートリアルでは、node-sqlite3を使用してSQLiteデータベースへの接続を作成します。次に、テーブルを作成し、データベースにデータを挿入するNode.jsアプリを作成します。最後に、データベースからデータを取得、更新、および削除するためにnode-sqlite3を使用するようにアプリを修正します。

前提条件

このチュートリアルに従うためには、次のものが必要です:

  • あなたのシステムにNode.jsの開発環境をセットアップします。Ubuntu 22.04を使用している場合は、チュートリアル「Ubuntu 22.04にNode.jsをインストールする方法」のオプション3に従って、最新バージョンのNode.jsをインストールしてください。他のシステムの場合は、チュートリアルシリーズ「Node.jsをインストールしてローカル開発環境を作成する方法」を参照してください。

  • あなたの開発環境にSQLite3がインストールされています。私たちのチュートリアル「Ubuntu 20.04でSQLiteをインストールして使用する方法」のステップ1に従ってください。

  • テーブルを作成し、テーブル内のデータを取得および変更するためのSQLクエリを書く基本的な知識。私たちのチュートリアル「Ubuntu 20.04にSQLiteをインストールして使用する方法」のステップ2から6を参照してください。

  • Node.jsプログラムの書き方に慣れていること、これは私たちのチュートリアル「Node.jsで最初のプログラムを書いて実行する方法」にあります。

ステップ 1 — プロジェクトディレクトリの設定

このステップでは、プロジェクトディレクトリを作成し、依存関係としてnode-sqlite3をダウンロードします。

まず、mkdirコマンドを使用してディレクトリを作成します。このチュートリアルのためにsqlite_demoと呼ばれていますが、お好きな名前に置き換えることができます:

  1. mkdir sqlite_demo

次に、cdコマンドを使用して新しく作成したディレクトリに移動します。

  1. cd sqlite_demo

プロジェクトディレクトリをnpmコマンドを使ってnpmパッケージとして初期化します:

  1. npm init -y

このコマンドは、プロジェクトの重要なメタデータを保持する package.json ファイルを作成します。 -y オプションは、npm にすべてのデフォルトを受け入れるよう指示します。

コマンドを実行した後、以下の出力が画面に表示されます:

Wrote to /home/sammy/sqlite_demo/package.json:

{
  "name": "sqlite_demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

出力は、重要なメタデータを記録するプロパティを含む package.json ファイルが作成されたことを示しています。重要なオプションのいくつかは次のとおりです:

  • 名前: あなたのプロジェクトの名前。
  • バージョン: あなたのプロジェクトのバージョン。
  • main: プロジェクトのスタート地点。

デフォルトのオプションはそのままにしておいても構いませんが、好みに合わせてプロパティの値を変更しても問題ありません。プロパティの詳細については、npmのpackage.jsonドキュメントを参照してください。

次に、npm installを使ってnode-sqlite3パッケージをインストールします:

  1. npm install sqlite3

パッケージをインストールすると、出力は以下のように表示されます:

added 104 packages, and audited 105 packages in 9s

5 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

既にnode-sqlite3をインストールしたので、次のセクションではそれを使用してSQLiteデータベースに接続します。

ステップ2 — SQLiteデータベースへの接続

このステップでは、node-sqlite3を使用して、異なるサメとその属性を含むSQLiteデータベースを作成し、Node.jsプログラムに接続します。データベース接続を確立するために、node-sqlite3パッケージはDatabaseクラスを提供します。このクラスはインスタンス化されると、お使いのコンピュータのディスクにSQLiteデータベースファイルを作成し、それに接続します。接続が確立されると、アプリケーション用のテーブルを作成します。これは後のセクションでデータを挿入、取得、または更新するために使用します。

nano またはお好みのテキストエディタを使用して、db.js ファイルを作成して開きます:

  1. nano db.js

db.jsファイルに、SQLiteデータベースとの接続を確立するための以下のコードを追加してください:

const sqlite3 = require("sqlite3").verbose();
const filepath = "./fish.db";

function createDbConnection() {
  const db = new sqlite3.Database(filepath, (error) => {
    if (error) {
      return console.error(error.message);
    }
  });
  console.log("Connection with SQLite has been established");
  return db;
}

最初の行では、node-sqlite3モジュールをプログラムファイルにインポートします。2行目では、filepath変数を、SQLiteデータベースが存在するパスとデータベースファイルの名前(今回はfish.db)で設定します。

次の行では、SQLite データベースとの接続を確立する createDbConnection() 関数を定義します。関数内では、new キーワードを使って sqlite3.Database() クラスのインスタンスを作成します。このクラスは、filepath とコールバックの2つの引数を取ります。

最初の引数であるfilepathは、SQLiteデータベースへの名前とパスを受け入れます。ここでは./fish.dbです。2番目の引数は、データベースが作成され、データベースへの接続が確立された後に実行されるコールバックです。このコールバックは、データベース接続を確立しようとしたときにエラーが発生した場合にerrorオブジェクトに設定されるerrorパラメータを受け取ります。コールバック内では、if文を使用してエラーがあるかどうかを確認します。条件が真であれば、console.error()メソッドを使用してエラーメッセージをログに記録します。

さて、sqlite3.Database()クラスを使用してインスタンスを作成すると、プロジェクトディレクトリにSQLiteデータベースファイルが作成され、db変数に格納されるデータベースオブジェクトが返されます。このデータベースオブジェクトは、テーブルを作成し、データを挿入、取得、または変更するSQLステートメントを渡すために使用できるメソッドを提供します。

最後に、成功メッセージをログに記録するためにconsole.log()を呼び出し、db変数内のデータベースオブジェクトを返します。

次に、強調表示されたコードを追加して、テーブルを作成する関数を作成します:

...
function createDbConnection() {
    ...
}

function createTable(db) {
  db.exec(`
  CREATE TABLE sharks
  (
    ID INTEGER PRIMARY KEY AUTOINCREMENT,
    name   VARCHAR(50) NOT NULL,
    color   VARCHAR(50) NOT NULL,
    weight INTEGER NOT NULL
  );
`);
}

createTable() 関数は SQLite データベースにテーブルを作成します。これは、データベースオブジェクト db をパラメータとして受け取ります。createTable() 関数内では、与えられた SQL ステートメントをデータベースに送信して実行させるために、db データベースオブジェクトの exec() メソッドを呼び出します。exec() メソッドは、結果行を返さないクエリにのみ使用されます。

SQLステートメントCREATE TABLE sharks...は、exec()メソッドに渡され、次のフィールドを持つテーブルsharksを作成します:

  • ID: INTEGERデータ型の値を格納します。PRIMARY KEY制約は、その列を主キーとして指定し、AUTOINCREMENTはSQLiteに対して、テーブル内の各行のID列の値を自動的にインクリメントするよう指示します。
  • name: サメの名前を最大50文字のVARCHARデータ型を使用して詳細に記述します。NOT NULL制約により、このフィールドはNULL値を格納できません。
  • color: VARCHARデータ型を用いて、最大50文字までのサメの色を表します。NOT NULL制約は、このフィールドがNULL値を受け入れないことを意味します。
  • weight: 鮫の体重をキログラム単位で保存し、INTEGERデータ型を使用し、NOT NULL制約を使ってNULL値を許可しないことを保証します。

同じdb.jsファイルに、createTable()関数を呼び出すために強調表示されたコードを追加してください:

function createDbConnection() {
  const db = new sqlite3.Database(filepath, (error) => {
    if (error) {
      return console.error(error.message);
    }
    createTable(db);
  });
  console.log("Connection with SQLite has been established");
  return db;
}


function createTable(db) {
    ...
}

コールバックが実行されるとき、dbデータベースオブジェクトを引数としてcreateTable()関数を呼び出します。

次に、createDbConnection()関数を呼び出すために次の行を追加します:

...
function createDbConnection() {
    ...
}


function createTable(db) {
    ...
}

module.exports = createDbConnection();

前述のコードでは、createDbConnection()関数を呼び出し、データベースへの接続を確立し、データベースオブジェクトを返します。その後、module.exportsを使用してデータベースオブジェクトをエクスポートし、他のファイルで参照できるようにします。

あなたのファイルには次の内容が含まれます:

const sqlite3 = require("sqlite3").verbose();
const filepath = "./fish.db";

function createDbConnection() {
  const db = new sqlite3.Database(filepath, (error) => {
    if (error) {
      return console.error(error.message);
    }
    createTable(db);
  });
  console.log("Connection with SQLite has been established");
  return db;
}

function createTable(db) {
  db.exec(`
  CREATE TABLE sharks
  (
    ID INTEGER PRIMARY KEY AUTOINCREMENT,
    name   VARCHAR(50) NOT NULL,
    color   VARCHAR(50) NOT NULL,
    weight INTEGER NOT NULL
  );
`);
}

module.exports = createDbConnection();

ファイルを保存して終了してください。nanoを使用している場合は、CTRL+Xを押して終了し、yを押して変更を保存し、ENTERを押してファイル名を確認します。

nodeコマンドを使用してdb.jsファイルを実行してください。

  1. node db.js

出力は、データベース接続が正常に確立されたことを示します:

Connection with SQLite has been established

次に、ls コマンドを使用して fish.db データベースファイルが作成されたかどうかを確認してください:

  1. ls
db.js  fish.db  node_modules  package-lock.json  package.json

fish.dbデータベースファイルの出力に表示されることは、データベースが正常に作成されたことを確認します。

今、db.js ファイルを実行するたびに、createTable() 関数が呼び出されてデータベースにテーブルが作成されます。既に存在するテーブルを作成しようとすると、SQLiteはエラーを発生させます。これを確認するには、node コマンドで db.js ファイルを再実行します:

  1. node db.js

今回は、以下の出力に示されているように、エラーが表示されます:

Connection with SQLite has been established
undefined:0


[Error: SQLITE_ERROR: table sharks already exists
Emitted 'error' event on Database instance at:
] {
  errno: 1,
  code: 'SQLITE_ERROR'
}

Node.js v17.6.0

エラーメッセージは、sharks テーブルがすでに存在することを示しています。これは、node コマンドを初めて実行したときに、fish データベースと sharks テーブルが作成されたためです。コマンドを再実行すると、createTable() 関数が2回目に実行されるため、テーブルがすでに存在しているためにエラーが発生します。

このエラーは、他のファイルでデータベースオブジェクトのメソッドを使用してデータベースを操作しようとするたびに発生します。例えば、次のステップでは、データをデータベースに挿入するファイルを作成します。データベースオブジェクトを使用するには、db.jsファイルをインポートし、データベースにデータを挿入するための関連メソッドを呼び出します。ファイルを実行すると、db.jsも実行され、同じエラーが発生します。

これを解決するために、fs モジュールの existsSync() メソッドを使用して、プロジェクトディレクトリ内のデータベースファイル fish.db の存在を確認します。データベースファイルが存在する場合、createTable() 関数を呼び出さずにデータベースへの接続を確立します。存在しない場合は、接続を確立し、createTable() 関数を呼び出します。

これを行うには、もう一度エディタでdb.jsを開いてください:

  1. nano db.js

db.jsファイルに、データベースファイルの存在を確認するためのハイライトされたコードを追加します:

const fs = require("fs");
const sqlite3 = require("sqlite3").verbose();
const filepath = "./fish.db";

function createDbConnection() {
  if (fs.existsSync(filepath)) {
    return new sqlite3.Database(filepath);
  } else {
    const db = new sqlite3.Database(filepath, (error) => {
      if (error) {
        return console.error(error.message);
      }
      createTable(db);
    });
    console.log("Connection with SQLite has been established");
    return db;
  }
}

まず、ファイルシステムと対話するために使用されるfsモジュールをインポートします。次に、追加されたif文では、fs.existSync()メソッドを呼び出して、指定された引数にあるファイルの存在を確認します。ここでは、データベースファイル./fish.dbです。ファイルが存在する場合は、データベースファイルパスでsqlite3.Database()を呼び出し、コールバックを省略します。ただし、ファイルが存在しない場合は、データベースインスタンスを作成し、コールバック内でcreateTable()関数を呼び出して、データベースにテーブルを作成します。

この時点で、完全なファイルは以下のように表示されます:

const fs = require("fs");
const sqlite3 = require("sqlite3").verbose();
const filepath = "./fish.db";

function createDbConnection() {
  if (fs.existsSync(filepath)) {
    return new sqlite3.Database(filepath);
  } else {
    const db = new sqlite3.Database(filepath, (error) => {
      if (error) {
        return console.error(error.message);
      }
      createTable(db);
    });
    console.log("Connection with SQLite has been established");
    return db;
  }
}

function createTable(db) {
  db.exec(`
  CREATE TABLE sharks
  (
    ID INTEGER PRIMARY KEY AUTOINCREMENT,
    name   VARCHAR(50) NOT NULL,
    color   VARCHAR(50) NOT NULL,
    weight INTEGER NOT NULL
  );
`);
}

module.exports = createDbConnection();

変更が完了したら、ファイルを保存して閉じてください。

db.jsファイルが何度も実行されてもエラーを出さないようにするためには、rmコマンドでfish.dbファイルを削除して最初からやり直します:

  1. rm fish.db

db.jsファイルを実行します:

  1. node db.js
Connection with SQLite has been established

さて、db.jsがデータベースに接続し、db.jsファイルを再実行してもテーブルを再作成しないことを確認します:

  1. node db.js

これで、もはやエラーが出ないことに気づくでしょう。

SQLiteデータベースへの接続を確立し、テーブルを作成したので、データベースにデータを挿入します。

ステップ3 — SQLiteデータベースへのデータの挿入

このステップでは、node-sqlite3モジュールを使用して、SQLiteデータベースにデータを挿入する関数を作成します。挿入したいデータをプログラムへのコマンドライン引数として渡します。

insertData.jsファイルを作成し、テキストエディタで開いてください:

  1. nano insertData.js

あなたのinsertData.jsファイルに、コマンドライン引数を取得するための以下のコードを追加してください:

const db = require("./db");

function insertRow() {
  const [name, color, weight] = process.argv.slice(2);
}

最初の行では、前のステップで db.js ファイルにエクスポートしたデータベースオブジェクトをインポートします。2行目では、テーブルにデータを挿入するためにすぐに使用する insertRow() 関数を定義します。この関数では、process.argv がコマンドライン引数を配列として返します。インデックス 0 の最初の要素は Node へのパスを含み、インデックス 1 の2番目の要素は JavaScript プログラムのファイル名を格納します。それ以降の要素は、インデックス 2 から始まり、ファイルに渡したコマンドライン引数を含みます。最初の2つの引数をスキップするには、JavaScript の slice() メソッドを使用して配列の浅いコピーを作成し、インデックス 2 から配列の最後までの要素を返します。

次に、データベースにデータを挿入するために、強調表示されたコードを追加します:

const db = require("./db");

function insertRow() {
  const [name, color, weight] = process.argv.slice(2);
  db.run(
    `INSERT INTO sharks (name, color, weight) VALUES (?, ?, ?)`,
    [name, color, weight],
    function (error) {
      if (error) {
        console.error(error.message);
      }
      console.log(`Inserted a row with the ID: ${this.lastID}`);
    }
  );
}

前述のコードでは、db.run() メソッドを呼び出しています。このメソッドは、SQL文、配列、コールバックという三つの引数を受け取ります。最初の引数である INSERT INTO sharks... は、データベースにデータを挿入するためのSQL文です。INSERT 文の中の VALUES ステートメントは、挿入する必要がある値のカンマ区切りリストを取ります。ここで、値を直接渡すのではなく、? プレースホルダーを渡していることに注意してください。これは、SQLインジェクション攻撃を避けるためです。実行時に、SQLiteは自動的にプレースホルダーを db.run() メソッドの第二引数であるコマンドライン引数の値を含む配列で渡された値に置き換えます。

最後に、db.run()メソッドの第3引数は、データがテーブルに正常に挿入されたときに実行されるコールバックです。エラーが発生した場合、エラーメッセージはコンソールにログされます。挿入が成功した場合、this.lastIDが返した新しく挿入された行のIDとともに成功メッセージをログに記録します。

では、ハイライトされた行を追加してinsertRow()関数を呼び出します:

const db = require("./db");

function insertRow() {
  const [name, color, weight] = process.argv.slice(2);
  db.run(
    `INSERT INTO sharks (name, color, weight) VALUES (?, ?, ?)`,
    [name, color, weight],
    function (error) {
      if (error) {
        console.error(error.message);
      }
      console.log(`Inserted a row with the ID: ${this.lastID}`);
    }
  );
}

insertRow();

ファイルを保存して閉じたら、サメの名前、色、および重量の引数を使ってファイルを実行します:

  1. node insertData.js sammy blue 1900

出力は、行が主ID 1 とともにテーブルに挿入されたことを示しています。

Inserted a row with the ID: 1

異なる引数でコマンドを再実行してください:

  1. node insertData.js max white 2100
Inserted a row with the ID: 2

前のコマンドを実行すると、sharks テーブルに2行が作成されます。

SQLiteデータベースにデータを挿入できるようになったので、次にデータベースからデータを取得します。

ステップ4 — SQLiteデータベースからデータを取得する

このステップでは、node-sqlite3モジュールを使用して、SQLiteデータベース内のsharksテーブルに保存されているすべてのデータを取得し、コンソールにログします。

最初に、listData.jsファイルを開きます:

  1. nano listData.js

listData.jsファイルに、すべての行を取得するための次のコードを追加してください:

const db = require("./db");

function selectRows() {
  db.each(`SELECT * FROM sharks`, (error, row) => {
    if (error) {
      throw new Error(error.message);
    }
    console.log(row);
  });
}

まず、db.jsファイルにデータベースオブジェクトをインポートします。次に、SQLiteデータベース内のすべての行を取得するselectRows()関数を定義します。関数内では、データベースオブジェクトdbのeach()メソッドを使用して、データベースから行を1つずつ取得します。each()メソッドは、SQL文とコールバックの2つの引数を取ります。

最初の引数SELECTはsharksテーブルのすべての行を返します。二番目の引数は、データベースから行が取得されるたびに実行されるコールバックです。そのコールバック内で、エラーをチェックします。エラーがある場合は、throw文を使用してカスタムエラーを作成します。取得中にエラーが発生しなかった場合は、データがコンソールにログ記録されます。

では、ハイライトされたコードを追加して selectRows() 関数を呼び出します:

const db = require("./db");

function selectRows() {
  db.each(`SELECT * FROM sharks`, (error, row) => {
    if (error) {
      throw new Error(error.message);
    }
    console.log(row);
  });
}

selectRows();

ファイルを保存して終了し、その後ファイルを実行します:

  1. node listData.js

コマンドを実行すると、次のように表示される出力に気付くでしょう。

{ ID: 1, name: 'sammy', color: 'blue', weight: 1900 }
{ ID: 2, name: 'max', color: 'white', weight: 2100 }

出力は、前のステップで sharks テーブルに挿入したすべての行を表示します。 node-sqlite3 モジュールは、それぞれの SQL 結果を JavaScript オブジェクトに変換します。

SQLiteデータベースからデータを取得できるようになったので、SQLiteデータベースのデータを更新します。

ステップ5 — SQLiteデータベース内のデータの修正

このステップでは、node-sqlite3モジュールを使用してSQLiteデータベースの行を更新します。そのためには、変更したい行のプライマリIDと、その行を更新するための値を含むコマンドライン引数をプログラムに渡します。

updateData.jsファイルを作成し、テキストエディタで開いてください:

  1. nano updateData.js

updateData.jsファイルに、レコードを更新するための以下のコードを追加してください:

const db = require("./db");

function updateRow() {
  const [id, name] = process.argv.slice(2);
  db.run(
    `UPDATE sharks SET name = ? WHERE id = ?`,
    [name, id],
    function (error) {
      if (error) {
        console.error(error.message);
      }
      console.log(`Row ${id} has been updated`);
    }
  );
}

updateRow();

最初に、db.jsファイルからデータベースオブジェクトをインポートします。次に、データベースの行を更新するupdateRow関数を定義します。関数内では、コマンドライン引数をidとname変数に展開します。id変数には、更新したい行の主キーIDが含まれ、nameには名前フィールドが反映する値が含まれます。

次に、db.run() 関数を次の引数で呼び出します:SQL 文とコールバック。UPDATE SQL 文は、name 列の現在の値を name 変数に渡された値に変更します。WHERE 句は、id 変数の中にあるIDを持つ行のみが更新されることを保証します。db.run() メソッドは、値が更新された後に実行されるコールバックという2番目の引数を取ります。

最後に、updateRow() 関数を呼び出します。

変更が完了したら、ファイルを保存して閉じてください。

idを変更したい行のupdateData.jsファイルを実行し、新しいnameを入力してください:

  1. node updateData.js 2 sonny
Row 2 has been updated

名前が変更されたことを確認してください。

  1. node listData.js

コマンドを実行すると、出力は以下のようになります:

{ ID: 1, name: 'sammy', color: 'blue', weight: 1900 }
{ ID: 2, name: 'sonny', color: 'white', weight: 2100 }

出力は、ID 2 の行がその name フィールドの値として sonny を持っていることを示しています。

これで、データベースの行を更新できるようになりました。次に、SQLiteデータベースからデータを削除します。

ステップ6 — SQLiteデータベース内のデータを削除する

このセクションでは、node-sqlite3を使用して、SQLiteデータベースのテーブルから行を選択し削除します。

deleteData.jsファイルを作成し、テキストエディタで開いてください:

  1. nano deleteData.js

deleteData.jsファイルに、データベースの行を削除するための以下のコードを追加してください:

const db = require("./db");

async function deleteRow() {
  const [id] = process.argv.slice(2);
  db.run(`DELETE FROM sharks WHERE id = ?`, [id], function (error) {
    if (error) {
      return console.error(error.message);
    }
    console.log(`Row with the ID ${id} has been deleted`);
  });
}

deleteRow();

まず、db.jsファイルにデータベースオブジェクトをインポートします。次に、テーブルsharksの行を削除するdeleteRow()を定義します。関数内で、主キーIDをアンパックし、それをid変数に格納します。次に、db.run()を呼び出します。これは2つの引数を取ります。最初の引数は、テーブルsharksの行を削除するSQL文DELETE from sharks...です。WHERE句は、id変数のIDを持つ行のみが削除されることを保証します。2番目の引数は、行が削除された後に実行されるコールバックです。成功した場合、関数は成功メッセージをログに記録します。それ以外の場合は、エラーをコンソールにログに記録します。

最後に、deleteRow() 関数を呼び出します。

ファイルを保存して閉じてから、次のコマンドを実行してください:

  1. node deleteData.js 2
Row with the ID 2 has been deleted

次に、行が削除されたことを確認してください。

  1. node listData.js

コマンドを実行すると、出力は以下のように表示されます:

{ ID: 1, name: 'sammy', color: 'blue', weight: 1900 }

ID 2の行は結果にもう表示されていません。これは、その行が削除されたことを確認します。

これで、node-sqlite3 モジュールを使用してSQLiteデータベースの行を削除できるようになりました。

結論

この記事では、node-sqlite3モジュールを使用してSQLiteデータベースに接続し、テーブルを作成するNode.jsアプリを作成しました。次に、アプリを修正してデータベースにデータを挿入、取得、更新できるようにしました。最後に、データベースのデータを削除するためにアプリを修正しました。

node-sqlite3メソッドの詳細については、node-sqlite3のWikiページを訪問してください。SQLiteについて学ぶには、SQLiteのドキュメントを参照してください。他のSQLデータベースとSQLiteの比較を知りたい場合は、私たちのチュートリアル「SQLite vs MySQL vs PostgreSQL: リレーショナルデータベース管理システムの比較」をご覧ください。Node.jsの旅を続けるためには、「Node.jsでのコーディング方法」シリーズを訪問してください。

関連記事: