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

將 Sequelize ORM 與 Node.js 和 Express 結合使用

簡介

Sequelize 是為 Node.js 創建的流行 ORM,在本教程中,我們將使用它來構建一個 CRUD API 來管理筆記。

與數據庫交互是後端應用程序的常見任務。這通常是通過原始 SQL 查詢完成的,這可能很難構建,尤其是對於那些剛接觸 SQL 或一般數據庫的人。

最終,對象關係映射器 (ORM) 應運而生 - 旨在使管理數據庫更容易。顧名思義,它們會自動從我們的代碼中映射出關係數據庫中的對象(實體)。

我們不再會編寫原始 SQL 查詢並針對數據庫執行它們。通過為我們提供一種將代碼連接到數據庫並操作持久化數據的編程方式,我們可以更多地關注業務邏輯,而不是容易出錯的 SQL。

什麼是 ORM?

對象關係映射 是一種將軟件對象映射到數據庫表的技術。開發人員可以與對象交互,而不必實際編寫任何數據庫查詢。當一個對像被讀取、創建、更新或刪除時,ORM 會在底層構造並執行一個數據庫查詢。

ORM 的另一個優點是它們支持多個數據庫:Postgres、MySQL、SQLite 等。如果您使用原始查詢編寫應用程序,則很難移動到不同的數據庫,因為需要重新編寫許多查詢。

使用 ORM,切換數據庫由 ORM 自己完成,通常您需要做的只是更改配置文件中的一兩個值。

續集

Node ORM 有很多,包括流行的 Bookshelf.js 和 TypeORM。

首先,它已經存在了很長時間 - 2011 年。它擁有數千個 GitHub 星,並被大量應用程序使用。由於它的年齡和流行度,它很穩定,並且有大量的在線文檔可供使用。

除了成熟穩定之外,Sequelize 還擁有龐大的功能集,涵蓋:查詢、範圍、關係、事務、原始查詢、遷移、讀取複製等。

需要注意的是,Sequelize 是基於 Promise 的,可以更輕鬆地管理異步函數和異常。它還支持所有流行的 SQL 方言:PostgreSQL、MySQL、MariaDB、SQLite 和 MSSQL。

另一方面,在 ORM(或 Object Document 中可以看到沒有 NoSQL 支持 映射器,在這種情況下),例如貓鼬。確實,決定選擇哪個 ORM 主要取決於您正在從事的項目的要求。

安裝 Sequelize

注意 :如果你想跟隨代碼,你可以在 GitHub 上找到它。

讓我們製作一個骨架節點應用程序並安裝 Sequelize。首先,讓我們為我們的項目創建一個目錄,進入它,然後使用默認設置創建一個項目:

$ mkdir notes-app
$ cd notes-app
$ npm init -y

接下來,我們將使用基本的 Express 服務器和路由器創建應用程序文件。我們稱之為index.js 匹配來自 npm init 的默認文件名 :

接下來,為了輕鬆創建 Web 服務器,我們將安裝 Express:

$ npm install --save express

安裝好之後,我們來設置服務器:

const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => res.send('Notes App'));

app.listen(port, () => console.log(`notes-app listening on port ${port}!`));

最後,我們可以通過 npm 繼續安裝 Sequelize 和我們選擇的數據庫 :

$ npm install --save sequelize
$ npm install --save sqlite3

使用哪個數據庫作為 Sequelize 與數據庫無關並不重要。無論底層數據庫如何,我們使用它的方式都是一樣的。 SQLite3 易於用於本地開發,是這些目的的流行選擇。

現在,讓我們在 index.js 中添加一些代碼 文件來設置數據庫並使用 Sequelize 檢查連接。根據您使用的數據庫,您可能需要定義不同的方言:

const Sequelize = require('sequelize');
const sequelize = new Sequelize({
  // The `host` parameter is required for other databases
  // host: 'localhost'
  dialect: 'sqlite',
  storage: './database.sqlite'
});

導入 Sequelize 後,我們將其設置為運行所需的參數。您還可以在此處添加更多參數,例如 pool ,儘管我們所擁有的足以開始。 dialect 取決於您使用的數據庫以及 storage 只是指向數據庫文件。

database.sqlite 文件是在我們項目的根級別自動創建的。

注意: 值得查看 Sequelize Docs 以設置不同的數據庫以及每個數據庫所需的信息。

如果您使用的是 MySQL、Postgres、MariaDB 或 MSSQL,您也可以只傳遞連接 URI,而不是單獨傳遞每個參數:

const sequelize = new Sequelize('postgres://user:[email protected]:5432/dbname');

