JavaScript >> Javascript 文檔 >  >> Node.js

如何在 Node.js 中使用 PostgreSQL

如何在 Node.js 中建立與 PostgreSQL 的池連接以及通過該池運行連接的便利功能。

開始使用

因為我們為本教程編寫的代碼是“獨立的”(意味著它不是更大的應用程序或項目的一部分),所以我們將從頭開始創建一個 Node.js 項目。如果您的計算機上尚未安裝 Node.js,請先閱讀本教程,然後再返回此處。

在計算機上安裝 Node.js 後,從計算機上的項目文件夾(例如,~/projects ),為我們的工作創建一個新文件夾:

終端

mkdir postgresql

接下來,cd 進入該目錄並創建一個 index.js 文件(這是我們編寫教程代碼的地方):

終端

cd postgresql && touch index.js

接下來,我們要安裝兩個依賴,pgexpress

終端

npm i pg express

第一個將讓我們訪問 PostgreSQL 的 Node.js 驅動程序(我們將在代碼中使用它來連接到數據庫),第二個 Express 將用於啟動演示服務器。

最後一步:在 package.json 為您創建的文件,請確保添加字段 "type": "module" 作為財產。這將啟用 ESModules 支持並允許我們使用 import 如下代碼所示。

有了這些,我們就可以開始了。

安裝和配置 PostgreSQL

在我們開始編寫代碼之前,我們需要確保您的機器上安裝了 PostgreSQL PostgreSQL 已正確添加到命令行的 PATH 變量(這會為您的計算機上的文件夾創建快捷方式,並使它們可以從命令行中的任何位置/目錄訪問)。

最好的起點是 PostgreSQL 下載頁面。在此處選擇您的操作系統,然後在下一頁上找到靠近頁面頂部的紅色“下載安裝程序”鏈接。

下載安裝程序後,運行它並完成屏幕上的步驟。確保安裝所有必要的依賴項(安裝程序 UI 中默認選中的任何內容都是首選以避免出現問題)。

注意 :如果您使用的是 MacOS,那麼在繼續本教程的下一部分之前,您應該只需要這樣做。

如果您使用的是 Windows 您需要完成一個額外的步驟:將 PostgreSQL 版本文件夾添加到您的 PATH。

為此,請在啟動欄上的“鍵入此處進行搜索”框中,鍵入“env”並單擊“編輯系統環境變量”結果的鏈接。在出現的“系統屬性”窗口中,找到並單擊窗口右下角的“環境變量...”按鈕。

在標有“<username> 的用戶變量”的框中 , 找到“路徑”行,單擊以突出顯示它,然後按列表下方的“編輯...”按鈕。

在彈出的“編輯環境變量”窗口中,點擊窗口右側的“新建”按鈕,在出現的文本字段中輸入C:\Program Files\PostgreSQL\14\bin . 記住 :14 在這個路徑中代表 應該 的最新版本的 PostgreSQL 安裝在您的計算機上(截至撰寫時)。這可能需要根據您閱讀本教程的時間進行調整。建議您導航到 C:\Program Files\PostgreSQL 文件夾並在 that 中找到最新/最高版本號 在此路徑中使用的文件夾。

設置後,在到目前為止彈出的每個窗口上單擊“確定”。 建議您在執行此操作後重新啟動計算機 以確保變量正確加載到您的命令行中。

重新啟動計算機後,您就可以繼續學習本教程了。

將 PostgreSQL 輔助命令添加到 package.json

首先,我們需要確保我們有一個正在運行的 PostgreSQL 服務器和一個數據庫on 那個服務器。為了使這更容易,我們將首先打開 package.json 項目根目錄下的文件。

/package.json

{
  "name": "ny290syhfjifjekd",
  "type": "module",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "pg:init": "pg_ctl init -D data",
    "pg:start": "pg_ctl -D data start",
    "pg:createdb": "createdb -h 127.0.0.1 app",
    "pg:stop": "pg_ctl -D data stop",
    "start": "NODE_ENV=development && node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.3",
    "pg": "^8.7.3"
  }
}

