В следующей главе я покажу вам, как вы можете настроить приложение Node.js для работы с базой данных и научу вас основам её использования.

This article was translated to Russian by Andrey Melikhov, a front-end developer from Yandex.Money and editor of the collective blog about front-end, devSchacht. Find Andrey on: Twitter, GitHub, Medium & SoundCloud

Read the original article in English: Node.js database tutorial.

Перевод этой статьи сделан Андреем Мелиховым, фронтенд-разработчиком из компании Яндекс.Деньги, редактором коллективного блога о фронтенде, devSchacht. Twitter | GitHub | Medium | SoundCloud



Хранение данных в глобальной переменной

Раздача статических страниц пользователям (как это делать вы узнали в предыдущей главе) может быть подходящим вариантом для лэндингов или для личных блогов. Однако, если вы хотите отдавать персонализированный контент, вам придётся где-то хранить данные.

Возьмём простой пример: регистрация пользователя. Вы можете отдавать пользовательский контент для отдельных пользователей или сделать его доступным для них только после идентификации.

Если пользователь хочет зарегистрироваться в вашем приложении, вы можете создать обработчик соответствующего роута, чтобы сделать регистрацию возможной:

const users = []
app.post('/users', function (req, res) {
  // извлекаем данные пользователя из тела запроса
  const user = req.body
  users.push({
    name: user.name,
    age: user.age
  })
  res.send('successfully registered')
})

Таким образом, вы можете хранить пользователей в глобальной переменной, которая будет храниться в памяти в течение всего срока жизни вашего приложения.

Использование этого метода может быть проблематичным по нескольким причинам:
• оперативная память дорогая,
• память очищается каждый раз, когда вы перезапускаете ваше приложение,
• если вы не будете чистить память, иногда вы можете получить переполнение памяти.

Хранение данных в файле

Следующее решение, которое может прийти к вам в голову — это хранить данные в файлах.

Если мы постоянно сохраняем наши пользовательские данные в файловой системе, мы можем избежать ранее перечисленных проблем.

На практике этот метод выглядит следующим образом:

const fs = require('fs')

app.post('/users', function (req, res) {
  const user = req.body
  fs.appendToFile('users.txt', JSON.stringify({name: user.name, age: user.age }), (err) => {  
    res.send('successfully registered')
  })
})
 

Таким образом, мы не потеряем пользовательские данные даже после перезагрузки сервера. Это решение также экономически выгодно, так как увеличение дискового пространства дешевле, чем покупка ОЗУ.

К сожалению, хранение пользовательских данных таким образом все ещё имеет несколько недостатков:

  • Добавление данных работает неплохо, но подумайте об обновлении или удалении.
  • Если мы работаем с файлами, нет простого решения для параллельные доступа к ним (системные блокировки не позволят вам писать в один файл параллельно).
  • Когда мы пытаемся масштабировать наше приложение, мы не можем разделить файлы между серверами (на самом деле можем, но это далеко за пределами уровня этого руководства).

Здесь выходят на сцену настоящие базы данных.

Возможно, вы уже слышали, что существуют два основных типа баз данных: SQL и NoSQL.

SQL

Начнём с SQL. Это язык запросов, предназначенный для работы с реляционными базами данных. SQL немного отличается в зависимости от продукта, который вы используете, но основы одинаковы в каждом из них.

Сами данные хранятся в таблицах, и каждая добавленная часть будет представлена в виде строки в таблице, как в Google Sheets или Microsoft Excel.

В базе данных SQL вы можете определить схемы — эти схемы предоставят скелет для данных, которые вы собираетесь разместить. Перед тем, как сохранить данные, необходимо задать типы различных значений. Например, вам нужно будет определить таблицу для ваших пользовательских данных и сообщить базе данных, что у неё есть имя пользователя, которое является строкой, и возраст, который является целым типом.

NoSQL

С другой стороны, NoSQL базы данных стали весьма популярными в последнее десятилетие. С NoSQL вам не нужно определять схему, и вы можете хранить любой произвольный JSON. Это хорошо сочетается с JavaScript, потому что мы можем легко превратить любой объект в JSON. Будьте осторожны, потому что вы никогда не можете гарантировать, что данные консистентны, и вы никогда не сможете узнать, какая структура находится в базе данных.

Node.js и MongoDB

Существует распространённое заблуждение о Node.js, которое мы слышим довольно часто:

«Node.js можно использовать только с MongoDB (которая является самой популярной NoSQL базой данных)».

По моему опыту, это не так. У большинства баз данных имеются драйверы для Node.js, и у них также есть библиотеки в NPM. По моему мнению, они такие же простые и лёгкие в использовании, как MongoDB.

Node.js и PostgreSQL

