sequelize-typescript

repository·master·Indexed 24 days ago

https://github.com/sequelize/sequelize-typescript

A library providing decorators and enhanced TypeScript support for Sequelize v6, enabling type-safe model definitions. It includes decorators for tables (@Table), columns (@Column), timestamps (@CreatedAt, @UpdatedAt, @DeletedAt), and associations (@HasMany, @BelongsTo, @BelongsToMany, @HasOne). Features include automatic type inference for columns, repository mode for separating static operations from model definitions, and dedicated validation decorators.

Tokens
10.9K
Snippets
20
Records
56
Agent score
81%

What's inside sequelize-typescript

  1. Define Many-to-Many associations

    master

    Use @BelongsToMany on both participating models. You must provide a through-table model as the second argument to the decorator. To access the through-table instance type-safely, you must manually define the type using an intersection type.

    @Table
    class Book extends Model {
      @BelongsToMany(() => Author, () => BookAuthor)
      authors: Author[];
    }
    
    @Table
    class Author extends Model {
      @BelongsToMany(() => Book, () => BookAuthor)
      books: Book[];
    
      // Type safe access to the through-table instance
      @BelongsToMany(() => Book, () => BookAuthor)
      booksWithThrough: Array<Book & {BookAuthor: BookAuthor}>;
    }
    
    @Table
    class BookAuthor extends Model {
      @ForeignKey(() => Book)
      @Column
      bookId: number;
    
      @ForeignKey(() => Author)
      @Column
      authorId: number;
    }
  2. Define a Model with @Table and @Column

    master

    Models must extend the Model class and be decorated with @Table. Database columns must be decorated with @Column. You can use a 'less strict' approach or a 'more strict' approach using TypeScript interfaces for attributes and creation attributes to ensure type safety.

    import { Table, Column, Model, HasMany } from 'sequelize-typescript';
    
    @Table
    class Person extends Model {
      @Column
      name: string;
    
      @Column
      birthday: Date;
    
      @HasMany(() => Hobby)
      hobbies: Hobby[];
    }
  3. Define One-to-Many associations

    master

    Use @HasMany on the parent model and @BelongsTo combined with @ForeignKey on the child model to define a one-to-many relationship. When retrieving the parent, use the include option in Sequelize find methods to resolve the associated children.

    @Table
    class Player extends Model {
      @Column
      name: string;
    
      @Column
      num: number;
    
      @ForeignKey(() => Team)
      @Column
      teamId: number;
    
      @BelongsTo(() => Team)
      team: Team;
    }
    
    @Table
    class Team extends Model {
      @Column
      name: string;
    
      @HasMany(() => Player)
      players: Player[];
    }
    
    // Usage
    Team.findOne({ include: [Player] }).then((team) => {
      team.players.forEach((player) => console.log(`Player ${player.name}`));
    });
  4. Best practices and limitations

    master

    Circular Dependencies

    When defining foreign keys, use a function wrapper () => Model instead of the class directly. This prevents ReferenceError when a model is undefined due to circular dependencies during initialization.

    Minification

    If you minify your code, the class names will change, which breaks Sequelize's default naming. You must explicitly set tableName and modelName in the @Table decorator options.

    Model Isolation

    • Without Repository Mode: A single model class can only be associated with one Sequelize instance.
    • File Structure: It is recommended to keep one model class per file to avoid ReferenceError issues caused by TypeScript's emitDecoratorMetadata.
  5. Handle multiple relations between the same models

    master

    When a model has multiple relations to the same target model (e.g., a Book having both an author and a proofreader who are both Person models), sequelize-typescript cannot automatically resolve the foreign keys. You must explicitly provide the foreign key name as a second argument to the @BelongsTo and @HasMany decorators.

    @Table
    class Book extends Model {
      @ForeignKey(() => Person)
      @Column
      authorId: number;
    
      @BelongsTo(() => Person, 'authorId')
      author: Person;
    
      @ForeignKey(() => Person)
      @Column
      proofreaderId: number;
    
      @BelongsTo(() => Person, 'proofreaderId')
      proofreader: Person;
    }
    
    @Table
    class Person extends Model {
      @HasMany(() => Book, 'authorId')
      writtenBooks: Book[];
    
      @HasMany(() => Book, 'proofreaderId')
      proofedBooks: Book[];
    }
  6. Install sequelize-typescript

    master

    To use sequelize-typescript, you must have sequelize@6 installed. You also need reflect-metadata and additional typings for Node and Validator.

    Ensure your tsconfig.json is configured with experimentalDecorators and emitDecoratorMetadata enabled, and a target of es6 or higher.

    npm install --save-dev @types/node @types/validator
    npm install sequelize reflect-metadata sequelize-typescript
    {
      "target": "es6",
      "experimentalDecorators": true,
      "emitDecoratorMetadata": true
    }
  7. Configure Sequelize with sequelize-typescript

    master

    To use your defined models, you must instantiate Sequelize from the sequelize-typescript package. You can provide models directly in the constructor via the models option, or add them later using sequelize.addModels(). The models option accepts an array of model classes or paths to model files (including globs).

    import { Sequelize } from 'sequelize-typescript';
    
    // Option 1: Configure via constructor
    const sequelize = new Sequelize({
      database: 'some_db',
      dialect: 'sqlite',
      username: 'root',
      password: '',
      storage: ':memory:',
      models: [__dirname + '/models'], // Can be class references or paths
    });
    
    // Option 2: Add models after instantiation
    sequelize.addModels([Person]);
    sequelize.addModels(['path/to/models']);
    sequelize.addModels([__dirname + '/**/*.model.ts']); // Using globs
  8. Enable and use Repository Mode

    master

    Repository mode separates static operations (like find, create) from model definitions, allowing models to be used with multiple Sequelize instances.

    Enable: Set repositoryMode: true in the Sequelize constructor.

    Usage: Use sequelize.getRepository(Model) to obtain a repository instance for performing operations.

    Associations: When using repository mode, you must use repositories within the include options for associations.

  9. Use the browser-compatible entrypoint for sequelize-typescript

    master
    The src/browser/index.ts file provides a browser-compatible entrypoint for sequelize-typescript. In environments where the full Sequelize library cannot be loaded, this entrypoint exports noop (no-operation) versions of all decorators and classes. This allows code using these decorators (like @Table, @Column, @HasMany, etc.) to be parsed and executed without throwing errors, even if the actual database logic is not present.
  10. Define Sequelize hooks using decorators

    master

    In sequelize-typescript, you can define model hooks using decorators on static methods. The library uses reflect-metadata to manage these hooks.

    When using hook decorators, ensure the decorated method is a static method. If you attempt to decorate a non-static method, the library will throw an error: Hook method '[methodName]' is not a static method. Only static methods can be used for hooks.

    Additionally, the method name cannot be identical to the hook type (e.g., you cannot name a method beforeCreate if you are using the beforeCreate hook), as these names are reserved by Sequelize.