Как создать сетевой API с помощью Express.js, Bun и MongoDB

Веб-API служат центральными компонентами современного ПО, обеспечивая беспрепятственную связь между приложениями в обширном пространстве интернета.

Если вы работаете с Node.js, то наверняка знакомы со стеком MER(A)N:

  1. MongoDB;
  2. Express;
  3. React/Angular;
  4. Node.

Но с появлением Bun создавать высокопроизводительные API стало гораздо проще. В этой статье будет описан процесс разработки API Express.js с использованием MongoDB на базе Bun.

Представление инструментов

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

  • Express.js. Изящный и простой веб-фреймворк, легко интегрируемый с Node.js и позволяющий пользователям с легкостью создавать API.
  • MongoDB. NoSQL-система управления базами данных, которая отличается универсальностью и легко справляется с большими объемами данных.
  • Bun. Среда выполнения JavaScript, которая служит заменой Node.js.

Настройка приложения и создание API

1. Установка Bun

Прежде всего установим Bun. Для этого надо выполнить команду:

curl https://bun.sh/install | bash

Проверьте корректность установки, выполнив команду bun --version.

2. Создание проекта Bun

Инициализируем проект Bun:

bun create blog-api

Вы можете использовать структуру папок, показанную ниже:

/blog-api

├── src
│ ├── models
│ ├── controllers
│ ├── routes
│ └── index.ts

├── dist

├── node_modules

├── .bunrc
├── .env
├── package.json
└── tsconfig.json

3. Установка библиотек: Express.js, MongoDB и Typescript

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

bun install express mongoose 
bun install @types/express @types/mongoose typescript bun-types -d

Эти скрипты установят Express.js для сервера, MongoDB с помощью Mongoose для базы данных и Typescript для обеспечения чистоты и безопасности кода.

4. Создание базовой серверной среды

Внутри src/app.ts создайте сервер Express.js:

import express from 'express';

const app = express();

app.use(express.json());

app.get("/ping", (_, res) => {
res.send("🏓 pong!");
});

app.listen(3000, () => {
console.log('The magic happens on port 3000!');
});

Это базовое определение приложения Express.js.

Теперь убедимся в том, что API развертывается с помощью Bun. Для этого изменим скрипт npm start в файле package.json:

"scripts": {
"start": "bun run src/app.ts,"
"dev": "bun run src/app.ts --watch"
}

Запустить приложение можно с помощью простой команды:

bun dev

Режим --watch в Bun обеспечивает сверхбыстрый, нативный просмотр файлов, что резко контрастирует с Node.js, где для достижения аналогичной функциональности и удобства разработки требуются внешние инструменты вроде Nodemon.

Можете проверить, работает ли сервер, сделав GET-запрос к маршруту /ping, который ответит игривым “🏓 pong!”.

5. Проектирование схемы Mongoose

В папке src/models создадим Post.ts:

import { Schema, InferSchemaType, model } from 'mongoose';

const postSchema = new Schema(
{
title: { type: String, required: true },
content: { type: String, required: true },
author: String,
createdAt: { type: Date, default: Date. now },
}
);

export type Post = InferSchemaType<typeof postSchema>;
export const Post = model('Post', postSchema);

6. Контроллеры: определение бизнес-логики

Каждому маршруту нужна соответствующая функция контроллера.

Например, в папке controllers может быть файл postController.ts:

import { Request, Response} from 'express';

export const createPost = async (req: Request, res: Response) => {
try {
const {author, title, content} = req.body;
const post = new Post({author, title, content});
await post.save();
res.status(201).send(post);
} catch (error) {
res.status(400).send(error);
}
};

export const readPost = async (req: Request, res: Response) => {
try {
const posts = await Post.find({});
res.status(200).send(posts);
} catch (error) {
res.status(500).send(error);
}
};

export const readPosts = async (req: Request, res: Response) => {
try {
const { id } = req.params;
const post = await Post.findById(id);
if (!post) {
res.status(404).send();
}
res.status(200).send(post);
} catch (error) {
res.status(500).send(error);
}
};

export const updatePost = async (req: Request, res: Response) => {
try {
const { id } = req.params;
const post = await Post.findByIdAndUpdate(id,
req.body,
{
new: true,
runValidators: true
});
if (!post) {
res.status(404).send();
}
res.status(200).send(post);
} catch (error) {
res.status(400).send(error);
}
};

export const deletePost = async (req: Request, res: Response) => {
try {
const { id } = req.params;
const post = await Post.findByIdAndDelete(id);
if (!post) {
res.status(404).send("Post wasn't found");
}
res.status(200).send(post);
} catch (error) {
res.status(500).send(error);
}
};

