Entity Service Usage
| 
 Full  
EntityService vs BaseEntityService
 The  
 Assuming your entities have an  The first parts of this page will be applicable to both   | 
Module Assumptions
The EntityService assumes you are in a Nest environment, using a TypeORM Repository,
inside a module that looks something like:
src/user/user.module.tsimport { Module } from "@nestjs/common";
import { TypeORMModule } from "@nestjs/typeorm";
import { User } from "./user.entity";
import { UserService } from "./user.service";
@Module({
  imports: [
    TypeORMModule.forFeature([ User ]),
  ],
  providers: [
    UserService,
  ],
})
export class UserModule {}
Entity Assumptions
EntityService requires entities to have an id field.
Your services entity might look something like:
src/user/user.entity.tsimport { Column, Entity, PrimaryColumn } from "typeorm";
@Entity()
export class User {
  @PrimaryColumn()
  public id: string;
  @Column({ nullable: true })
  public name: string;
  @Column()
  public username: string;
  @Column()
  public password: string;
  @Column({
    default: false,
  })
  public isAdmin: boolean;
}
Extend EntityService
Your UserService will need to extend EntityService:
src/user/user.service.tsimport { EntityService } from "@formulaic/entity-service"; (1)
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { User } from "./user.entity";
@Injectable()
export class UserService extends EntityService<User> {
  public constructor(
    @InjectRepository(User)
    private readonly users: Repository<User>,
  ) {
    super("User", users); (2)
  }
}
| 1 | EntityService can also be imported from the @formulaic/api bundle | 
| 2 | EntityService takes two arguments - the name of the class, which makes error messages more detailed and traceable, and the TypeORM Repository. | 
Utility Methods
EntityService provides you with a number of utilities.
To demonstrate a few, we can update the UserService to create a default
administrator account if one does not already exist.
We’ll first setup the basic structure to lay out the flow:
src/user/user.service.tsimport { EntityService } from "@formulaic/entity-service";
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { User } from "./user.entity";
@Injectable()
export class UserService extends EntityService<User> {
  private readonly admin: Promise<Data<User> | DatabaseException<User>>;
  public constructor(
    @InjectRepository(User)
    private readonly users: Repository<User>,
  ) {
    super("User", users);
    this.admin = this.ensureInitialAdmin();
  }
  /**
   * Use to ensure there's an initial administrator account
   * before performing operations that may require an account to exist.
   *
   * If the database encountered an error while locating or creating an admin account,
   * this method will resolve `false`.
   */
  public async hasAdminAccount(): Promise<boolean> {
    const admin = await this.admin;
    return admin.hasData;
  }
}
Finding Single Entities
EntityService wraps TypeORM’s findOne method,
returning responses structured using FP for easy transformation.
src/user/user.service.tsclass UserService /* ... */ {
  public async ensureInitialAdmin() {
    const existing = await this.findOne({
      where: {
        isAdmin: true,
      },
    });
  }
}
This will produce one of the following responses:
- 
Data<User>if a user is found - 
EntityNotFound<"User", FindOneOptions<User>, User>if the query was not able to find an admin - 
DatabaseException<User, "findOne">if TypeORM threw an unexpected error, such as if the table hasn’t been created yet. 
Creating new entities
The EntityService comes with a basic wrapper around TypeORM’s save method.
src/user/user.service.tsclass UserService /* ... */ {
  public async createUser(
    username: string,
    password: string,
    name?: string,
    isAdmin: boolean = false,
  ) {
    const user = new User();
    user.username = username;
    user.password = password;
    user.name = name;
    user.isAdmin = isAdmin;
    return this.save(user);
  }
}
This will return:
- 
Data<User>if the user was created - 
DatabaseException<User, "save">if the save unexpectedly fails 
Create admin if missing
Going back to ensureInitialAdmin,
we can use FP's substituteAsync method to create a user if one does not exist.
src/user/user.service.tsclass UserService /* ... */ {
  public async ensureInitialAdmin(
    name: string = "Admin",
    username: string = "admin",
    password: string = "admin",
  ) {
    const existing = await this.findOne({
      where: {
        isAdmin: true,
      },
    });
    const admin = await existing.substituteAsync(() => this.createUser(username, password, name, true));
    return admin;
  }
}
FP's substituteAsync will only be used if existing is EntityNotFound - it’ll leave successful data (Data<User>) and errors (DatabaseException) alone.
admin will now be one of:
- 
Data<User>(either fromfindOneorcreateUser) - 
DatabaseException<User, "findOne"> - 
DatabaseException<User, "save"> 
Finding By ID
So far all of the operations used did not depend on the structure of the entity, and would work under either EntityService or BaseEntityService.
However, one of the most frequently used methods does require an id field - findById.
The method wraps findOne under the hood, so you’ve already seen the return values.