最後,讓我們通過運行 .authenticate() 來測試連接 方法。在引擎蓋下,它只是運行一個 SELECT 查詢並檢查數據庫是否響應正確:

sequelize
  .authenticate()
  .then(() => {
    console.log('Connection has been established successfully.');
  })
  .catch(err => {
    console.error('Unable to connect to the database:', err);
  });

運行應用程序,我們得到了歡迎:

$ node index.js
notes-app listening on port 3000!
Executing (default): SELECT 1+1 AS result
Connection has been established successfully.

創建映射模型

在我們構建筆記 API 之前,我們需要創建一個筆記表。為此,我們需要定義一個 Note 模型,我們將其分配給一個常量,以便它可以在我們的整個 API 中使用。在 define 函數我們指定表名和字段。在這種情況下,註釋的文本字段和標籤的字符串:

與關係數據庫一樣,在構建 API 之前,我們需要先創建足夠的表。由於我們想避免使用 SQL 手動創建它,我們將定義一個 Model 類,然後讓 Sequelize 將其映射到一個表中。

這可以通過擴展 Sequelize.Model 來完成 類並運行 .init() 函數,傳遞參數,或通過定義 const 並為其分配 .define() 的返回值 Sequelize 中的方法。

後者更簡潔,所以我們將使用那個:

const Note = sequelize.define('notes', { note: Sequelize.TEXT, tag: Sequelize.STRING });

將模型映射到數據庫

現在我們有了一個 Note 模型我們可以創建 notes 數據庫中的表。在生產應用程序中,我們通常會通過遷移來更改數據庫,以便在源代碼管理中跟踪更改。

不過,為了簡潔起見,我們將使用 .sync() 方法。 .sync() 是什麼 做的很簡單——它將所有定義的模型同步到數據庫:

sequelize.sync({ force: true })
  .then(() => {
    console.log(`Database & tables created!`);
  });

在這裡,我們使用了 force 標記並將其設置為 true .如果一個表已經存在,該方法將DROP 它和 CREATE 一個新的。如果不存在,則只是創建一個表。

最後,讓我們創建一些示例筆記,然後將它們持久化到數據庫中:

sequelize.sync({ force: true })
  .then(() => {
    console.log(`Database & tables created!`);

    Note.bulkCreate([
      { note: 'pick up some bread after work', tag: 'shopping' },
      { note: 'remember to write up meeting notes', tag: 'work' },
      { note: 'learn how to use node orm', tag: 'work' }
    ]).then(function() {
      return Note.findAll();
    }).then(function(notes) {
      console.log(notes);
    });
  });

運行服務器,我們的筆記在控制台中打印出來,Sequelize 執行的 SQL 操作也一樣。讓我們連接到數據庫以驗證記錄確實已正確添加:

免費電子書:Git Essentials

查看我們的 Git 學習實踐指南,其中包含最佳實踐、行業認可的標準以及隨附的備忘單。停止谷歌搜索 Git 命令並真正學習 它!

$ sqlite3 database.sqlite
sqlite> select * from notes;
1|pick up some bread after work|shopping|2020-02-21 18:24:19.402 +00:00|2020-02-21 18:24:19.402 +00:00
2|remember to write up meeting notes|work|2020-02-21 18:24:19.402 +00:00|2020-02-21 18:24:19.402 +00:00
3|learn how to use node orm|work|2020-02-21 18:24:19.402 +00:00|2020-02-21 18:24:19.402 +00:00
sqlite> .exit

數據庫就位並創建好表後,讓我們繼續實現基本的 CRUD 功能。

讀取實體

我們的模型,Note ,現在有內置方法可以幫助我們對數據庫中的持久化記錄執行操作。

讀取所有實體

例如,我們可以讀取使用 .findAll() 保存的該類的所有記錄 方法。讓我們創建一個服務於所有持久化實體的簡單端點:

app.get('/notes', function(req, res) {
  Note.findAll().then(notes => res.json(notes));
});

.findAll() 方法返回一個註釋數組,我們可以通過 res.json 使用它來呈現響應正文 .

讓我們通過 curl 測試端點 :

$ curl http://localhost:3000/notes
[{"id":1,"note":"pick up some bread after work","tag":"shopping","createdAt":"2020-02-27T17:02:10.881Z","updatedAt":"2020-02-27T17:02:10.881Z"},{"id":2,"note":"remember to write up meeting notes","tag":"work","createdAt":"2020-02-27T17:02:10.881Z","updatedAt":"2020-02-27T17:02:10.881Z"},{"id":3,"note":"learn how to use node orm","tag":"work","createdAt":"2020-02-27T17:02:10.881Z","updatedAt":"2020-02-27T17:02:10.881Z"}]

