Skip to content

[Startup MVP recipes #5.2] A simple resource generated by nest-cli then configured (part 2)

Warning: we are using typeorm 0.3 and postgres for this tutorial

This tutorial continues where we left in

And let’s fill in the rest of generated boilerplate by nest-cli.

The Read of CRUD

Find queries are pretty straightforward:

// users.resolver.ts

@Query(() => [User], { name: 'users' })
async findAll(): Promise<User[]> {
  return this.usersService.findAll();
}

@Query(() => User, { name: 'user' })
async findOne(
  @Args('uuid', { type: () => String }) uuid: string,
): Promise<User> {
  return this.usersService.findOne(uuid);
}
// users.service.ts

async findAll() {
  return this.userRepo.find();
}

async findOne(uuid: string) {
  return this.userRepo.findOneBy({ uuid });
}

The Update of CRUD

Define update DTO first, note that it can leverage PartialType of the create DTO. For detailed tutorial of PartialType and OmitType check official doc at https://docs.nestjs.com/openapi/mapped-types

// update-user.input.ts

@InputType()
export default class UpdateUserInput extends PartialType(CreateUserInput) {
  @Field(() => String)
  uuid: string;
}

For update mutation, for a minimal version we can just return the count of affected rows:

// users.resolver.ts

@Mutation(() => Int)
async updateUser(
  @Args('updateUserInput') updateUserInput: UpdateUserInput,
): Promise<number> {
  return this.usersService.update(updateUserInput.uuid, updateUserInput);
}
// users.service.ts

async update(uuid: string, updateUserInput: UpdateUserInput) {
    return (await this.userRepo.update({ uuid }, updateUserInput)).affected;
}

Alternatively, we may want to return the updated User from the update mutation, the code may not look elegant but it works:

// users.service.ts

import * as _ from 'lodash';

async update(uuid: string, updateUserInput: UpdateUserInput) {
  const result = await this.userRepo
    .createQueryBuilder()
    .update(User)
    .set(updateUserInput)
    .where({ uuid })
    .returning('*')
    .execute();
  return result.raw[0]
    ? _.mapKeys(result.raw[0], (_v: any, k: any) => _.camelCase(k))
    : null;
 }

Since we are using snake case naming strategy for postgres to keep it consistent with postgres standard we need to map the object names back to camel case.

Here returning('*') enforces the user entity to be returned.

The Delete of CRUD

Similarly if we just want to return affected Rows count:

// users.resolver.ts

@Mutation(() => Int)
async removeUser(@Args('uuid', { type: () => String }) uuid: string) {
  return this.usersService.remove(uuid);
}
// users.service.ts

async remove(uuid: string) {
  return (await this.userRepo.delete(uuid)).affected;
}

To return the deleted users entities + affected rows count together we will have another in-depth tutorial on that in the future.

Leave a Reply

Your email address will not be published. Required fields are marked *