Skip to content

Controller

Phat Nguyen edited this page Nov 20, 2024 · 5 revisions

Instructions

  1. Define CRUD Controller

    1.1 with Repository

    1.2 with Service

  2. Define Relational CRUD Controller

You can choose to define controller with Repository or Service depends on your demand!


Scenario:

  • Note Application included User, UserProfile and Note entities
  • User HAS_ONE UserProfile
  • User HAS_MANY Note
  • UserProfile BELONGS_TO User
  • Note BELONGS_TO User

Implementation:

Entities:

@model({})
export class User extends BaseTzEntity {
    @property({ type: 'number' })
    id: number

    @hasOne(() => UserProfile, { keyTo: 'userId' })
    profile?: UserProfile;
  
    @hasMany(() => Note, { keyTo: 'userId' })
    notes: Array<Note>;

    // Many more properties
}

export interface IUserRelations {
    profile?: UserProfile,
    notes?: Array<Note>
}
@model({})
class UserProfile extends BaseTzEntity {
    @property({ type: 'number' })
    id: number

    @belongsTo(() => User, { keyFrom: 'userId' }, { name: 'user_id' })
    userId: number;

    // Many more properties
}

export interface IUserProfileRelations {
    user?: User 
}
@model({})
class Note extends BaseTzEntity {
    @property({ type: 'number' })
    id: number
    
    @belongsTo(() => User, { keyFrom: 'userId' }, { name: 'user_id' })
    userId: number;

    // Many more properties
}

export interface INoteRelations {
    user?: User 
}

Repositories:

export class UserRepository extends TzCrudRepository<User, IUserRelations> {
  public readonly profile: HasOneRepositoryFactory<UserProfile, IdType>;
  public readonly notes: HasManyRepositoryFactory<Note, IdType>;

  constructor(
    @inject(`YOUR_DATABASE_KEY_HERE`)
    dataSource: DataSource,
    @repository.getter('UserProfileRepository')
    protected userProfileRepositoryGetter: Getter<UserProfileRepository>,
    @repository.getter('NoteRepository')
    protected noteRepositoryGetter: Getter<NoteRepository>,
  ) {
    super(User, dataSource);

    this.profile = this.createHasOneRepositoryFactoryFor('profile', userProfileRepositoryGetter);
    this.registerInclusionResolver('profile', this.profile.inclusionResolver);

    this.note = this.createHasManyRepositoryFactoryFor('note', noteRepositoryGetter);
    this.registerInclusionResolver('note', this.note.inclusionResolver);
  }
export class UserProfileRepository extends TzCrudRepository<UserProfile, IUserProfileRelations> {
  public readonly user: BelongsToAccessor<User, IdType>;

  constructor(
    @inject(`YOUR_DATABASE_KEY_HERE`)
    dataSource: DataSource,
    @repository.getter('UserRepository')
    protected userRepositoryGetter: Getter<UserRepository>,
  ) {
    super(UserProfile, dataSource);
    this.user = this.createBelongsToAccessorFor('user', userRepositoryGetter);
    this.registerInclusionResolver('user', this.user.inclusionResolver);
  }
}
export class NoteRepository extends TzCrudRepository<Note, INoteRelations> {
  public readonly user: BelongsToAccessor<User, IdType>;