如您所見,我們所有的數據庫條目均以 JSON 形式返回給我們。

不過,如果我們希望添加更多功能,我們有查詢操作,例如 SELECT , WHERE , AND , OR , 和 LIMIT 此方法支持。

可在 Sequelize Docs 頁面上找到支持的查詢方法的完整列表。

讀取實體WHERE

考慮到這一點,讓我們創建一個服務於單個特定註釋的端點:

app.get('/notes/:id', function(req, res) {
  Note.findAll({ where: { id: req.params.id } }).then(notes => res.json(notes));
});

端點接受 id 參數,用於通過WHERE查找筆記 條款。讓我們通過 curl 測試一下 :

$ curl http://localhost:3000/notes/2
[{"id":2,"note":"remember to write up meeting notes","tag":"work","createdAt":"2020-02-27T17:03:17.592Z","updatedAt":"2020-02-27T17:03:17.592Z"}]

注意 :由於此路由使用通配符參數,:id ,它將匹配任何 /notes/ 之後的字符串 .因此,這條路線應該在end 你的 index.js 文件。這允許其他路線,例如 /notes/search , 處理 /notes/:id 之前的請求 把它撿起來。否則 search URL 路徑中的關鍵字將被視為 ID。

讀取實體 WHERE AND

對於更具體的查詢,讓我們使用 WHERE 創建一個端點 和 AND 聲明:

app.get('/notes/search', function(req, res) {
  Note.findAll({ where: { note: req.query.note, tag: req.query.tag } }).then(notes => res.json(notes));
});

在這裡,我們正在尋找與 note 匹配的註釋 和 tag 由參數指定。再次,讓我們通過 curl 測試一下 :

$ curl "http://localhost:3000/notes/search?note=pick%20up%20some%20bread%20after%20work&tag=shopping"
[{"id":1,"note":"pick up some bread after work","tag":"shopping","createdAt":"2020-02-27T17:09:53.964Z","updatedAt":"2020-02-27T17:09:53.964Z"}]

讀取實體或

如果我們想更模糊一點,我們可以使用 OR 語句並蒐索匹配 any 的註釋 的給定參數。更改 /notes/search 路線:

const Op = Sequelize.Op;

app.get('/notes/search', function(req, res) {
  Note.findAll({
    where: {
      tag: {
        [Op.or]: [].concat(req.query.tag)
      }
    }
  }).then(notes => res.json(notes));
});

這裡我們使用 Sequelize.Op 實現 OR 詢問。 Sequelize 提供了多種運算符可供選擇,例如 Op.or , Op.and , Op.eq , Op.ne , Op.is , Op.not 等。這些主要用於創建更複雜的操作,例如使用正則表達式字符串進行查詢。

請注意,我們使用的是 req.query.tag 作為 .findAll() 的參數 . Sequelize 在這裡需要一個數組,所以我們強制 tag 使用 [].concat() 成為一個數組 .在下面的測試中,我們將在請求 URL 中傳遞多個參數:

$ curl "http://localhost:3000/notes/search?tag=shopping&tag=work"
[{"id":1,"note":"pick up some bread after work","tag":"shopping","createdAt":"2020-02-27T17:11:27.518Z","updatedAt":"2020-02-27T17:11:27.518Z"},{"id":2,"note":"remember to write up meeting notes","tag":"work","createdAt":"2020-02-27T17:11:27.518Z","updatedAt":"2020-02-27T17:11:27.518Z"},{"id":3,"note":"learn how to use node orm","tag":"work","createdAt":"2020-02-27T17:11:27.518Z","updatedAt":"2020-02-27T17:11:27.518Z"}]

當像這樣多次傳遞相同的查詢參數時,它將在 req.query 中顯示為一個數組 目的。所以在上面的例子中,req.query.tag['shopping', 'work'] .

讀取實體限制

我們將在本節中介紹的最後一件事是 LIMIT .假設我們想修改之前的查詢,使其最多只返回兩個結果。我們將通過添加 limit 來做到這一點 參數並為其分配一個正整數:

const Op = Sequelize.Op;

app.get('/notes/search', function(req, res) {
  Note.findAll({
    limit: 2,
    where: {
      tag: {
        [Op.or]: [].concat(req.query.tag)
      }
    }
  }).then(notes => res.json(notes));
});

您可以在 Sequelize 文檔中查看完整的查詢函數列表。

插入實體

插入實體要簡單得多,因為實際上沒有兩種方法可以執行此操作。