我們的目標是在 scripts 中添加一些“幫助”命令 我們的 package.json 部分 文件。具體來說,我們需要添加五個命令:

  1. pg:init 這將初始化 PostgreSQL 存儲數據的數據目錄。
  2. pg:start 這將啟動 PostgreSQL 服務器。
  3. pg:createdb 這將在服務器上創建一個 PostgreSQL 數據庫。
  4. pg:stop 這將停止 PostgreSQL 服務器。
  5. start 我們將使用它通過 Express 啟動我們的演示服務器。

請注意,對於 pg 前綴腳本,我們使用命令 pg_ctlcreatedb .早些時候,當我們安裝 PostgreSQL 時,這些命令會暴露給您的 PATH 變量,這意味著它們可以在您的終端中全局訪問(如果您在 Windows 上,請確保您完成了上述附加步驟以使其工作)。

準備好這些之後,我們希望通過終端從我們之前創建的項目文件夾的根目錄運行以下命令:

終端

mkdir data

接下來,初始化我們的 PostgreSQL 服務器,運行:

終端

npm run pg:init

幾秒鐘後,您應該會看到一條有關此操作成功的消息。接下來,我們要啟動服務器:

終端

npm run pg:start

這將顯示一些輸出並表明服務器已成功啟動。最後,創建我們要運行的數據庫:

終端

npm run pg:createdb

這將創建一個名為 app 的數據庫 在您剛剛啟動的服務器上,這是我們將在下面的示例中使用的數據庫。

創建連接池

假設所有這些都有效,現在,我們需要通過 Node.js 代碼建立與 PostgreSQL 的連接。首先,我們要在項目的根目錄下創建一個新文件,postgresql.js

/postgresql.js

import postgresql from 'pg';

const { Pool } = postgresql;

export default (callback = null) => {
  // We'll handle our connection to PostgreSQL here...
};

在這個新文件中,首先,我們要從 pg 導入默認導出 我們之前安裝的包為 postgresql .接下來,在導入下方,我們要“拔掉”Pool (區分大小寫)類使用 JavaScript 解構(由 {} 表示 在 const 之後 )。

這裡,Pool 是一個允許我們創建 pooled 的類 連接到我們的 PostgreSQL 數據庫。 這很重要 .在多用戶 Web 應用程序中,我們希望在與數據庫通信時盡可能高效。 每個請求創建一個連接 這意味著我們可以很容易地淹沒我們的數據庫,從而可能導致停機。

在使用 PostgreSQL 時,我們可以使用 pooling 創建一個連接“池”的功能,可以由用戶臨時佔用,然後在完成後返回到池中(想像這就像一本圖書館的書被借出然後稍後返回)。

終端

import postgresql from 'pg';
import os from 'os';

const { Pool } = postgresql;

export default (callback = null) => {
  // NOTE: PostgreSQL creates a superuser by default on localhost using the OS username.
  const pool = new Pool({
    user: process.env.NODE_ENV === 'development' && (os.userInfo() || {}).username || '',
    database: 'app',
    password: '',
    host: '127.0.0.1',
    port: 5432,
  });

  // We'll handle making the connection accessible in our app here...
};

使用 Pool 可訪問的類,在我們從文件中導出的函數內部,我們要創建一個 new 它的實例並將其分配給變量 pool (小寫,這裡)。這個變量,pool 將包含“池實例”,我們將使用它作為連接到 PostgreSQL 數據庫的起點。

new Pool() 類,我們傳遞一個選項對象,其中包含數據庫的連接信息。在這裡,因為我們剛剛在本地啟動了我們的 PostgreSQL 服務器,所以我們將主機設置為 127.0.0.1localhost的IP地址版本 ) 和 5432 的端口 (PostgreSQL 的默認端口)。