  constructor(
    @inject(`YOUR_DATABASE_KEY_HERE`)
    dataSource: DataSource,
    @repository.getter('UserRepository')
    protected userRepositoryGetter: Getter<UserRepository>,
  ) {
    super(Note, dataSource);

    this.user = this.createBelongsToAccessorFor('user', userRepositoryGetter);
    this.registerInclusionResolver('user', this.user.inclusionResolver);
  }
}

Controllers:

1. Define CRUD Controller
1.1 with repository

Require steps: . Define CRUD Controller

const basePath = '/users';

const Controller = defineCrudController({
    entity: User,
    repository: { name: UserRepository.name }, // NOTICE THIS
    controller: { basePath },
});

@api({ basePath })
export class UserController extends Controller {
    constructor(
        @inject('repositories.UserRepository') 
        private userRepository: UserRepository,
    ) {
        super(userRepository);
    }
}
const basePath = '/user-profiles'

const Controller = defineCrudController({
    entity: UserProfile,
    repository: { name: UserProfileRepository.name },
    controller: { basePath },
});

@api({ basePath })
export class UserController extends Controller {
    constructor(
        @inject('repositories.UserProfileRepository') 
        private userProfileRepository: UserProfileRepository,
    ) {
        super(userProfileRepository);
    }
}
const basePath = '/notes'

const Controller = defineCrudController({
    entity: Note,
    repository: { name: NoteRepository.name },
    controller: { basePath },
});

@api({ basePath })
export class NoteController extends Controller {
    constructor(
        @inject('repositories.NoteRepository') 
        private noteRepository: NoteRepository,
    ) {
        super(noteRepository);
    }
}

Define CRUD Controller With Repository Options: REF

After defining all controllers, your application will have all necessary controller endpoints.

Example: NoteController (These endpoints gonna be similar for another entites)

GET         /notes/count
GET         /notes/find-one
GET         /notes
POST        /notes
PATCH       /notes

PUT         /notes/{id}
PATCH       /notes/{id}
GET         /notes/{id}
DELETE      /notes/{id}

Note: By this way, in case you want to customize logic of 1 endpoint, you have to re-define whole endpoint and implement endpoint business logic!

1.2 with Service

Required steps: . Define CRUD Service for Entity . Define Service CRUD Controller

Define CRUD Service for Entity

export class NoteService extends BaseCrudService<Note> {
    constructor(
        // repositories
        @inject('repositories.NoteRepository')
        private noteRepository: NoteRepository,
    ) {
        super({
            scope: NoteService.name,
            repository: noteRepository,
        });
    }

    // Overridable methods 

    // GET      /<entity_endpoint>              find(filter, options);
    // GET      /<entity_endpoint>/{id}         findById(id, filter, options);
    // GET      /<entity_endpoint>/fine-one     findOne(filter, options);
    // GET      /<entity_endpoint>/count        count(where, options);

    // POST     /<entity_endpoint>              create(data, options);
    // PATCH    /<entity_endpoint>              updateAll(data, where, options);
    // PATCH    /<entity_endpoint>/{id}         updateWithReturn(id, data, options);
    // PUT      /<entity_endpoint>/{id}         replaceById(id, data, options);
    // DELETE   /<entity_endpoint>/{id}         deleteById(id, options);

    // Example
    async updateWithReturn(
      id: number,
      payload: Partial<Note>,
      options: ICrudMethodOptions,
    ): Promise<ProductionOrderProcess> {
        // YOUR BUSINESS LOGIC HERE
    }

}

Define Service CRUD Controller

const basePath = '/notes'

const CRUDController = defineServiceCrudController({
  entity: Note,
  service: { name: NoteService.name }, // NOTICE THIS
  controller: { basePath },
});

@api({ basePath })
export class NoteController extends CRUDController {}

Define CRUD Controller With Service Options: REF

Similar to defineCrudController, all necessary endpoints will available in your application after using defineServiceCrudController.

Note: By this way, in case you want to customize logic of 1 endpoint, you have to override implementation of 1 method in CrudService NO NEED TO DECLARE AGAIN ENDPOINT!

2. Define Relational CRUD Controller

Required steps: . Define Relation CRUD Controller

const basePath = '/users';
const relationPath = '/notes'

const Controller = defineRelationCrudController({
  association: {
    source: User.name,
    target: Note.name,
    relationType: 'hasMany',
    relationName: 'Note',
  },
  schema: { target: getModelSchemaRef(Note, { exclude: ['id', 'createdAt', 'modifiedAt'] }) },
  options: {
    useControlTarget: true,
    endPoint: relationPath,
  },
});

@api({ basePath })
export class UserNoteController extends Controller {
  constructor(
    @inject('repositories.UserRepository')
    private userRepository: UserRepository,
    @inject('repositories.NoteRepository')
    private noteRepository: NoteRepository,
  ) {
    super(userRepository, noteRepository);
  }
}

Define Relation CRUD Controller Options: REF

After defining all controllers, your application will have all necessary controller endpoints for relation.

Example: UserNoteController (These endpoints gonna be similar for another entites)

GET         /users/{id}/notes/count
GET         /users/{id}/notes
POST        /users/{id}/notes
PATCH       /users/{id}/notes
DELETE      /users/{id}/notes

Minimal Technology Vietnam

Clone this wiki locally