讓我們添加一個新的端點來添加註釋:

const bodyParser = require('body-parser');
app.use(bodyParser.json());

app.post('/notes', function(req, res) {
  Note.create({ note: req.body.note, tag: req.body.tag }).then(function(note) {
    res.json(note);
  });
});

body-parser 端點需要模塊來接受和解析 JSON 參數。您不需要顯式安裝 body-parser 包,因為它已經包含在 Express 中。

在我們使用 .create() 的路線內 根據傳遞的參數將註釋插入數據庫的方法。

我們可以用另一個 curl 來測試它 請求:

$ curl -d '{"note":"go the gym","tag":"health"}' -H "Content-Type: application/json" -X POST http://localhost:3000/notes
{"id":4,"note":"go the gym","tag":"health","updatedAt":"2020-02-27T17:13:42.281Z","createdAt":"2020-02-27T17:13:42.281Z"}

運行此請求將導致在我們的數據庫中創建一個便箋並將新的數據庫對象返回給我們。

更新實體

有時,我們希望更新已經存在的實體。為此,我們將依賴 .update() .findByPk() 結果的方法 方法:

app.put('/notes/:id', function(req, res) {
  Note.findByPk(req.params.id).then(function(note) {
    note.update({
      note: req.body.note,
      tag: req.body.tag
    }).then((note) => {
      res.json(note);
    });
  });
});

.findByPk() 方法也是我們模型類中的繼承方法。它搜索具有給定主鍵的實體。本質上,使用此方法通過 ID 返回單個實體比編寫 SELECT WHERE 更容易 查詢。

給定返回的實體,我們運行 .update() 方法來實際放置新值。讓我們通過 curl 驗證這一點 :

$ curl -X PUT -H "Content-Type: application/json" -d '{"note":"pick up some milk after work","tag":"shopping"}' http://localhost:3000/notes/1
{"id":1,"note":"pick up some milk after work","tag":"shopping","createdAt":"2020-02-27T17:14:55.621Z","updatedAt":"2020-02-27T17:14:58.230Z"}

觸發此請求會使用新內容更新第一個便箋並返回更新後的對象:

刪除實體

最後,當我們想從數據庫中刪除記錄時,我們使用 .destroy() .findByPk() 結果的方法 方法:

app.delete('/notes/:id', function(req, res) {
  Note.findByPk(req.params.id).then(function(note) {
    note.destroy();
  }).then((note) => {
    res.sendStatus(200);
  });
});

.delete() 的路線 看起來類似於 .update() .我們使用 .findByPk() 按 ID 查找特定註釋。然後,.destroy() 方法從數據庫中刪除註釋。

最後,一個 200 OK 響應返回給客戶端。

結論

對象關係映射 (ORM) 是一種將軟件對象映射到數據庫表的技術。 Sequelize 是與 Node.js 一起使用的流行且穩定的 ORM 工具。在本文中,我們討論了 ORM 是什麼,它們是如何工作的,以及使用它們與編寫原始查詢相比有哪些優勢。

有了這些知識,我們開始編寫一個簡單的 Node.js/Express 應用程序,它使用 Sequelize 來持久化 Note 模型到數據庫。使用繼承的方法,我們隨後對數據庫執行了 CRUD 操作。

如果您在本教程中遇到任何問題,請隨時查看 GitHub 上的代碼。


Tutorial JavaScript 教程
  1. 🚀 快速提示:全局註冊 Vue 組件

  2. jQuery 將文本轉換為 HTML 列表 - $.stringToList

  3. 在 C、Go 和 Swift 中構建一個小型跨平台 CLI 工具

  4. useState vs useReducer 終極指南

  5. 使用 React 時要避免的 10 個錯誤

  6. AngularJS 和 Laravel:開始構建 CRM

  7. 7 個很棒的 Node JS 項目創意,適合初學者,練習你的技能並被錄用

  1. 如何使用 XState 和 React 構建有限狀態機?

  2. 遞歸的簡單介紹

  3. 在 i18next 中嵌入貨幣、日期等格式

  4. Next.js 中的 Web Vitals 入門

  5. JSONP 與 jQuery、MooTools 和 Dojo

  6. 如何在 vue 中進行動態操作

  7. 為服務器端渲染添加樣式並自動化構建過程

  1. ApostropheCMS 和 Jamstack

  2. JavaScript 對象值() |方法

  3. Material UI Datepicker 和 @date-io/date-fns 2.0.0

  4. 使用 Node.Js 設置環境以開始在本地機器上測試 MongoDB - 第 2 部分