Sequelize-Typescript One To Many Association with Repository returns 1 child entity when there's more than one

55 Views Asked by At

I have been working around using Sequelize-Typescript and I have stumbled upon the one-to-many association. Here I have "Album" and "Photos" as my entities. One "Album" has many "Photos". So here's my entity codes for your reference:

Album.ts

`

@Table({
  timestamps: true,
  deletedAt: "albumDeletedAt",
  paranoid: true,
  createdAt: true,
  updatedAt: true
})
export class Album extends Model {
  @PrimaryKey
  @Column({
      type: DataType.INTEGER,
      primaryKey: true,
      autoIncrement: true
  })
  declare id: number;

  @Column({
      type: DataType.STRING,
      allowNull: false
  })
  declare name: string;

  @Column({
     type: DataType.STRING,
     allowNull: true
  })
  declare description?: string;

  @Column({
      type: DataType.STRING,
      allowNull: false
  })
  declare thumbnailURL: string;

  @HasMany(() => Photo)
  declare photos: Photo[];

  @CreatedAt
  declare createdAt: Date;

  @UpdatedAt
  declare updatedAt: Date;
 }`

Photo.ts

import { BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, PrimaryKey, Table, 
UpdatedAt } from "sequelize-typescript";
import { Album } from "./album";

@Table({
  timestamps: true,
  deletedAt: "photoDeletedAt",
  paranoid: true,
  createdAt: true,
  updatedAt: true
})
export class Photo extends Model {
  @PrimaryKey
  @Column({
      type: DataType.INTEGER,
      primaryKey: true,
      autoIncrement: true
  })
  declare id: number;

  @Column({
     type: DataType.STRING,
     allowNull: false
  })
  declare name: string;

  @Column({
     type: DataType.STRING,
     allowNull: false
  })
  declare photoURL: string;

  @ForeignKey(() => Album)
  @Column({
     type: DataType.INTEGER,
     allowNull: true,
  })
  declare albumId: number;

  @BelongsTo(() => Album)
  declare album: Album;

  @CreatedAt
  declare createdAt: Date;

  @UpdatedAt
  declare updatedAt: Date;
}

AlbumRepository.ts

export class AlbumRepository {
private albumRepository: Repository<Album>;
private photoRepository: Repository<Photo>;

/**
 * This constructor injects the DatabaseMiddleware class created and annotated as "@Service()"
 * @param databaseMiddleware Database Middleware created
 */
constructor(public databaseMiddleware: DatabaseMiddleware) {
    this.albumRepository = databaseMiddleware.getDatabaseInstance().getRepository(Album);
    this.photoRepository = databaseMiddleware.getDatabaseInstance().getRepository(Photo);
}

/**
 * Creates an "Album" data in the database.
 * @param albumCreateDTO Album Create DTO data model
 * @returns "Album" data entity model (if successfully created in the database) or null.
 */
public async createAlbum(albumCreateDTO: AlbumCreateDto) {
    try {
        return await this.albumRepository.create({ ...albumCreateDTO }, { include: [this.photoRepository] });
    } catch (error) {
        console.error("Error while creating an album data to the database due to", error?.message);
        return null;
    }
}

/**
 * Find the album via it's ID
 * @param id The ID of the album
 * @returns Album (if exists) or null
 */
public async getAlbumById(id: number) : Promise<Album | null> {
    try {
        return await this.albumRepository.findByPk(id, { include: [this.photoRepository] }) ?? null;
    } catch (error) {
        console.error(`Error finding the album via ID for the ID of ${id} due to`, error);
        return null;
    }
}

}

And finally, my Sequelize-Typescript instance which is below:

export default class DatabaseMiddleware {
private databaseInstance: Sequelize;

constructor() {
    this.databaseInstance = new Sequelize({
        dialect: "mysql",
        host: process.env.DATABASE_HOST,
        database: process.env.DATABASE_NAME,
        username: process.env.DATABASE_USERNAME,
        password: process.env.DATABASE_PASSWORD,
        port: 3306,
        // storage: process.env.DATABASE_STORAGE_PATH,
        logging(sql, timing) {
            console.log("The SQL statement from Sequelize executed is", sql, timing);
        },
        query: { raw: true }
        models: [User, Album, Video, Photo],
        repositoryMode: true
    });
}

public async connectToDatabase() {
    try {
        await this.databaseInstance.authenticate();
        if (process.env.ENVIRONMENT_PROFILE === "development") {
            await this.databaseInstance.sync({ alter: true, force: true });
        }
        console.log("Connection to database has been established successfully");
    } catch (error) {
        console.error("Unable to connect to database due to", error);
    }
}

public getDatabaseInstance() {
    return this.databaseInstance;
}

}

I'm using class based style to create the Express.JS REST API and I'm adding dependency injection in every class that I use except the entities. I'm able to create an "Album" with the "Photos" and I'm able to see it on the database. But it's when I call the getAlbumById function in the repository that I only get 1 "Photo". If I have inserted 3 "Photo"s then, during the invocation of the function, it returns only 1 "Photo" when there's 3 of them in database.

UPDATE OF THE QUESTION'S DESCRIPTION

Here's how the query looks like after running with include: [this.photoRepository] in the AlbumRepository.ts.

First screenshot 1st Screenshot of the updated query

Second screenshot 2nd Screenshot of the updated query

If you have any solution, please do share them here. Thanks!

Error when removing raw: true flag from the AlbumRepository.ts which is the function getAlbumByID:

Error finding the album via ID for the ID of 1 due to TypeError: result.get is not a function at E:\Coding Projects\Projects\Back-Ends\Profilic\node_modules\sequelize\src\model.js:1885:39 at Array.reduce () at E:\Coding Projects\Projects\Back-Ends\Profilic\node_modules\sequelize\src\model.js:1884:19 at Array.map () at Function._findSeparate (E:\Coding Projects\Projects\Back-Ends\Profilic\node_modules\sequelize\src\model.js:1881:39) at Function.findAll (E:\Coding Projects\Projects\Back-Ends\Profilic\node_modules\sequelize\src\model.js:1840:24) at processTicksAndRejections (node:internal/process/task_queues:95:5) at async Function.findOne (E:\Coding Projects\Projects\Back-Ends\Profilic\node_modules\sequelize\src\model.js:1998:12) at async Function.findByPk (E:\Coding Projects\Projects\Back-Ends\Profilic\node_modules\sequelize\src\model.js:1955:12) at async AlbumRepository.getAlbumById (E:\Coding Projects\Projects\Back-Ends\Profilic\repositories\album.repository.ts:52:20)

0

There are 0 best solutions below