我們還設置了 database 到“app”(我們剛剛用 db:createdb 創建的那個 腳本)並將密碼設置為空字符串。對於 user ,我們做一些有趣的事情。

默認情況下,PostgreSQL 使用操作系統上的當前用戶名在本地為我們創建一個超級用戶(例如,我的用戶名是 rglover 在我的筆記本電腦上,所以 PostgreSQL 創建了用戶名 rglover )。

雖然我們可以 硬編碼這個,它使我們的代碼相當不靈活。為了解決這個問題,我們為 os 添加了一個額外的導入 包,它是一個核心 Node.js 模塊,它使我們能夠訪問有關操作系統的信息。在這裡,假設我們的 NODE_ENVdevelopment (您會注意到我們將其設置為 start 的一部分 我們之前定義的腳本),我們調用 os.userInfo() 我們期望返回一個描述當前操作系統用戶的對象的函數。

在那個對像上,username 字段將匹配計算機上當前登錄的用戶(與 PostgreSQL 將用於創建超級用戶的值相同)。 (os.userInfo() || {}).username 這裡有一個安全技巧:如果 os.userInfo() 沒有返回任何東西,我們想回退到一個空對象,這樣如果我們返回 null 就不會導致意外的運行時錯誤 或 undefined (你沒有 這樣做,但它確實使我們的代碼更能容忍錯誤)。

有了這個,現在我們有了池連接,但我們還沒有完成。為了使這個連接有用,我們需要讓我們的整個應用程序/代碼庫都可以訪問它。

終端

import postgresql from 'pg';
import os from 'os';

const { Pool } = postgresql;

export default (callback = null) => {
  // NOTE: PostgreSQL creates a superuser by default on localhost using the OS username.
  const pool = new Pool({
    user: process.env.NODE_ENV === 'development' && (os.userInfo() || {}).username || '',
    database: 'app',
    password: '',
    host: '127.0.0.1',
    port: 5432,
  });

  const connection = {
    pool,
    query: (...args) => {
      return pool.connect().then((client) => {
        return client.query(...args).then((res) => {
          client.release();
          return res.rows;
        });
      });
    },
  };

  process.postgresql = connection;

  if (callback) {
    callback(connection);
  }

  return connection;
};

就在我們對 new Pool() 的調用下方 ,在這裡,我們添加了一個新變量 connection 設置等於具有兩個值的對象:pool (我們的新 pool new Pool() 返回的實例 ) 和 query .

在此對象定義下方,請注意 Node.js 上的 process ,我們正在添加一個屬性 postgresql 並將其分配給這個 connection 目的。這將使我們能夠在整個應用程序中全局訪問池連接(process 對象可以在我們的整個代碼庫中訪問)。

回到 query 部分,這是我們為了方便而添加的特殊功能。使用連接池時,每次我們想要執行查詢時,都需要連接到該池,運行我們的查詢,然後將連接返回或“釋放”回池中。

雖然這樣做很好,但它可能很麻煩。為了讓我們的工作更輕鬆,這裡的 query 我們在 connection 上設置的屬性 “自動化”連接和釋放過程。首先,我們分配 query 到一個普通的 JavaScript 函數並使用 JavaScript rest 運算符 ... 說“在名為 args 的變量中提取傳遞給此函數的所有參數 它的作用域是我們定義的函數體。”

在該函數內部,我們返回對 pool.connect() 的調用 它本身返回一個 JavaScript Promise。當這個 Promise 解決 ,我們希望它傳遞一個 client 聯繫。在那個連接上,我們可以對我們的 PostgreSQL 數據庫執行查詢,所以我們調用 client.query() 通過 ...args 來自我們的包裝函數的值。這裡,...args 被稱為“傳播”,因為我們正在“傳播” args 的值 作為傳遞給 client.query() 的參數 .

