Знакомство с SurrealDB с помощью Express.js, Node.js и TypeScript

Если вы следите за новостями в области программирования, то, возможно, слышали об одной из последних разработок  —  базе данных SurrealDB. Она все еще находится в стадии бета-тестирования, но вызывает немалый интерес.

Что такое SurrealDB

Начнем с выяснения того, что такое SurrealDB и чем отличается от любой другой базы данных. Мы привыкли считать, что базы данных ограничены определенной архитектурной парадигмой, будь то реляционная, документная, графовая и любая другая. Каждая из этих парадигм имеет свои сильные и слабые стороны и часто применяется для решения конкретных задач.

Кроме того, базы данных можно разделить на два основных типа: SQL и NoSQL. SQL (Structured Query Language, язык структурированных запросов) предписывает использовать синтаксис, следуя которому запросы к базе данных достигают максимальной эффективности, производительности и читабельности. Базы данных SQL являются наиболее распространенными, но и у SQL, и у NoSQL есть свои преимущества и недостатки, обычно обусловленные одной из вышеупомянутых парадигм.

Описание SurrealDB

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

SurrealDB  —  это и реляционная база данных, и база данных документов, и база графических данных. Вы можете применять все из них одновременно. Кроме того, в ней используется SQL, только расшифровывается это в данном случае как Surreal Query Language (язык сюрреальных запросов). Таким образом, технически это NoSQL (нереляционная база данных), но, вероятно, большинство SQL-разработчиков не сразу это заметят, поскольку SurrealQL  —  это расширение существующего синтаксиса SQL.

Феномен SurrealDB заключается в объединении достоинств многих архитектурных парадигм в нечто, что эффективнее работает в более широком спектре проектов. Денежным стимулом для разработчиков является размещение бессерверных управляемых экземпляров SurrealDB, которые можно легко настроить и которые действуют скорее как REST API.

Rust

Есть еще вишенка на торте, и это Rust  —  язык программирования, сохраняющий популярность в течение последних нескольких лет, по крайней мере по версии Stack Overflow. Программы на Rust, как правило, довольно быстрые, и не зря разработчики предпочитают работать с этим языком.

Основная идея

SurrealDB призвана сменить парадигму в технологиях баз данных. По крайней мере в теории, она объединяет преимущества реляционной, документной и графовой архитектур в краткую и быструю базу данных, для работы с которой можно использовать SQL-подобный синтаксис.

Как использовать SurrealDB

Чтобы следовать дальнейшим инструкциям, вам потребуется установить SurrealDB. На момент написания статьи облачная платформа еще не работала, поэтому базу данных нужно установить самостоятельно. Затем убедитесь, что она добавлена в PATH, и выполните стандартную команду surreal --help, чтобы проверить ее работоспособность.

Совет: если видите исполняемый файл surreal в path, но он не запускается в терминале, возможно, нужно открыть новое окно терминала.

Работа с CLI

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

Чтобы запустить базу данных в памяти, выполним следующую команду:

surreal start --log trace --user root --pass root memory

Более подробную информацию можно найти в документации, поэтому не будем вдаваться в детали. Важно помнить, что Surreal запускается в памяти и по умолчанию инициализируется на порту 8000.

Поскольку мне было трудно найти пример, можно инициализировать базу данных постоянного хранения, указав в команде старта вместо памяти параметр --path:

surreal start --log trace --user root --pass root file://"[absolute_path_to_directory_to_store_database]"

Теперь можно взаимодействовать с новой базой данных в памяти с помощью этой команды:

surreal sql --conn http://localhost:8000 --user root --pass root --ns test --db test

Она откроет CLI, который позволяет запускать SurrealQL в базе данных. Попробуйте сделать следующее:

> CREATE users:test SET name = "test_user"

Вы увидите нечто подобное:

[{"time":"1.6982ms","status":"OK","result":[{"id":"users:test","name":"test_user"}]}]

Это подтверждает, что вы создали новую запись в таблице users с id users:test и именем test_user. Вы можете выбрать этого пользователя, выполнив следующий запрос:

> SELECT * FROM users WHERE name = "test_user"

Все очень просто и напоминает SQL. Однако вам не придется создавать приложение с запросами вручную, тем более из CLI.

Инициализация Express

Весь код, приведенный в этом руководстве, находится на GitHub.

В этом руководстве мы будем использовать Node.js, TypeScript и Express, что является довольно базовым стеком. Если вы не работали с TypeScript, не волнуйтесь  —  процесс будет подробно объяснен. Убедитесь, что у вас установлен Node.

Начнем с инициализации нового проекта npm в выбранном вами каталоге. Рекомендую просто скопировать мой package.json (приведен ниже) вместо того, чтобы самостоятельно устанавливать зависимости:

{
"name": "surrealdb_express",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"scripts": {
"start": "ts-node index.ts",
"dev": "ts-node-dev index.ts",
"init": "tsc --init"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"dotenv": "^16.0.3",
"express": "^4.18.1",
"surrealdb.js": "^0.5.0",
"ts-node": "^10.9.1",
"ts-node-dev": "^2.0.0",
"typescript": "^4.8.4"
},
"devDependencies": {
"@types/express": "^4.17.14"
}
}

Затем инициализируем файл tsconfig.json в корне проекта, выполнив следующую команду:

npm run init

Это работает, потому что TypeScript устанавливается как пакет npm из package.json, в котором также указан скрипт init. После этого в корне проекта должен появиться файл tsconfig.json. Он содержит опции для компиляции TypeScript, для которых характерны широкие возможности настроек. Однако в данном случае мы будем использовать параметры по умолчанию.

