В этой статье мы рассмотрим переменные окружения (environment variables). По сути, это пары ключ-значение набора данных, которые хранятся на уровне операционной системы.
В этой статье мы узнаем о переменных окружения в Node.js с примерами. Содержание:
Что такое переменные окружения
Почему переменные окружения важны
Необходимые условия для проекта
Установка Node.js и настройка нового проекта
Инициализация первой переменной окружения в Node.js
Переменные окружения при вызове API / асинхронные задачи
Переменные окружения в операциях с базой данных / асинхронные задачи
Расширенные возможности работы с переменными окружения в Node.js: кодирование, валидация и преобразование типов
Управление секретами и лучшие практики безопасности с примерами
Подводные камни и как их избежать
Переменные окружения
Переменные окружения — это набор данных в виде пар ключ-значение, доступных на уровне операционной системы. Этот набор данных доступен во всех основных оболочках командной строки операционных систем Windows, Mac и Linux.
Почему переменные окружения важны?
Разделение проблем
Безопасность
Переносимость
Масштабируемость
Совместимость
Интероперабельность
Необходимые условия для этого проекта
Предполагается, что у вас есть:
Базовые знания Node.js
Базовые знания JavaScript
Некоторые знания о разработке бэкенда
Установка Node и настройка проекта
Установить Node.js на машину можно разными способами. Для начала нужно зайти на официальный сайт Node и скачать оттуда нужную версию.
Давайте рассмотрим установку Node.js в операционной системе Linux, предпочтительно Ubuntu
Шаг 1. Откройте терминал
Шаг 2. Используйте скрипт curl или wget для установки nvm, используя git-репозиторий nvm. Запустите скрипт для установки nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
# OR
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
Шаг 3. Закройте и снова откройте терминал и выполните следующие действия, чтобы применить скрипт
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
Шаг 4. Подтверждение установки
Убедиться, что NVM установлен в вашей системе, можно с помощью следующей команды:
nvm --version
*Шаг 5. * Установка Node
Введите следующую команду для установки последней версии node:
nvm install latest
или
nvm install --lts
Шаг 6. Установка версии по умолчанию
Чтобы установить версию Node, которая будет использоваться в вашей системе по умолчанию, используйте команду:
nvm alias default 20.8.0
Шаг 7. Проверка установки
Чтобы проверить, установлен ли Node, введите команду:
node -v # сообщает текущую версию node в вашей системе. В нашем случае это 20.8.0
Инициализация проекта
Теперь, когда мы установили Node, давайте создадим новый проект, в котором мы будем использовать переменную окружения.
Создайте новую директорию, назовите ее env-demo и перейдите в нее:
mkdir env-demo
cd env-demo
Теперь введите команду для инициализации нового проекта и заполните поля по своему усмотрению.
npm init
package name: (env-demo)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
В итоге будет создан файл package.json, который будет выглядеть примерно так:
{
"name": "env-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Инициализация первой переменной окружения
Шаг 1: Установите пакет dotenv
Сейчас у вас инициализирован проект в приведенном выше разделе и создан файл package.json
. Чтобы установить пакет node dotenv, который используется для создания env-файлов, введите в терминале команду:
npm install dotenv --save
Пакет dotenv будет установлен и сохранен как зависимость в файле package.json. Это должно выглядеть примерно так:
{
"name": "env-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"dotenv": "^16.3.1"
}
}
Шаг 2 Создание файла .env
В корневой папке создайте новый файл и назовите его
.env
.Откройте его в текстовом редакторе и создайте несколько пар ключ-значение. Это ваши переменные окружения. Вот пример
DATABASE_URL=sample-database-url
PORT=3000
SECRET_KEY=mysecretkey
Шаг 3. Загрузка и чтение переменных окружения
В корневой папке создайте файл index.js, затем откройте терминал и введите следующую команду для установки Express.js:
npm install express --save
Express будет установлен и сохранен в качестве зависимости в файле package.json
. Файл package.json
будет выглядеть следующим образом:
{
"name": "env-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"dotenv": "^16.3.1",
"express": "^4.18.2"
}
}
и структура вашего проекта выглядит следующим образом:
Теперь откройте файл index.js и введите следующую команду, чтобы запустить простой веб-сервер.
const express = require('express');
const app = express();
const port = 4000; // defaults to 4000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}/`);
});
запустите код с помощью
node index.js
и перейдите на localhost://4000, чтобы получить hello world
Загрузка и чтение файла .env
Теперь, когда сервер запущен, давайте прочитаем файл .env
, который мы создали в предыдущем шаге, и загрузим данные о порте из файла .env
.
Откроем файл index.js
и «потребуем» там библиотеку dotenv
:
require('dotenv').config()
Теперь вы можете получить доступ к файлу .env
с помощью process.env
. Давайте воспользуемся process.env
для доступа к номеру порта в файле index.js
:
const express = require('express');
require('dotenv').config()
const app = express();
const port = process.env.PORT || 4000; // Read from .env if not available then defaults to 4000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}/`);
});
Теперь давайте перезапустим сервер и перейдем на localhost://3000
. Вместо того, чтобы работать на 4000, наш сервер теперь работает на порту 3000, который он взял из файла .env
Вы также можете увидеть это в консоли:
Итак, вы успешно создали и сохранили файл env на своей машине.
В качестве другого примера вы можете получить доступ к DATABASE_URL
следующим образом:
const dbUrl = process.env.DATABASE_URL;
// Use dbUrl to connect to your database
Вот все файлы
index.js
const express = require('express');
require('dotenv').config()
const app = express();
const port = process.env.PORT || 4000; // Read from .env if not available then defaults to 4000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}/`);
});
package.json
{
"name": "env-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"dotenv": "^16.3.1",
"express": "^4.18.2"
}
}
.env
DATABASE_URL=sample-database-url
PORT=3000
SECRET_KEY=mysecretkey
Как использовать переменные окружения в асинхронных задачах
В этом разделе мы поговорим о том, как переменные окружения можно использовать в асинхронных задачах, таких как вызов API или операции с базой данных.
Переменные окружения особенно важны в этих задачах, потому что их можно использовать для безопасного хранения учетных данных и конечных точек.
Кроме того, эти переменные можно автоматизировать, поскольку они меняются в разных средах, таких как разработка, стейджинг и продакшен.
Шаг 1. Настройка переменных окружения
Откройте файл .env
и отредактируйте его следующим образом:
API_URL=https://jsonplaceholder.typicode.com/todos/1
API_KEY=sfjks4325
PORT=3000
Шаг 2. Использование async / await с переменными окружения
Далее установите библиотеку axios, чтобы выполнять вызовы на удаленный сервер:
npm install axios --save
Эта команда установит axios и сохранит ее как зависимость в файле package.json.
Файл package.json
должен выглядеть примерно так:
{
"name": "env-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^1.5.1",
"dotenv": "^16.3.1",
"express": "^4.18.2"
}
}
Далее используйте axios для выполнения вызовов сайта jsonplaceholder и записи их в консоль.
Введите этот код в файл index.js
const express = require('express');
require('dotenv').config();
const axios = require('axios');
const app = express();
const port = process.env.PORT || 4000; // Read from .env if not available then defaults to 4000
const fetchData = async () => {
const url = process.env.API_URL;
const apiKey = process.env.API_KEY;
try {
const response = await axios.get(url, {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
console.log(response.data);
} catch (error) {
console.error(`Error fetching data: ${error}`);
}
};
app.get('/', (req, res) => {
fetchData()
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}/`);
});
Что мы здесь делаем:
Импортируем модули
express
,dotenv
,axios
.Затем инициализируем приложение и порт с помощью переменных окружения.
Затем создаем асинхронную функцию
const fetchData = async ()=> (...)
В этой функции мы используем url и apiKey из файла
.env
. Хотя я должен отметить, что вам не нужен apiKey для вызова сайта jsonplaceholder. Я просто поместил этот ключ для демонстрации.Записываем данные в консоль.
Вызываем метод
fetchData()
на маршруте get. Таким образом, каждый раз, когда кто-то переходит на/
, вызывается метод и данные записываются в консоль.
Использование Async/await с переменными окружения в операциях с базой данных
Давайте рассмотрим еще один пример. На этот раз мы будем выполнять операции с базой данных с помощью переменных окружения.
Шаг 1: Настройка переменных окружения.
Откройте файл .env
в текстовом редакторе и введите следующее:
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mydatabase
DB_USER=username
DB_PASSWORD=password
Шаг 2: Установите библиотеку pg
Следующим шагом будет установка библиотеки pg
:
npm install pg --save
*Шаг 3: * Подключитесь к базе данных и выполните асинхронный запрос с помощью файла .env
Напишите следующий код, чтобы подключиться к базе данных и использовать учетные данные .env, которые мы только что создали:
const { Pool } = require('pg');
require('dotenv').config();
const pool = new Pool({
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD
});
const fetchUsers = async () => {
try {
const res = await pool.query('SELECT * FROM users');
console.log(res.rows);
} catch (err) {
console.error(`Error fetching users: ${err}`);
}
};
fetchUsers();
Мы не интегрируем этот пример в нашу обычную кодовую базу, потому что он является исключением и не будет полезен в дальнейшем в этом руководстве.
Расширенные возможности работы с переменными окружения в Node JS: кодирование, валидация и преобразование типов
Переменные окружения — это не только чтение и хранение. С помощью расширенных манипуляций с переменными окружения мы узнаем о:
кодировании
валидации и
преобразовании типов
1. Кодирование
Кодирование используется для различных целей в переменных окружения, в том числе для обеспечения безопасности. Наиболее популярный тип кодировки — base64.
Кодирование:
const decodedData = Buffer.from(encodedData, 'base64').toString('utf-8');
console.log(`Decoded: ${decodedData}`);
2. Валидация
Валидация используется для проверки того, является ли код правильным или нет. Например, url, который вы собираетесь использовать — является ли он корректным или нет и т.д.
Или, как в этом примере, мы проверяем, находится ли номер порта в указанном диапазоне или нет.
const validatePort = (port) => {
const parsedPort = parseInt(port, 10);
if (isNaN(parsedPort) || parsedPort < 1024 || parsedPort > 65535) {
throw new Error('Invalid port number');
}
return parsedPort;
};
const port = validatePort(process.env.PORT);
3. Преобразование типа
Переменные окружения всегда имеют тип string. Но часто возникает необходимость использовать и другие типы данных, например int или booleans. Здесь поможет преобразование типов.
Пример целого числа:
const isProduction = process.env.NODE_ENV === 'production';
const retries = parseInt(process.env.RETRIES, 10) || 3;
Пример булевого значения
const shouldLog = process.env.SHOULD_LOG === 'true';
Комбинированный пример
Вот пример, объединяющий все расширенные манипуляции с env, доступные в переменных окружения.
// Encoding and decoding
const apiKey = Buffer.from(process.env.API_KEY || '', 'base64').toString('utf-8');
// Validation
if (apiKey.length !== 32) {
throw new Error('Invalid API key length');
}
// Type conversion
const maxUsers = parseInt(process.env.MAX_USERS, 10);
if (isNaN(maxUsers)) {
throw new Error('MAX_USERS must be an integer');
}
Управление секретами и лучшие практики безопасности с примерами
В этом разделе мы узнаем об управлении секретами и лучших практиках безопасности с примерами. Вот шаги, которые вы можете предпринять для защиты переменной окружения, а также их примеры.
1. Никогда не делайте коммит файлов .env
Всегда следите за тем, чтобы файлы .env находились в gitignore и никогда не коммитились в git-репозиторий.
# Ignore dotenv environment variables file
.env
2. Хеширование пароля
Всегда хэшируйте пароли, хранение паролей в виде обычного текста — плохая практика. Для хэширования паролей можно использовать, например, библиотеку bcryt.
const bcrypt = require('bcrypt');
const saltRounds = 10;
async function hashPassword(plainPassword) {
const hash = await bcrypt.hash(plainPassword, saltRounds);
return hash;
}
3. Кодирование переменных окружения
В качестве меры безопасности всегда кодируйте секреты, обычно они имеют кодировку base-64.
const buffer = Buffer.from(process.env.MY_SECRET, 'base64');
const decodedSecret = buffer.toString('utf-8');
4. Централизованное управление секретами
Для крупных проектов можно рассмотреть сервисы централизованного управления секретами — например, hashicorp vault, AWS secrets manager.
Они предлагают такие расширенные функции, как ротация секретов, автоматическая аренда и ротация, а также ведение журнала аудита.
Вот пример с node vault:
const vault = require("node-vault")({
endpoint: "https://vault-endpoint",
token: process.env.VAULT_TOKEN,
});
async function getDatabaseCreds() {
const data = await vault.read("path/to/secret");
return {
user: data.data.username,
password: data.data.password,
};
}
5. Принцип наименьших привилегий
Всегда следуйте принципу наименьших привилегий. API, которые не требуют идентификационных данных на запись, никогда не должны получать привилегии на запись.
Подводные камни
Хардкодинг служебной информации
Отсутствие валидации
Игнорирование окружения
Неадекватная безопасность
Плохая документация
Возврат в предыдущее значение (Fallback) по умолчанию
Нужен Chat API для вашего сайта или приложения
Поставщиком Chat API является DeadSimpleChat
Добавьте масштабируемый чат в приложение за считанные минуты
Одновременно 10 миллионов пользователей
Uptime 99,999%
Фичи модерации
Чат 1-1
Групповой чат
Полностью настраиваемый
Чат API и SDK
Предварительно созданный чат
Заключение
В этой статье мы узнали о переменных окружения и о том, как их использовать в Node.js.
Мы рассмотрели важные темы, связанные с управлением переменными окружения в Node.js. Этого должно быть достаточно для большинства случаев использования переменных окружения в приложениях Node.js.
Надеюсь, вам понравилась статья, и спасибо за чтение.
Всех разработчиков на JavaScript приглашаем на открытое занятие в OTUS «Построение графических приложений с применением библиотеки Konva.js». Записаться на урок можно на странице курса.