Работа с базами данных в TypeScript: PostgreSQL и MongoDB на практике

Онлайн-тренажер Python для начинающих

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

Начать курс

Введение: Почему TypeScript и базы данных — идеальная пара

Современная веб-разработка немыслима без работы с базами данных (БД). TypeScript, как строго типизированная надстройка над JavaScript, предлагает разработчикам мощный инструмент для создания надежных и масштабируемых приложений. В этой статье мы на практике разберем, как подключать и использовать две самые популярные базы данных — реляционную PostgreSQL и документо-ориентированную MongoDB — в экосистеме TypeScript.

Вы узнаете, как настроить ORM/ODM, написать типизированные модели и выполнить базовые CRUD-операции. Материал рассчитан на разработчиков, которые уже знакомы с основами TypeScript и Node.js.

1. Настройка проекта и установка зависимостей

Прежде чем писать код, создадим новый проект и установим необходимые пакеты. Для работы с PostgreSQL мы будем использовать TypeORM — один из самых популярных ORM для TypeScript. Для MongoDB — Mongoose.

Инициализация проекта

mkdir typescript-db-practice
cd typescript-db-practice
npm init -y
npm install typescript ts-node @types/node --save-dev
npx tsc --init

Установка зависимостей для PostgreSQL

npm install typeorm reflect-metadata pg
npm install @types/node --save-dev

Установка зависимостей для MongoDB

npm install mongoose
npm install @types/mongoose --save-dev

После установки в файле tsconfig.json обязательно включите опции "experimentalDecorators": true и "emitDecoratorMetadata": true — они необходимы для работы TypeORM.

2. Работа с PostgreSQL через TypeORM

TypeORM поддерживает как Active Record, так и Data Mapper паттерны. Мы рассмотрим Data Mapper, так как он дает больше гибкости и лучше сочетается с чистой архитектурой.

Создание сущности (Entity)

Создадим модель пользователя. Каждая сущность — это класс, декорированный специальными аннотациями.

// src/entities/User.ts
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;

    @Column({ length: 100 })
    name: string;

    @Column({ unique: true })
    email: string;

    @Column({ select: false })
    password: string;

    @CreateDateColumn()
    createdAt: Date;
}

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

Создадим файл для инициализации базы данных и репозиторий для работы с пользователями.

// src/data-source.ts
import "reflect-metadata";
import { DataSource } from "typeorm";
import { User } from "./entities/User";

export const AppDataSource = new DataSource({
    type: "postgres",
    host: "localhost",
    port: 5432,
    username: "postgres",
    password: "password",
    database: "test_db",
    synchronize: true, // Авто-создание таблиц (только для разработки!)
    logging: false,
    entities: [User],
    migrations: [],
    subscribers: [],
});
// src/repositories/userRepository.ts
import { AppDataSource } from "../data-source";
import { User } from "../entities/User";

export const userRepository = AppDataSource.getRepository(User);

CRUD операции на практике

// src/index.ts
import { AppDataSource } from "./data-source";
import { userRepository } from "./repositories/userRepository";

async function main() {
    await AppDataSource.initialize();
    console.log("PostgreSQL connected!");

    // CREATE
    const newUser = userRepository.create({
        name: "Иван Петров",
        email: "ivan@example.com",
        password: "hashed_password"
    });
    await userRepository.save(newUser);
    console.log("User created:", newUser);

    // READ
    const users = await userRepository.find();
    console.log("All users:", users);

    // UPDATE
    await userRepository.update(1, { name: "Иван Сидоров" });

    // DELETE
    await userRepository.delete(1);
}

main().catch(console.error);

Важно: В production используйте миграции вместо synchronize: true.

3. Работа с MongoDB через Mongoose

Mongoose — это ODM (Object Document Mapper) для MongoDB. Он предоставляет удобную схему для валидации данных и типизации.

Определение схемы и модели

// src/models/Product.ts
import mongoose, { Schema, Document } from 'mongoose';

export interface IProduct extends Document {
    name: string;
    price: number;
    inStock: boolean;
    tags: string[];
    createdAt: Date;
}

const ProductSchema: Schema = new Schema({
    name: { type: String, required: true, maxlength: 200 },
    price: { type: Number, required: true, min: 0 },
    inStock: { type: Boolean, default: true },
    tags: { type: [String], default: [] },
    createdAt: { type: Date, default: Date.now }
});

export const Product = mongoose.model<IProduct>('Product', ProductSchema);

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

// src/mongo-index.ts
import mongoose from 'mongoose';
import { Product } from './models/Product';

async function runMongo() {
    await mongoose.connect('mongodb://localhost:27017/shop');
    console.log("MongoDB connected!");

    // CREATE
    const laptop = new Product({
        name: "Ноутбук Dell XPS 15",
        price: 1499.99,
        tags: ["электроника", "ноутбуки"]
    });
    await laptop.save();
    console.log("Product saved:", laptop);

    // READ (с фильтром)
    const cheapProducts = await Product.find({ price: { $lt: 1000 } });
    console.log("Cheap products:", cheapProducts);

    // UPDATE (одно поле)
    await Product.updateOne(
        { name: "Ноутбук Dell XPS 15" },
        { $set: { price: 1399.99 } }
    );

    // DELETE
    await Product.deleteOne({ _id: laptop._id });
}

runMongo().catch(console.error);

Обратите внимание: Mongoose автоматически преобразует типы TypeScript в типы BSON, поддерживаемые MongoDB.

4. Сравнение подходов и лучшие практики

Выбор между SQL и NoSQL зависит от структуры данных. Вот несколько практических рекомендаций для TypeScript-разработчиков:

  • Типизация — ваш друг. Всегда создавайте интерфейсы для документов MongoDB и используйте декораторы TypeORM. Это поможет избежать ошибок на этапе компиляции.
  • Используйте DTO (Data Transfer Objects). Не передавайте сущности напрямую в HTTP-ответы. Создавайте отдельные типы для API-слоя.
  • Обработка ошибок. Всегда оборачи

Блоги

Рекомендаций по книгам