Часть 2.Создание REST API: основы базы данных
В первой статье вы создали веб-сервер, тут вы создадите модуль, который отвечает за запуск и завершение работы пула соединений с базой данных используя node-oracledb. А также добавите функцию, которая упрощает выполнение простых операторов, автоматически получая и освобождая соединения из пула.
Запуск connection pool
Поскольку node-oracledb построен поверх клиентских библиотек OCI, он имеет встроенную поддержку для создания пулов OCI, которые работают на стороне клиента и имеют отличные характеристики производительности. Чтобы создать connection pool, начните с создания нового файла конфигурации с именем database.js в каталоге config. Скопируйте и вставьте следующий код в файл и сохраните изменения.
module.exports = {
hrPool: {
user: process.env.HR_USER,
password: process.env.HR_PASSWORD,
connectString: process.env.HR_CONNECTIONSTRING,
poolMin: 10,
poolMax: 10,
poolIncrement: 0
}
};
Как и в случае с файлом config / webserver.js, этот файл позволяет устанавливать некоторые свойства с помощью переменных среды. Использование переменных среды обеспечивает гибкость при развертывании приложения в различных средах и помогает сохранить пароли и другую конфиденциальную информацию вне исходного кода. Выполните следующие команды из терминала, чтобы установить необходимые переменные среды и убедиться, что они доступны в будущих сеансах терминала.
echo "export HR_USER=hr" >> ~/.bashrc
echo "export HR_PASSWORD=oracle" >> ~/.bashrc
echo "export HR_CONNECTIONSTRING=0.0.0.0/orcl" >> ~/.bashrc
source ~/.bashrc
Вы могли заметить, что значения poolMin и poolMax были одинаковыми и что poolIncrement был установлен в 0. Это создаст пул фиксированного размера, который требует меньше ресурсов для управления — хорошая идея для пулов, которые получают согласованное использование.
Хотя Node.js часто описывают как «single-threaded» (однопоточный), он имеет пул потоков, доступный для определенных операций, которые в противном случае блокировали бы основной поток, выполняющий код JavaScript. Этот пул потоков используется node-oracledb для выполнения всех своих асинхронных операций, таких как получение соединений и выполнение кода SQL и PL / SQL. Однако по умолчанию размер пула потоков равен 4. Если вы хотите, чтобы все 10 подключений в пуле могли работать одновременно, вам необходимо соответственно увеличить количество потоков.
Переменная окружения UV_THREADPOOL_SIZE может использоваться для настройки размера пула потоков. Значение UV_THREADPOOL_SIZE может быть установлено до запуска приложения Node.js или изнутри, но оно должно быть установлено до того, как будет сделан первый вызов, использующий пул потоков. Это связано с тем, что пул потоков создается при его первом использовании и после его создания, его размер является фиксированным. Откройте файл index.js в корне приложения и добавьте следующие строки после первой строки (в которой содержится модуль веб-сервера).
// *** line that requires services/web-server.js is here ***
const dbConfig = require('./config/database.js');
const defaultThreadPoolSize = 4;
// Increase thread pool size by poolMax
process.env.UV_THREADPOOL_SIZE = dbConfig.hrPool.poolMax + defaultThreadPoolSize;
Теперь, когда пул потоков имеет соответствующий размер, вы можете перейти к модулю базы данных. Создайте новый файл в каталоге services с именем database.js. Скопируйте и вставьте в него следующий код и сохраните изменения.
const oracledb = require('oracledb');
const dbConfig = require('../config/database.js');
async function initialize() {
const pool = await oracledb.createPool(dbConfig.hrPool);
}
module.exports.initialize = initialize;
Этот модуль сначала вводит node-oracledb и файл конфигурации. Затем определяется асинхронная функция с именем initialize, которая затем предоставляется через объект module.exports. Функция initialize создает пул соединений, который хранится во внутреннем кэше пулов соединений как пул «по умолчанию».
Теперь вам нужно соединить все так, чтобы пул соединений запускался до открытия веб-сервера. Вернитесь в файл index.js и добавьте следующую строку ниже строки 1.
// *** line that requires services/web-server.js is here ***
const database = require('./services/database.js');
Затем добавьте следующий блок try в функцию stratup, непосредственно перед существующим блоком try, который запускает веб-сервер.
try {
console.log('Initializing database module');
await database.initialize();
} catch (err) {
console.error(err);
process.exit(1); // Non-zero failure code
}
// *** existing try block in startup here ***
На этом этапе вы можете установить node-oracledb и протестировать код. Выполните следующие команды в терминале из каталога hr_app.
npm install oracledb -s
node .
Если вы видите сообщения о том, что модуль базы данных и веб-сервер запущены, тогда, поздравляю — у вас теперь работает пул соединений!
Завершение работы connection pool
Если сейчас закрыть приложение (используя ctrl + c, как и раньше), процесс Node.js будет уничтожен до закрытия пула соединений. Хотя все связанные процессы базы данных должны очищаться автоматически, лучше всего явно закрыть пул соединений перед выходом из процесса Node.js.
Вернитесь в файл services / database.js, добавьте в конец следующие строки кода и сохраните обновления.
// *** previous code above this line ***
async function close() {
await oracledb.getPool().close();
}
module.exports.close = close;
Функция close использует метод oracledb.getPool () для синхронного получения пула по умолчанию, а затем вызывает метод close для пула, чтобы закрыть его.
Чтобы вызвать функцию close в нужное время, добавьте следующие строки кода в файл index.js внутри функции shutdown сразу после существующего блока try, который останавливает веб-сервер.
// *** existing try-catch block in shutdown here ***
try {
console.log('Closing database module');
await database.close();
} catch (err) {
console.log('Encountered error', e);
err = err || e;
}
Если вы снова запустите и закроете приложение, вы увидите, что модуль базы данных закрывается после закрытия веб-сервера, но до завершения процесса.
Упрощение простых операций CRUD
Выполнение кода SQL или PL / SQL с помощью node-oracledb обычно представляет собой трехэтапный процесс: получить соединение, выполнить код, а затем освободить соединение. Если все, что вы хотите сделать, это выполнить один вызов для выполнения (не требуется многоэтапная транзакция), то получение и освобождение соединения может выглядеть как стандартный код. Мне нравится создавать функцию, которая выполняет все три операции одним вызовом. Вернитесь в файл services / database.js, добавьте следующий код внизу и сохраните изменения.
// *** previous code above this line ***
function simpleExecute(statement, binds = [], opts = {}) {
return new Promise(async (resolve, reject) => {
let conn;
opts.outFormat = oracledb.OBJECT;
opts.autoCommit = true;
try {
conn = await oracledb.getConnection();
const result = await conn.execute(statement, binds, opts);
resolve(result);
} catch (err) {
reject(err);
} finally {
if (conn) { // conn assignment worked, need to close
try {
await conn.close();
} catch (err) {
console.log(err);
}
}
}
});
}
module.exports.simpleExecute = simpleExecute;
Как правило, вы не будете использовать модуль базы данных в модуле веб-сервера, но добавите его сейчас, чтобы убедиться, что он работает правильно. Откройте файл services / web-server.js и добавьте следующую строку под существующими объявлениями констант вверху.
// line that requires ../config/web-server.js here
const database = require('./database.js');
Затем используйте следующий код, чтобы заменить весь обработчик app.get, который отвечает «Hello World!» (Все 3 строки).
// *** line that adds morgan to app here ***
app.get('/', async (req, res) => {
const result = await database.simpleExecute('select user, systimestamp from dual');
const user = result.rows[0].USER;
const date = result.rows[0].SYSTIMESTAMP;
res.end(`DB user: ${user}\nDate: ${date}`);
});
Новый обработчик использует функцию simpleExecute модуля базы данных для извлечения значений текущего пользователя и systimestamp из базы данных. Затем значения используются в литерале шаблона для ответа клиенту динамическим сообщением.
Запустите приложение еще раз и перейдите к localhost: 3000. Вы должны увидеть что-то вроде следующего изображения.
Если вы видите такое сообщение, значит, вcе работает как надо. В следующей статье вы продолжите создавать API, добавив маршрутизацию, контроллер и логику базы данных для запроса GET.