Новое:Talanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

+1 487новых вакансий за сутки·92 551вакансия открыта сейчас·Новое:Talanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

Новые вакансии в TelegramРаботодателям
Talanto
Вакансии
Компании7 220
РезюмеAIПодготовкаСтатьи
BoostТарифыBoost
Войти

Не нашли подходящее?

Пусть нужная вакансия найдёт вас сама.

  1. Подборка в TelegramНовые вакансии по вашему фильтру — сразу в бота.Подключить
  2. Мэтчи по резюмеЗагрузите резюме — покажем, где вы подходите лучше всего.Загрузить
  3. Сохранить фильтрНастройте поиск один раз и возвращайтесь к новым вакансиям.Настроить
Что нового на TalantoTalanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

Для работодателей

Опубликуйте вакансию — отклики придут в ваш кабинет.

  1. Вакансиябесплатно
  2. Откликиконтакты бесплатно
  3. Наймэтапы и интервью

40 компаний публикуют вакансии напрямую · 4 402 кандидата открыты к предложениям

Разместить вакансию

Популярные направления

Все подборки
  • Python17 105
  • Облачные технологии14 073
  • QA вакансии3 460
  • Backend вакансии2 098
  • Инженер данных1 805
  • Продакт-менеджер1 266

Разработка

  • Android1 866
  • iOS1 699
  • Fullstack вакансии979
  • AI вакансии951
  • Frontend вакансии742
  • Mobile вакансии44

По стеку

  • Go8 651
  • Java6 544
  • Node.js2 018
  • .NET1 719
  • Kotlin1 623
  • Rust1 453
  • C#709
  • PHP683
  • C++571

DevOps и инфраструктура

  • Kubernetes7 763
  • DevOps вакансии6 057
  • Linux-инфраструктура5 492
  • Terraform / IaC4 261
  • CI/CD3 240
  • Мониторинг и Observability1 783
  • SRE566
  • Кибербезопасность453

Аналитика и данные

  • Data Scientist1 140
  • Бизнес-аналитик930
  • Аналитик данных853
  • Системный аналитик154

Другие направления

  • Проджект-менеджер615
  • UI/UX дизайн146
  • Ручное тестирование14

По регионам

  • Вакансии в США43 685
  • Вакансии в Европе23 862
  • Вакансии в Канаде4 413
  • Работа в России3 909

По формату

  • Удалённые вакансии73 887
  • Релокация в США43 685
  • Гибридная работа12 392
  • Офисная работа7 088

По уровню

  • Senior вакансии42 236
  • Middle вакансии20 545
  • Junior вакансии6 653

Сейчас нанимают

Все компании
  • Wise190 вакансий
  • Rakuten189 вакансий
  • ASML166 вакансий
  • Grab138 вакансий
  • Celonis39 вакансий
  • Spotify36 вакансий
  • Adyen29 вакансий
  • Infobip28 вакансий
  • Monzo20 вакансий
  • Darktrace16 вакансий

Вакансии из 344 источников: карьерные страницы компаний и джоб-борды.

КомпанииО насНовостиПубличная офертаУсловия использованияПолитика конфиденциальностиПоддержка
←К теме NestJSВсе вопросы
NestJSSeniorКодинг

Как обрабатывать транзакции базы данных в NestJS с TypeORM?

В закладки
Сначала попробуйте ответить сами30 секунд
Сравнить с ответом ↓

В NestJS+TypeORM транзакции оформляют через DataSource.transaction(), QueryRunner с явным управлением или декоратор @Transaction(). Предпочтителен QueryRunner: он даёт полный контроль над коммитом, откатом и вложенными savepoint-ами.

Подход 1: DataSource.transaction() — простой callback

Самый компактный способ для атомарных операций без сложной логики:

import { Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { User } from './user.entity';
import { Wallet } from './wallet.entity';

@Injectable()
export class UserService {
  constructor(private readonly dataSource: DataSource) {}

  async createUserWithWallet(name: string, email: string): Promise<User> {
    return this.dataSource.transaction(async (manager) => {
      const user = manager.create(User, { name, email });
      await manager.save(user);

      const wallet = manager.create(Wallet, { userId: user.id, balance: 0 });
      await manager.save(wallet);

      return user; // при выбросе исключения — автоматический rollback
    });
  }
}

Транзакции в NestJS с TypeORM

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

Подход 2: QueryRunner — полный контроль

Рекомендуется для сложных сценариев: savepoint, условный rollback, несколько этапов коммита:

import { Injectable, InternalServerErrorException } from '@nestjs/common';
import { DataSource, QueryRunner } from 'typeorm';
import { Order } from './order.entity';
import { Product } from './product.entity';

@Injectable()
export class OrderService {
  constructor(private readonly dataSource: DataSource) {}

  async placeOrder(userId: string, productId: string, qty: number): Promise<Order> {
    const qr: QueryRunner = this.dataSource.createQueryRunner();
    await qr.connect();
    await qr.startTransaction('SERIALIZABLE'); // уровень изоляции явно

    try {
      const product = await qr.manager.findOneOrFail(Product, {
        where: { id: productId },
        lock: { mode: 'pessimistic_write' }, // SELECT ... FOR UPDATE
      });

      if (product.stock < qty) {
        throw new Error('Недостаточно товара на складе');
      }

      product.stock -= qty;
      await qr.manager.save(product);

      const order = qr.manager.create(Order, { userId, productId, qty });
      await qr.manager.save(order);

      await qr.commitTransaction();
      return order;
    } catch (err) {
      await qr.rollbackTransaction();
      throw new InternalServerErrorException(err.message);
    } finally {
      await qr.release(); // всегда освобождаем соединение
    }
  }
}

Подход 3: SAVEPOINT для вложенных транзакций

async complexFlow(qr: QueryRunner): Promise<void> {
  await qr.startTransaction();
  try {
    await qr.manager.save(MainEntity, { /* ... */ });

    // Создаём savepoint перед рискованной операцией
    await qr.query('SAVEPOINT sp1');
    try {
      await qr.manager.save(RiskyEntity, { /* ... */ });
    } catch {
      await qr.query('ROLLBACK TO SAVEPOINT sp1'); // частичный откат
    }

    await qr.commitTransaction();
  } catch (err) {
    await qr.rollbackTransaction();
    throw err;
  } finally {
    await qr.release();
  }
}

Инкапсуляция через декоратор (DRY-подход)

// transaction.decorator.ts
import { DataSource } from 'typeorm';

export function Transactional() {
  return function (
    _target: object,
    _key: string,
    descriptor: PropertyDescriptor,
  ) {
    const original = descriptor.value;
    descriptor.value = async function (...args: unknown[]) {
      const dataSource: DataSource = this.dataSource; // инжектируется в класс
      return dataSource.transaction((manager) =>
        original.apply({ ...this, manager }, args),
      );
    };
    return descriptor;
  };
}

Уровни изоляции в TypeORM

await qr.startTransaction('READ COMMITTED');  // по умолчанию в PostgreSQL
await qr.startTransaction('REPEATABLE READ');
await qr.startTransaction('SERIALIZABLE');    // максимальная защита, больше блокировок

Подводные камни

  • Утечка соединений при пропуске qr.release() — если не вызвать release() в блоке finally, соединение не вернётся в пул и приложение рано или поздно «зависнет» из-за исчерпания пула.
  • Использование обычного Repository внутри QueryRunner — инжектированный через @InjectRepository() репозиторий не участвует в транзакции QueryRunner. Для операций в транзакции используйте qr.manager.getRepository(Entity).
  • Дедлоки при SERIALIZABLE — при высокой конкурентности уровень SERIALIZABLE порождает сериализационные ошибки (ERROR: 40001). Нужна логика повторных попыток (retry) с экспоненциальной задержкой.
  • Async context propagation — при передаче QueryRunner между сервисами через аргументы нарушается инкапсуляция. Рассмотрите использование AsyncLocalStorage или библиотеки typeorm-transactional для прозрачной передачи контекста.
  • N+1 внутри транзакции — ленивые отношения TypeORM открывают новые соединения, игнорируя активный QueryRunner. Всегда загружайте связи явно через relations или qr.manager.find().
  • Тестирование транзакций — мокировать DataSource сложно. Удобнее поднимать реальную тестовую БД (через testcontainers) и откатывать каждый тест через qr.rollbackTransaction().
  • Уровень изоляции по умолчанию — TypeORM не выставляет уровень изоляции явно; используется дефолт СУБД. В PostgreSQL это READ COMMITTED, а в MySQL — REPEATABLE READ. При смешанном окружении это неожиданно.

Частые вопросы на собеседовании

  • Что такое NestJS и каковы его основные принципы проектирования?
  • Какова роль модулей в NestJS? Как работает система модулей?
  • В каких backend-проектах NestJS является сильным выбором, а где лучше выбрать более простой или другой стек?
  • Что такое dependency injection (DI) в NestJS и как он реализован?

Типичные ошибки

  • Дает общий ответ про Node.js и не называет конкретные API NestJS.
  • Не объясняет, где в lifecycle находится транзакции TypeORM в NestJS.
  • Не разделяет validation, authorization, business logic и persistence.
  • Игнорирует ошибки, лимиты входных данных, observability и тестирование.

Что проверяет интервьюер

  • Может объяснить транзакции TypeORM в NestJS на примере кода.
  • Называет ключевые API: DataSource.transaction().
  • Использует точные API NestJS, а не вымышленные hooks/decorators/methods.
  • Видит production-риски: безопасность, отказоустойчивость, логирование и тесты.

Как вы знаете этот вопрос?

Проверить себя по теме NestJS →

Источники

  • https://docs.nestjs.com/techniques/database#transactions
  • https://docs.nestjs.com/techniques/database
←Предыдущий вопросКак писать unit-тесты и e2e-тесты в NestJS с помощью Jest?Следующий вопрос→Как реализовать управление доступом на основе ролей (RBAC) в NestJS?

Связанные темы

NestJSdatabase-designreliability

Вакансии с этим навыком

  • Fullstack JavaScript разработчик
  • Senior Backend Developer
  • Automation QA Engineer (Payments)