When to use Assemblers
In most cases an Assembler will not be required because your Entity and DTO will be the same shape. The only time you need to define an assembler is when the DTO and Entity are different. The most common scenarios are- Additional computed fields and you do not want to include the business logic in your DTO definition.
- Different field names because of poorly named columns in the database or to make a DB change passive to the end user.
- You need to transform the create or update DTO before being passed to your persistence QueryService
Why?
Separation of concerns.Resolvers
Your resolvers only concern is dealing with graphql and translating the request (a DTO) into something the service cares about. The resolver should not care about how it is persisted. The underlying Entity could have additional fields that you do not want to expose in your API, or it may be persisted into multiple stores. By separating the resolver from the persistence layer you can evolve your API separate from your database model.Services
The services concern are operating on a DTO, preventing the leaking of persistence details to the API. Innestjs-query services can be composed. In the case of assemblers information is translated using the assembler and delegated to an underlying service.
This alleviates any awkwardness around passing in a DTO and receiving a different object type back. Instead, your service can use an assembler to alleviate these concerns.
Assemblers
The assembler provides a single, testable, place to provide a translation between the DTO and entity, and vice versa.Why not use the assembler in the resolver?
The resolvers concern is translating graphql requests into the specified DTO. The services concern is accepting and returning a DTO based contract. Then using an assembler to translate between the DTO and underlying entities. If you follow this pattern you could use the same service with other transports (rest, microservices, etc) as long as the request can be translated into a DTO.ClassTransformerAssembler
In most cases the class-transformer package will properly map back and forth. Because of this there is aClassTransformerAssembler that leverages the plainToClass method.
NOTE The ClassTransformerAssembler is the default implementation if an Assembler is not manually defined.
If you find yourself in a scenario where you need to compute values and you dont want to add the business logic to your DTO you can extend the ClassTransformerAssembler.
Lets take a simple example, where we have TodoItemDTO and we want to compute the age.
todo-item.assembler.ts
AbstractAssembler
To create your ownAssembler extend the AbstractAssembler.
Lets assume we have the following UserDTO.
user.dto.ts
- TypeORM
- Sequelize
- Mongoose
user.entity.ts
UserDTO into the UserEntity and back you can extend an Assembler that the QueryService will use.
user.assembler.ts
@Assembler decorator. It will register the assembler with nestjs-query so QueryServices can look it up later.
Converting the Query
Next theconvertQuery method.
transformQuery function from @ptc-org/nestjs-query-core. This method will remap all fields specified in the field map to correct field name.
In this example
Converting the DTO
The next piece is theconvertToDTO, which will convert the entity into a the correct DTO.
Converting the Entity
The next piece is theconvertToEntity, which will convert the DTO into a the correct entity.
Converting Aggregate Query
TheconvertAggregateQuery is used to convert an AggregateQuery. This examples uses the transformAggregateQuery helper to map aggregate query fields.
Converting Aggregate Response
TheconvertAggregateResponse is used to convert an AggregateResponse. This examples uses the transformAggregateResponse helper to map aggregate response fields.
Converting Create DTO
TheconvertToCreateEntity is used to convert an incoming create DTO to the appropriate create entity, in this case
partial.
Converting Update DTO
TheconvertToUpdateEntity is used to convert an incoming update DTO to the appropriate update entity, in this case a
partial.
AssemblerQueryService
AnAssemblerQueryService is a special type of QueryService that uses the Assembler to translate between the DTO and Entity.
The easiest way to create an AssemblerQueryService is to use the @InjectAssemblerQueryService decorator.
Before using the decorator you need to register your Assembler with nestjs-query
Module
user.module.ts
Auto Generated Resolver
If you want your assembler to be used by the auto-generated resolver you can specify theAssemblerClass option.
user.module.ts
Manual Resolver
If you are manually defining you resolver or want to use theAssemblerQueryService in another service use the @InjectAssemblerQueryService decorator.
user.resolver.ts
QueryService<UserDTO>.