所以,假設我們調用了我們分配給 query 的函數 像 query('SELECT * FROM books') ,我們將有效地編寫 client.query('SELECT * FROM books') . ...args 部分只是自動化將所有傳遞給函數的參數移動到另一個函數(或對象)然後“傳遞”給另一個函數(或對象)的過程。

client.query() 之後 被調用,我們期望 it 從數據庫返回一個響應,並在該響應上,一個名為 rows 的屬性 這是我們數據庫中與我們的查詢匹配的行數組(如果有的話)。

就像 pool.connect() 我們期望 client.query() 返回一個 JavaScript Promise。在這裡,在 .then() 回調函數(在 Promise 解決/我們的查詢完成後調用),我們調用 client.release() 將我們的連接放回池中,然後返回 res.rows .這確保了 res.rows 的值 “冒泡”到對 query 的原始調用 .

按照這裡的模式,我們希望能夠做這樣的事情:

const rows = await process.postgresql.query('SELECT * FROM books');
console.log(rows);
/*
  [{ id: 1, title: 'The Best Book Ever', author: 'Author McAuthorstuff' }]
*/

這正是我們接下來打算連接的內容。在我們完成這個文件之前,我們想提請注意 callback 參數被傳遞給我們從這個文件中導出的函數。如果已定義,則在我們將連接設置到 process 之後 ,我們想調用該函數並將其傳遞給我們的 connection 目的。我們接下來看看為什麼。

設置演示應用

現在我們準備好使用我們的連接了。為此,我們將設置一個準系統 Express.js 應用程序,用一些測試數據為我們的數據庫播種,然後連接一個 Express 端點,我們可以在其中測試對 query 的調用 我們剛剛在上面定義的函數。

/index.js

import express from 'express';
import postgresql from 'postgresql';

postgresql();

const app = express();

app.get('/books', async (req, res) => {
  const rows = await process.postgresql.query('SELECT * FROM books');
  res.status(200).send(JSON.stringify(rows));
});

app.listen(3000, () => {
  console.log('App running at http://localhost:3000');
});

這就是我們所需要的。在這裡,我們導入 express 來自 express 我們之前安裝的包並通過將其作為函數調用來創建它的新實例 express() ,將其存儲在變量 app 中 .

接下來,首先關注底部,我們調用 app.listen() 它告訴 Express 開始在我們計算機的 3000 端口上偵聽 HTTP 請求(為了表明這個過程已經完成,我們向它添加一個回調函數來註銷一條消息,讓我們知道服務器已啟動)。

