NestJS Enterprise Backend APIs · Leçon

Transactions et migrations

Mettez en œuvre des transactions de base de données pour des opérations atomiques et gérez efficacement les modifications de schéma avec les migrations TypeORM.

Leçon 2 sur 312 étapes

Transactions et migrations est une leçon NestJS Enterprise Backend APIs gratuite sur CoddyKit. Ceci est la leçon 2 sur 3. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage NestJS Enterprise Backend APIs, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours NestJS Enterprise Backend APIs comprend 3 leçons au total.

Certaines parties de cette leçon n'ont pas encore été traduites et s'affichent en anglais.

Atomic Operations: Transactions

Imagine transferring money between two bank accounts. If the deduction from one account succeeds but the addition to the other fails, your data is inconsistent!

Database transactions solve this by grouping multiple database operations into a single, atomic unit. Either all operations succeed (commit), or none of them do (rollback).

Understanding ACID Properties

Transactions ensure data reliability by adhering to ACID properties:

  • Atomicity: All or nothing.
  • Consistency: Database state remains valid.
  • Isolation: Concurrent transactions don't interfere.
  • Durability: Committed changes are permanent.

These properties are vital for maintaining data integrity in complex applications.

TypeORM Manual Transactions

TypeORM allows you to manage transactions manually using the DataSource or EntityManager. This gives you fine-grained control over when to commit or rollback.

You typically use dataSource.transaction(async manager => { ... }), where manager is a transactional entity manager.

Manual Transaction Example

Here's a simplified example of transferring funds within a transaction. If any step fails, the entire operation is rolled back.

import { DataSource, Entity, PrimaryGeneratedColumn, Column, Repository } from 'typeorm';

@Entity()
export class Account {
  @PrimaryGeneratedColumn() id: number;
  @Column({ type: 'decimal', precision: 10, scale: 2 }) balance: number;
}

async function runTransaction() {
  const AppDataSource = new DataSource({
    type: 'sqlite',
    database: ':memory:',
    entities: [Account],
    synchronize: true,
  });

  await AppDataSource.initialize();
  const accountRepo = AppDataSource.getRepository(Account);

  await AppDataSource.transaction(async manager => {
    const sender = await manager.save(accountRepo.create({ balance: 100 }));
    const receiver = await manager.save(accountRepo.create({ balance: 50 }));

    console.log(`Initial: Sender ${sender.balance}, Receiver ${receiver.balance}`);

    sender.balance -= 20;
    receiver.balance += 20;

    await manager.save(sender);
    // Simulate an error here to trigger rollback:
    // throw new Error('Failed to update receiver!');
    await manager.save(receiver);

    console.log(`Final: Sender ${sender.balance}, Receiver ${receiver.balance}`);
  });

  await AppDataSource.destroy();
}

// To run this in a Node.js context:
// runTransaction().catch(console.error);
// For CoddyKit, we just show the code.

Decorators for Transactions

NestJS and TypeORM often work together. For convenience, TypeORM provides the @Transaction() and @TransactionManager() decorators.

You can apply @Transaction() to a service method, and TypeORM will automatically wrap the method's execution in a transaction.

Decorator Transaction Example

Using the @Transaction() decorator makes your service code cleaner, as you don't need to manually handle transaction commits or rollbacks.

import { Injectable } from '@nestjs/common';
import { Repository, EntityManager } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Transaction } from 'typeorm'; // Import Transaction decorator

// Assume Account Entity is defined elsewhere
class Account { 
  id: number; 
  balance: number; 
}

@Injectable()
export class BankService {
  constructor(
    @InjectRepository(Account) private accountRepository: Repository<Account>,
  ) {}

