Node.js數據庫鉤子的使用
在本文中,我將解釋如何在您的Node.js應用程序中使用數據庫鉤子來解決在開發(fā)過程中可能出現的特定問題。
許多應用程序只需要在服務器、數據庫之間建立連接池并執(zhí)行查詢。然而,根據您的應用程序和數據庫部署情況,可能需要進行其他配置。
例如,多區(qū)域分布式SQL數據庫可以根據應用程序用例的不同拓撲進行部署。某些拓撲需要在每個會話中在數據庫上設置屬性。
讓我們探索一下Node.js生態(tài)系統(tǒng)中一些最受歡迎的數據庫客戶端和ORM提供的一些鉤子。
基礎
在使用最流行的關系數據庫時,Node.js社區(qū)有許多可供選擇的驅動程序。在這里,我將重點關注與兼容PostgreSQL的數據庫客戶端,它們可以用于連接到YugabyteDB或其他PostgreSQL數據庫。
Sequelize、Prisma、Knex和node-postgres是一些功能各異的受歡迎的客戶端,具體取決于您的需求。我鼓勵您閱讀它們的文檔,以確定哪個最適合您的需求。
這些客戶端提供了不同用例的鉤子。例如:
- 連接鉤子: 在連接和斷開與數據庫的連接之前或之后立即執(zhí)行函數。
- 日志鉤子: 在不同的日志級別下將消息記錄到stdout。
- 生命周期鉤子: 在對數據庫進行調用之前或之后立即執(zhí)行函數。
在本文中,我將介紹這些客戶端提供的一些鉤子,并說明您如何在分布式SQL應用程序中使用它們的好處。
我還將演示如何在創(chuàng)建用戶之前使用鉤子對用戶的密碼進行散列,并在連接到具有讀副本的多區(qū)域數據庫后如何設置運行時配置參數。
Sequelize
Sequelize ORM提供了許多鉤子來管理數據庫事務的整個生命周期。
beforeCreate生命周期鉤子可用于在創(chuàng)建新用戶之前對密碼進行散列處理:
User.beforeCreate(async (user, options) => { const hashedPassword = await hashPassword(user.password); user.password = hashedPassword; });
接下來,我使用afterConnect連接鉤子來設置會話參數。
通過這個YugabyteDB部署,您可以從跟隨者執(zhí)行讀取操作以減少延遲,并且無需從主集群節(jié)點讀?。?/p>
const config = { ? host: process.env.DB_HOST, ? port: 5433, ? dialect: "postgres", ? dialectOptions: { ? ? ssl: { ? ? ? require: true, ? ? ? rejectUnauthorized: true, ? ? ? ca: [CERTIFICATE], ? ? }, ? }, ? pool: { ? ? max: 5, ? ? min: 1, ? ? acquire: 30000, ? ? idle: 10000, ? }, ? hooks: { ? ? ?async afterConnect(connection) { ? ? ? ?if (process.env.DB_DEPLOYMENT_TYPE === "multi_region_with_read_replicas") { ? ? ? ? ?await connection.query("set yb_read_from_followers = true; set session characteristics as transaction read only;"); ? ? ? ?} ? ? ?}, ? ?}, }; const connection = new Sequelize( ? ?process.env.DATABASE_NAME, ? ?process.env.DATABASE_USER, ? ?process.env.DATABASE_PASSWORD, ? ?config );
通過使用這個鉤子,在連接池中的每個數據庫會話在建立新連接時都會設置這些參數:
- set yb_read_from_followers = true;:此參數控制是否啟用從跟隨者讀取。
- set session characteristics as transaction read only;:此參數將只讀設置應用于后續(xù)的所有語句和事務塊。
Prisma
盡管在Node.js社區(qū)中,Prisma是許多人首選的ORM,但在撰寫本文時,Prisma并不包含Sequelize中的許多內置鉤子。目前,該庫包含用于處理查詢生命周期、日志記錄和斷開連接的鉤子,但在建立連接之前或之后提供的幫助很少。
以下是如何使用Prisma的生命周期中間件在創(chuàng)建用戶之前對密碼進行哈希處理的方法:
prisma.$use(async (params, next) => { ?if (params.model == 'User' && params.action == 'create') { ? ?params.args.data.password = await hashPassword(params.args.data.password); ?} ?return next(params) }) const create = await prisma.user.create({ ?data: { ? ?username: 'bhoyer', ? ?password: 'abc123' ?}, })
要設置會話參數以利用我們的讀取副本,我們需要在查詢數據庫之前執(zhí)行一條語句:
await prisma.$executeRaw(`set yb_read_from_followers = true; set session characteristics as transaction read only;`); const users = await prisma.user.findMany();
如果您需要立即在連接池中建立連接以設置參數,您可以使用Prisma顯式地進行連接,而不使用連接池的延遲連接。
Prisma具有查詢(query)、錯誤(error)、信息(info)和警告(warn)等日志級別??梢允褂没谑录娜罩居涗泚硖幚聿樵兪录?/p>
const prisma = new PrismaClient({ ?log: [ ? ?{ ? ? ?emit: 'event', ? ? ?level: 'query', ? ?}, ? ?{ ? ? ?emit: 'stdout', ? ? ?level: 'error', ? ?}, ? ?{ ? ? ?emit: 'stdout', ? ? ?level: 'info', ? ?}, ? ?{ ? ? ?emit: 'stdout', ? ? ?level: 'warn', ? ?}, ?], }); prisma.$on('query', (e) => { ?console.log('Query: ' + e.query); ?console.log('Params: ' + e.params); ?console.log('Duration: ' + e.duration + 'ms'); });
這在開發(fā)過程中對于在分布式系統(tǒng)中進行查詢調優(yōu)非常有幫助。
下面是如何在退出之前使用beforeExit鉤子來訪問數據庫的示例:???????
const prisma = new PrismaClient(); prisma.$on('beforeExit', async () => { ?// PrismaClient still available ?await prisma.issue.create({ ? ?data: { ? ? ?message: 'Connection exiting.'? ? ?}, ?}) });
Knex是一個輕量級的查詢構建器,但它沒有更全功能的ORM中的查詢中間件。
要對密碼進行哈希處理,可以使用自定義函數來手動處理:
async function handlePassword(password) { const hashedPassword = await hashPassword(password); return hashedPassword; } const password = await handlePassword(params.password); knex('users').insert({...params, password});
在Knex.js查詢構建器中實現連接鉤子所需的語法與Sequelize類似。以下是如何設置會話參數以從YugabyteDB的副本節(jié)點讀取的示例代碼:
const knex = require('knex')({ client: 'pg', connection: {/*...*/}, pool: { afterCreate: function (connection, done) { connection.query('set yb_read_from_followers = true; set session characteristics as transaction read only;', function (err) { if (err) { //Query failed done(err, conn); } else { console.log("Reading from replicas."); done(); } }); } } });
node-postgres庫是所有討論過的庫中最低級的庫。在底層,使用Node.js的EventEmitter來觸發(fā)連接事件。
當在連接池中建立新連接時,會觸發(fā)connect事件。我們可以使用它來設置我們的會話參數。我還添加了一個錯誤鉤子來捕獲和記錄所有錯誤消息的示例代碼:???????
const config = { ? user: process.env.DB_USER, ? host: process.env.DB_HOST, ? password: process.env.DB_PASSWORD, ? port: 5433, ? database: process.env.DB_NAME, ? min: 1, ? max: 10, ? idleTimeoutMillis: 5000, ? connectionTimeoutMillis: 5000, ? ssl: { ? ? rejectUnauthorized: true, ? ? ca: [CERTIFICATE], ? ? servername: process.env.DB_HOST, ? } }; const pool = new Pool(config); pool.on("connect", (c) => { ? c.query("set yb_read_from_followers = true; set session characteristics as transaction read only;"); }); pool.on("error", (e) => { ? console.log("Connection error: ", e); });
在node-postgres中,我們沒有可用的生命周期鉤子,所以像Prisma一樣,我們必須手動進行密碼哈希處理:
async function handlePassword(password) { const hashedPassword = await hashPassword(password); return hashedPassword; } const password = await handlePassword(params.password); const user = await pool.query('INSERT INTO user(username, password) VALUES ($1, $2) RETURNING *', [params.username, password]);
總結
正如你所看到的,鉤子可以解決之前由復雜且容易出錯的應用程序代碼所引起的許多問題。每個應用程序都有不同的要求和面臨新的挑戰(zhàn)。在你的開發(fā)過程中,可能會經過很多年才需要使用特定的鉤子,但現在,當那一天來臨時,請準備就緒。
到此這篇關于Node.js數據庫鉤子的使用的文章就介紹到這了,更多相關Node.js數據庫鉤子內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
利用nodejs讀取圖片并將二進制數據轉換成base64格式
這篇文章主要介紹了利用nodejs讀取圖片并將二進制數據轉換成base64格式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08Windows安裝Node.js報錯:2503、2502的解決方法
這篇文章主要給大家介紹了關于在Windows系統(tǒng)下安裝Node.js報錯:2503、2502的解決方法,文中將解決的方法一步步介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧。2017-10-10