Для простоты мы будем использовать SQL в следующем примере. Мой выбор — PostgreSQL.

Чтобы запустить PostgreSQL, вам необходимо установить его на свой компьютер. Если вы используете Mac, вы можете использовать Homebrew для установки PostgreSQL. В противном случае, если вы работаете в Linux, вы можете установить его с помощью своего диспетчера пакетов.

Для получения дополнительной информации ознакомьтесь с этим отличным руководством по началу работы с вашей первой базой данных.

Если вы планируете использовать инструмент для просмотра базы данных, я бы рекомендовал утилиту для командной строки — psql. Она поставляется вместе с сервером PostgreSQL. Вот небольшая инструкция, которая пригодится, если вы начнёте её использовать.

Если вам не нравится интерфейс командной строки, вы можете использовать pgAdmin, который является инструментом с открытым исходным кодом и предназначен для администрирования PostgreSQL.

Обратите внимание, что SQL — это сам по себе язык программирования. Мы не будем рассматривать все его функции, только наиболее простые. Если нужно изучить его глубже, то в Интернете есть много отличных онлайн-курсов, которые охватывают все основы PostgreSQL.

Взаимодействие Node.js с базой данных

Во-первых, мы должны создать базу данных, которую мы будем использовать. Для этого введите следующую команду в терминал: createdb node_hero.

Затем мы должны создать таблицу для наших пользователей.

CREATE TABLE users(
  name VARCHAR(20),
  age SMALLINT
);

Наконец, мы можем вернуться к программированию. Вот как вы можете взаимодействовать с вашей базой данных через вашу программу на Node.js.

‘use strict’

const pg = require('pg')
const conString = 'postgres://username:password@ localhost/node_hero' // убедитесь, что вы указали данные от вашей базы данных

pg.connect(conString, function (err, client, done) {
  if (err) {
    return console.error('error fetching client from pool', err)
  }
  client.query('SELECT $1::varchar AS my_ rst_query', ['node hero'], function (err, result) {
    done()

    if (err) {
      return console.error('error happened during query', err)
    }
    console.log(result.rows[0])
    process.exit(0)
  })
})

Это был простой пример — "hello world" в PostgreSQL. Обратите внимание, что первым параметром является строка, которая является нашей SQL-командой, второй параметр представляет собой массив значений, которыми мы хотели бы параметризовать наш запрос.

Большой ошибкой с точки зрения безопасности был бы ввод данных, пришедших от пользователя, в том виде, в котором они были переданы. Приведённая выше функция client.query защищает вас от SQL-инъекций, которые являются распространённым видом атаки, когда злоумышленник пытается внедрить в запрос произвольный SQL-код. Всегда учитывайте это при создании любого приложения, в котором возможен ввод данных со стороны пользователя. Чтобы узнать больше, ознакомьтесь с нашим контрольным списком безопасности Node.js-приложений.

Давайте продолжим наш предыдущий пример.

app.post('/users', function (req, res, next) {
  const user = req.body

  pg.connect(conString, function (err, client, done) {
    if (err) {
      // Передача ошибки в обработчик express
      return next(err)
    }
    client.query('INSERT INTO users (name, age) VALUES ($1, $2);', [user.name, user.age], function (err, result) {
      done() // Этот коллбек сигнализирует драйверу pg, что соединение может быть закрыто или возвращено в пул соединений
      if (err) {
        // Передача ошибки в обработчик express
        return next(err)
      }
      res.send(200)
    })
  })
})

Достижение разблокировано: пользователь сохранён в базе данных! :) Теперь давайте попробуем прочитать эти данные. Затем добавим новый роут для поиска пользователей в наше приложение.

app.get('/users', function (req, res, next {
  pg.connect(conString, function (err, client, done) {
    if (err) {
      // Передача ошибки в обработчик express
      return next(err)
    }
    client.query('SELECT name, age FROM users;', [], function (err, result) {
      done()
      if (err) {
        // Передача ошибки в обработчик express
        return next(err)
      }
      res.json(result.rows) 
    })
  })
})

Это было не так сложно, не так ли?

Теперь вы можете запустить любой сложный SQL-запрос, который вы можете найти в вашем Node.js-приложении.

С помощью этой техники вы можете постоянно хранить данные в своём приложении, и благодаря трудолюбивой команде разработчиков модуля node-postgres это проще простого.

Мы рассмотрели все основы, которые вы должны знать об использовании баз данных в Node.js. Теперь попробуйте создать что-то самостоятельно.


Пробуйте всё и экспериментируйте, потому что это лучший способ стать настоящим героем Node.js! Практикуйтесь и будьте готовы к следующей главе о том, как общаться со сторонними API!