  @Transaction()
  async transferFunds(
    senderId: number,
    receiverId: number,
    amount: number,
    @TransactionManager() manager?: EntityManager, // Manager is injected
  ): Promise<boolean> {
    const accountManager = manager || this.accountRepository.manager; // Use injected manager

    const sender = await accountManager.findOneBy(Account, { id: senderId });
    const receiver = await accountManager.findOneBy(Account, { id: receiverId });

    if (!sender || !receiver || sender.balance < amount) {
      throw new Error('Transfer failed: Invalid accounts or insufficient funds');
    }

    sender.balance -= amount;
    receiver.balance += amount;

    await accountManager.save(sender);
    await accountManager.save(receiver);

    console.log(`Transferred ${amount}. Sender: ${sender.balance}, Receiver: ${receiver.balance}`);
    return true;
  }
}

// Note: This code is illustrative within a NestJS service context. 
// It's not runnable as a standalone script without a full NestJS setup.

Managing Schema Changes: Migrations

As your application evolves, your database schema will change (e.g., adding new columns, tables). Directly modifying the database can be risky and hard to track.

Database migrations provide a structured, version-controlled way to apply changes to your database schema, ensuring consistency across environments.

Generating TypeORM Migrations

TypeORM has a powerful CLI to generate migration files. It compares your entities with the current database schema and creates a script to bridge the differences.

First, ensure your ormconfig.json or DataSource is set up correctly. Then, run the command:

  • npx typeorm migration:generate src/migration/MyNewMigration

This creates a timestamped .ts file in the specified folder.

Migration File Structure

Each migration file contains two key methods:

  • up(queryRunner: QueryRunner): Promise: Defines changes to apply to the database (e.g., create table, add column).
  • down(queryRunner: QueryRunner): Promise: Defines how to revert the changes made by up (e.g., drop table, remove column).

This allows you to easily roll forward and backward through schema versions.

import { MigrationInterface, QueryRunner, Table } from 'typeorm';

export class MyNewMigration1678886400000 implements MigrationInterface {
  public async up(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.createTable(
      new Table({
        name: 'products',
        columns: [
          { name: 'id', type: 'int', isPrimary: true, isGenerated: true, generationStrategy: 'increment' },
          { name: 'name', type: 'varchar' },
          { name: 'price', type: 'decimal', precision: 10, scale: 2, default: 0 },
        ],
      }),
      true,
    );
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.dropTable('products');
  }
}

Applying and Reverting Migrations

Once you've written or generated your migration files, you need to apply them to your database:

  • Apply all pending migrations: npx typeorm migration:run
  • Revert the last applied migration: npx typeorm migration:revert

Always run migrations in your development and production environments to keep schemas synchronized.

Check Your Knowledge

Which of the following statements about database transactions and migrations in TypeORM is FALSE?

Recap: Transactions & Migrations

You've learned about two critical concepts for robust database management:

  • Transactions: Ensure data integrity through atomic operations following ACID principles, using TypeORM's manual methods or the @Transaction() decorator.
  • Migrations: Provide a structured way to evolve your database schema, using TypeORM CLI to generate and apply changes via up() and down() methods.

Mastering these will help you build more reliable and maintainable backend APIs.

Gratuit pour commencer

Apprends TypeScript avec un tuteur IA — gratuit

Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.

Cours
20
Leçons
76

Questions Fréquemment Posées

La leçon « Transactions et migrations » est-elle gratuite ?

Oui — le texte complet de « Transactions et migrations » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours NestJS Enterprise Backend APIs, passe à CoddyKit PRO. Le cours NestJS Enterprise Backend APIs comprend 3 leçons au total.

Qu'est-ce que j'apprendrai dans « Transactions et migrations » ?

Mettez en œuvre des transactions de base de données pour des opérations atomiques et gérez efficacement les modifications de schéma avec les migrations TypeORM. Tu pratiques NestJS Enterprise Backend APIs avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer NestJS Enterprise Backend APIs ?

Aucune expérience préalable n'est requise. NestJS Enterprise Backend APIs sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 3.

Combien de temps prend la leçon « Transactions et migrations » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon NestJS Enterprise Backend APIs ?

Oui. Chaque leçon NestJS Enterprise Backend APIs inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Dépôts TypeORM personnalisés
  2. Transactions et migrations
  3. Initialisation et tests de la base de données
← Retour à NestJS Enterprise Backend APIs