После появления tsconfig.json нужно настроить еще один файл конфигурации  —  .env. Это отличный способ хранения секретов, которые не должны быть легко доступны из исходного кода. Этот файл также отлично подходит для хранения переменных, которые могут меняться в зависимости от среды. Поэтому мы будем использовать его для хранения хоста базы данных, порта и учетных данных.

Создайте файл .env в корне проекта и добавьте в него следующее. Предполагается, что вы использовали приведенные выше команды для запуска SurrealDB на port 8000 с учетными данными по умолчанию.

PORT=3000

SURREAL_DB_URL=http://localhost:8000/rpc

SURREAL_DB_USER=root
SURREAL_DB_PASSWORD=root

Теперь, когда конфигурационные файлы подготовлены, запустим приложение. Создайте файл index.ts в корне проекта, если у вас его еще нет. В этот файл добавьте следующее:

import Express from "express";
import { config } from "dotenv";
config();
const app = Express();
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.listen(process.env.PORT || 3000, () => {
console.log(`Server started on port ${process.env.PORT}`);
});

Это настройка простого приложения Express, использующего PORT, указанный в файле .env. Функция config() инициализирует объект process.env с парами “ключ-значение”, помещенными в файл .env. Теперь можно получить к ним доступ в проекте. Выполним следующую команду и перейдем на localhost:3000 в браузере.

npm run dev

Подключение к базе данных

Теперь вы увидите в браузере текст “Hello World”. Это означает, что приложение Express запущено. Соединения с базой данных мы будем обрабатывать в отдельном файле. Создадим новую папку в корневом каталоге под названием utils и добавим в нее файл surrealdb.ts.

Теперь используем следующий код, чтобы установить соединение с базой данных. Начнем с импорта зависимостей и инициализации экземпляра Surreal:

import Surreal from "surrealdb.js";
import { config } from "dotenv";
config();
const {
SURREAL_DB_HOST: db_url,
SURREAL_DB_USER: db_user,
SURREAL_DB_PASSWORD: db_pass,
} = process.env;
const db = new Surreal(db_url);

Как видите, мы импортируем класс Surreal из библиотеки surrealdb.js. Затем настраиваем переменные env. После этого можно использовать класс Surreal для инициализации экземпляра db.

Теперь осталось создать функцию, которую можно использовать для подготовки к работе класса DB при запуске приложения. В том же файле, ниже только что написанного кода, напишите следующую функцию:

export async function initDB() {
try {
console.log("Initializing database...");
if (!db_user || !db_pass || !db_url) {
throw new Error("DB_USERNAME or DB_PASSWORD not set")
}
await db
.connect(db_url)
.then(() => {
console.log("Connected to database");
})
.catch((err) => {
console.log("Error connecting to database", err);
});

await db
.signin({
user: db_user,
pass: db_pass,
})
.then((res) => {
console.log("Signed in to database", res);
})
.catch((err) => {
console.log("Error signing in to database", err);
});

await db.use("test", "test");
} catch (err) {
console.error(err);
}
}
export default db;

Эта функция выполняет несколько действий. Во-первых, подключается к экземпляру базы данных, запущенному в терминале на PORT 8000. Во-вторых, по умолчанию использует учетные данные root root, которые указаны в .env и заданы в команде surreal start. Наконец, мы указываем, какое пространство имен и базу данных нужно использовать (в данном случае test test).

Последняя строка тоже важна. Нам нужно экспортировать экземпляр db из этого файла, чтобы к нему можно было получать доступ в рамках всего проекта. Как только запустим функцию initDB и она не выдаст никаких ошибок, сможем использовать другие методы, которые предоставляет db.

Вернемся к index.ts. Импортируем функцию initDB и активируем ее при запуске приложения:

import Express from "express";
import { config } from "dotenv";
import { initDB } from "./utils/surrealdb";
config();
initDB();
const app = Express();
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.listen(process.env.PORT || 3000, () => {
console.log(`Server started on port ${process.env.PORT}`);
});

Запустите приложение снова и увидите следующий лог:

Initializing database...
Server started on port 3000
Connected to database
Signed in to database

Это означает, что все прошло успешно и соединение с базой данных установлено. Теперь мы можем приступить к самой интересной части. Не будем углубляться в то, как настраивать приложение, а лишь вкратце рассмотрим, как использовать метод db.query. Допустим, вы создаете новый файл для своих запросов:

import { db } from "utils/surrealdb";

const newQuery = db.query("CREATE test:test SET name=$variable", {
    variable: "test"
});

Как видите, метод query очень похож на то, что мы делали в командной строке, но с одним заметным исключением  —  синтаксисом $variable. Очень важно использовать именно этот синтаксис и не пытаться применять другие способы для предотвращения атак SQL-инъекций, такие как интерполяция строк. Как видите, при указании переменной в запросе используется синтаксис $[variable_name] и указывается значение переменной вторым аргументом.

Если хотите узнать больше о методе db.query, документация доступна здесь. Там можно узнать и о других методах экземпляра db.

Заключение

Теперь вы знаете, как создать приложение на SurrealDB, Express и TypeScript. SurrealDB  —  еще совсем новая база данных, которая находится в стадии бета-версии.

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

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

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Aidan Tilgner: SurrealDB Explained With Express.js, Node.js, and TypeScript

Предыдущая статьяWasp  —  DSL-язык для современных веб-приложений
Следующая статьяКакие типы изображений можно создавать в Midjourney