在此之上,我們在 URL /books 處定義一個 HTTP GET 路由 (這可以在瀏覽器中的 http://localhost:3000/books 訪問 )。在該路由的回調處理程序中,我們假設我們可以訪問我們的 process.postgresql 我們在 /postgresql.js 中分配的值 (我們在上面導入並調用上面我們對 express() 的調用的導出函數 )。

最後,使用生成的 rows 我們期待從我們的 query 便利功能,我們res 池到初始請求,發回行的字符串化副本。

在您的終端中,從我們項目的根目錄開始,如果我們運行 npm start ,我們應該會看到控制台打印出“App running...”消息。如果我們訪問那條路線 http://localhost:3000/books 在瀏覽器中,我們應該會看到一個空數組打印到屏幕上。

如果你這樣做了,這意味著我們與 PostgreSQL 的連接正在工作,從技術上講,我們的工作已經完成。

不過,在我們結束之前,看看一些真實數據會很有幫助。為此,我們需要利用 /postgresql.js 中預期的回調函數 .

為數據庫播種

在應用程序中,生成測試數據的過程鬆散地稱為“為數據庫播種”。通常,您將創建一個“夾具”,它是一些自動化播種過程的代碼(表現自己)。

/index.js

import express from 'express';
import postgresql from './postgresql.js';

postgresql(async (connection) => {
  await connection.query('CREATE TABLE IF NOT EXISTS books (id bigserial primary key, title text, author text);');
  await connection.query('CREATE UNIQUE INDEX IF NOT EXISTS title ON books (title);');

  const books = [
    { title: 'Mastering the Lightning Network', author: 'Andreas Antonopoulos' },
    { title: 'Load Balancing with HAProxy', author: 'Nick Ramirez' },
    { title: 'Silent Weapons for Quiet Wars', author: 'Unknown' },
  ];

  for (let i = 0; i < books.length; i += 1) {
    const book = books[i];
    await connection.query(`INSERT INTO books (title, author) VALUES ('${book.title}', '${book.author}') ON CONFLICT DO NOTHING;`);
  }

  console.log('PostgreSQL database seeded!');
});

const app = express();

app.get('/books', async (req, res) => {
  const rows = await process.postgresql.query('SELECT * FROM books');
  res.status(200).send(JSON.stringify(rows));
});

app.listen(3000, () => {
  console.log('App running at http://localhost:3000');
});

在這裡,我們為調用 postgresql() 添加了一個回調函數 , 期待一個 connection 作為參數傳遞的對象。在那個連接上,我們需要運行三個查詢:

  1. 用於創建名為 books 的表的查詢 如果它不存在,則在我們的數據庫中。
  2. title 上創建唯一索引的查詢 我們的 books 列 表。
  3. 對於我們要“播種”數據庫的每本書,如果該書不存在,則插入該書的查詢。

這裡的具體查詢和代碼並不是非常重要。需要注意的主要是我們對 query 的使用 我們連接的便利功能。我們不僅可以調用它來獲取數據返回 作為回報,但我們也可以使用它來運行不期望返回值的任意查詢。

在這裡,我們就是這樣做的,在我們的標題上設置我們的表和一個唯一索引(這可以防止重新啟動應用程序創建重複),然後循環我們的 books 數組 ,執行 INSERT 查詢每本書到books 表。

現在,如果我們重新啟動我們的應用程序,然後加載 http://localhost:3000/books 在瀏覽器中路由,我們應該會看到我們返回的三本書。

總結

在本教程中,我們學習瞭如何使用 Node.js 設置和連接到 PostgreSQL 數據庫。我們學習瞭如何讓 PostgreSQL 在我們的計算機上運行,如何編寫一些 NPM 腳本來幫助我們設置數據庫,以及如何編寫一個具有便捷功能的模塊,用於連接到我們的 PostgreSQL 服務器作為連接池並運行查詢。最後,我們學習瞭如何使用一些測試數據為數據庫播種,並在 Express.js 中設置測試器路由來驗證我們的便利功能是否正常工作。


Tutorial JavaScript 教程
  1. 涵蓋這些主題使您成為 Javascript 面試老闆 - 第 1 部分

  2. 在 vue.js 中使用計算的滾動頂部

  3. Jenkins 和 JUnit 與 API 測試集成

  4. 如何使用 JavaScript 檢測我是否在緩存頁面上

  5. Leaflet:如何基於從 API 獲取的 JSON 創建標記標籤

  6. 原理圖:構建塊

  7. 如何構建 GitHub 應用程序以獲得樂趣和利潤

  1. React Native 與 React:基本語法

  2. 零延遲開發和單元測試迭代

  3. 我可以在整個頁面加載之前運行 javascript 嗎?

  4. 從頭開始的 Vuejs 反應性

  5. JavaScript 中的數組方法

  6. Minute JavaScript - 數組映射方法

  7. 創建 CLI 以自動創建文件

  1. 如何在 Node.js 中創建自己的加密貨幣區塊鏈

  2. 使用 Agora Web SDK 為直播構建舉手功能

  3. 用於 React Native 的帶有情感/樣式組件的媒體查詢、偽類等。

  4. 對於那些想要使用 Markdown 創建文檔站點的人...