Не забывайте об ответственной обработке ошибок. Заключите контроллеры в блоки try-catch и отправляйте клиенту информативные ответы. Впоследствии импортируйте эти функции в свои маршруты и включайте их по мере необходимости.

7. Добавление маршрутов к бизнес-функциям

Для API блога понадобятся следующие CRUD-маршруты.

  • Create a post (создать пост).
  • Read a post (прочитать пост).
  • Update a post (обновить пост).
  • Delete a post (удалить пост).

В каталоге src/routes создайте файл blogRoutes.ts со следующими параметрами:

import express from 'express';

const router = express.Router();

import { createPost, readPost, readPosts, updatePost, deletePost } from '../controllers/postController';

router.post('/post', createPost);
router.get('/posts', readPosts);
router.get('/post/:id', readPost);
router.put('/post/:id', updatePost);
router.delete('/post/:id', deletePost);

export default router;

8. Настройка MongoDB

Настало время реализовать подключение к базе данных. Добавьте следующее в src/config/db.ts:

import mongoose from 'mongoose';

export default function connectDB() {

const mongoURI = process.env.MONGODB_URI ?? 'mongodb://localhost:27017/mydatabase';

try {
mongoose.connect(mongoURI);
} catch (error) {
const castedError = error as Error;
console.error(castedError.message);
process.exit(1);
}

mongoose.connection.once("open", (_) => {
console.log(`Database connected`);
});

mongoose.connection.on("error", (err) => {
console.error(`Database connection error: ${err}`);
});

}

Теперь создайте файл .env в корне проекта и добавьте туда свои настройки, например MONGODB_URI.

Такая конфигурация обеспечивает доступ к переменным среды напрямую, без помощи утилит вроде dotenv, которые часто используются в проектах Node.js. Это достижимо, поскольку Bun по умолчанию поддерживает доступ к переменным среды через файлы .env.

9. Собираем все вместе

После подготовки моделей, маршрутов и контроллеров включите их в файл index.ts. После этой интеграции приложение будет готово к запуску.

import express from "express";
import postRoute from "./routes/postRoute"
import connectDB from "./config/db"


const app = express();
const port = process.env.APP_PORT || 8080;

// подключение к базе данных
connectDB();

// промежуточное ПО
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get("/ping", (_, res) => {
res.send("🏓 pong!");
});

// маршруты
app.use('/api/post', postRoute);

app.listen(port, () => {
console.log(Listening on port: ${port} );
});

Теперь API на базе Bun готов.


Добавление функций мониторинга в приложения Express.js

Логирование запросов в Express

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

Для начала нужно установить Morgan. Это можно сделать через Bun:

bun add morgan
bun add @types/morgan -d

Необходимо добавить Morgan в качестве промежуточного ПО в приложение Express. Это делается с помощью app.use(). Morgan имеет различные предопределенные форматы логирования, такие как dev, tiny, combined, common и т. д. Пользователь также может создать собственный формат. Добавим пользовательский формат в файл index.ts.

app.use(morgan(':method :url :status :response-time ms'));

Morgan подключен к приложению Express и готов регистрировать все HTTP-запросы. Ведение лога очень удобно для контроля и устранения ошибок. Гибкость Morgan позволяет настроить его в соответствии с потребностями пользователя.

Валидация данных в Express

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

Прежде всего установите его в проект следующим образом:

bun add express-validator
bun add -d @types/express-validator

Затем в файле маршрута postRoute.ts нужно будет импортировать и использовать его так, как показано ниже:

const router = express.Router();

export const postValidator = [
body('title').notEmpty(),
body('content').notEmpty(),
body('author').notEmpty(),
];

router.post('/post',postValidator, createPost);
// другие маршруты

В приведенном выше коде видно, что еще до перехода к обработчику маршрута /post, Express-validator проверит поля имени пользователя, электронной почты и пароля на соответствие установленным критериям. Чтобы протестировать операцию валидации, можно сделать POST-запрос, не передав требуемый ключ author или передав пустую строку для одного из полей. Это вызовет ответ об ошибке, указывающий на отсутствие или пустоту поля.

Для тех, кто хочет узнать больше, официальная документация по Express.js, MongoDB и Bun  —  отличная отправная точка. Но этой статьи должно быть достаточно для создания готового к производству приложения с помощью Bun.

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

Читайте нас в Telegram, VK и Дзен


Перевод статьи Tharaka Romesh: Building a Web API using Express.js, Bun and MongoDB

Предыдущая статьяОшибки в Rust: формула
Следующая статьяAndroid 14: обновления в области конфиденциальности и безопасности