Зарегистрируйтесь, чтобы продолжить обучение

Плейсхолдеры JS: SQL

Как правило, запросы в базу формируются динамически на основе данных, которые поступают извне. Например, создание пользователя чаще всего происходит во время регистрации. В таком случае код вставки мог бы выглядеть например так:

const name = 'Tony Stark'
const phone = '+123456789'

await sql.unsafe`INSERT INTO users (username, phone) VALUES (${name1}, ${phone1})`

Составленные таким образом запросы несут в себе серьезную опасность — SQL-инъекции. Вместо обычных данных злоумышленник может отправить текст, который изменит SQL-запрос. Представьте, что внутри переменной хранилась бы такая строка:

const name = 'somename'
const phone = '); INSERT INTO users (username, phone) VALUES (\'i am hacker\', \'777777\''

await pool.query(sql.unsafe`
  INSERT INTO users (username, phone) VALUES (${name}, ${phone})
`)

В таком случае после подстановки мог бы получиться следующий запрос:

INSERT INTO users (username, phone) VALUES ('somename', '');
INSERT INTO users (username, phone) VALUES ('i am hacker', '777777');

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

В разработке существует правило «Никогда не доверяй пользовательским данным» — оно особенно важно в работе с базами данных. Любые данные перед вставкой нужно экранировать с помощью подготовленных запросов.

К счастью для нас библиотека умеет обрабатывать такие случаи, ничего специально для этого делать не нужно:

const name = 'somename'
const phone = '); INSERT INTO users (username, phone) VALUES (\'i am hacker\', \'777777\''

await pool.query(sql.unsafe`
  INSERT INTO users (username, phone) VALUES (${name}, ${phone})
`)

const result = await pool.query(sql.unsafe`
  INSERT INTO users (username, phone) VALUES (${name}, ${phone})
`)

console.log(result.rows) // =>
// [{
//   username: 'Tony Stark',
//   phone: "'); INSERT INTO users (username, phone) VALUES ('i am hacker', '777777"
// }]

Так же библиотека предоставляет функционал для формирования запросов с использованием массивов:

const ids = [1, 2, 3]
const result = await pool.query(sql.unsafe`
  SELECT * FROM users WHERE id IN (${sql.join(ids, sql`,`)})
`)

Метод join() позволяет объединять массивы значений в строку с использованием определенного разделителя. Обычно используется при создании списков значений для операторов IN.

Возврат идентификатора

Когда мы вставляем данные в базу, иногда нам нужно получить идентификатор вставленной записи и потом использовать его в коде. Это происходит, когда мы создаем какую-то сущность и хотим потом ее использовать, например, для формирования ссылок или вставки связанных записей.

Чтобы выполнить эту задачу, немного изменим запрос:

const name = 'Tony Stark'
const phone = '+123456789'

const { rows } = await pool.query(sql.unsafe`
  INSERT INTO users (username, phone) VALUES (${name}, ${phone}) RETURNING id
`)

console.log(rows[0].id) // => 1

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff