I am currently developing application using nestjs with fastify adapter
But something weird on object construction.
Following all the related classes, and methods:
Controller endpoint handler
#Get()
#ApiOperation({
description: "Get all user admin",
})
async findAll(
#Query() filter: GetListAdminReqFilter,
#Query() pagination: PaginatedReqDto
): Promise<RestRespDto<GetListAdminRespDto[]>> {
return new RestRespDto({
data: await this.adminService.findAll(
new GetListAdminReqDto(filter, pagination)
),
});
}
The request dto
export class GetListAdminReqDto extends PaginatedReqDto {
constructor(filter: GetListAdminReqFilter, pagination: PaginatedReqDto) {
super();
this.filter = filter;
this.pagination = pagination.pagination;
this.page = pagination.page;
}
filter?: GetListAdminReqFilter;
}
The pagination req dto
export class PaginatedReqDto {
#ApiPropertyOptional({
default: 10,
description: "Number of items to retrieve",
})
pagination?: number;
#ApiPropertyOptional({
description: "Page number, e.g:1 ",
default: 1,
})
page?: number;
}
The filter
export class GetListAdminReqFilter {
#ApiPropertyOptional()
#IsOptional()
name?: string;
#ApiPropertyOptional()
#IsOptional()
email?: string;
#ApiPropertyOptional()
#IsOptional()
divisi?: string;
#ApiPropertyOptional({ enum: AdminStatusEnum})
#IsOptional()
status?: AdminStatusEnum;
}
The result of GetListAdminReqDto object is following:
{
filter: [Object: null prototype] {
pagination: '10',
page: '1',
name: 'asdfasdf',
email: 'asdfasdf',
divisi: 'asdfasdf'
},
pagination: '10',
page: '1'
}
Why pagination and page is property also included in filter?, i dont understand what happened, any help will be appreciated
My guess is that the #Query() filter: GetListAdminReqFilter is receiving the page and pagination properties because your ValidationPipe is allowing properties outside of the DTO. You could alter the global ValidationPipe.whitelist property:
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // this prevents properties outside the DTO to be ignored
}),
);
Related
I want to get the prospectousId from Dto, but I am getting the below error:,
tenant.controller.ts
#Post('/promote-prospectus')
#HttpCode(HttpStatus.OK)
#ApiOperation({ summary: 'Promoting a prospectus to a tenant.' })
#ApiResponse({ status: HttpStatus.OK, description: 'successful operation', })
async promoteAProspectus(#Res() response: Response, #Body() Dto: promoteProspectousDto) {
let result = await this.tenantsService.promoteAProspectus(Dto);
return response.status(HttpStatus.CREATED).json({ message: 'Prospectous promoted as a tenant by the system.', data: result });
}
tenant.service.ts
public async promoteAProspectus(Dto: promoteProspectousDto): Promise<Tenants> {
/**
* Scopes to be implement while promoting a prospectous to a tenant.
*
* #scope 1 promote the prospectous to a tenant and construct DB.
* #scope 2 Configure all default roles for the newly created tenant.
* #scope 3 Configure administrator user. & assign the administrator role.
*
*/
let prospectus = await this.tenantsRepository.findOne({ Dto.prospectousId });
console.log('hyyyyyyyyyyyyyyyyyyyyyyyyyyy', prospectus);
if (prospectus) {
const { id } = prospectus;
// let tenant: Tenants = await this.promoteAsTenant(prospectus);
// await this.rolesService.onBoardTenantDefaultRoles(tenant.tenantDb);
// let administrator: Users = await this.onBoardAdministratorUser(RequestedBy);
// await this.allocateAdministratorRole(administrator, tenant);
return tenant;
}
throw new ConflictException(`Unable to promote.`);
}
tenant.entity.ts
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
export enum ETenantStatus {
TRIAL = 'TRIAL',
ACTIVE = 'ACTIVE',
BLOCKED = 'BLOCKED',
ARCHIVED = 'ARCHIVED',
}
#Entity({ name: `${Tenants.name}` })
export class Tenants {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column({ nullable: false })
organization: string;
#Column({ unique: false })
db: string;
#Column({ unique: true })
key: string;
#Column({ unique: true })
domain: string;
#Column({ nullable: false })
location: string;
#Column({ nullable: false })
referedBy: string;
#Column()
#CreateDateColumn({ type: 'timestamp without time zone' })
public createdAt: Date;
#Column()
#UpdateDateColumn({ type: 'timestamp without time zone' })
public updatedAt: Date;
}
dto:
import { ApiProperty, ApiPropertyOptional } from "#nestjs/swagger";
import { IsBoolean, IsNotEmpty } from "class-validator";
export class tenantsDto {
#ApiProperty({ required: true, default: '' })
#IsNotEmpty()
organization: string;
#ApiProperty({ required: true, default: '' })
#IsNotEmpty()
domain: string;
#ApiPropertyOptional({ default: '' })
notes: string;
#ApiPropertyOptional({ default: '' })
db: string;
#ApiPropertyOptional({ default: '' })
#IsNotEmpty()
key: string;
#ApiPropertyOptional({ default: '' })
location: string;
#ApiPropertyOptional({ default: '' })
referedBy: string;
#ApiPropertyOptional({ default: false })
skipDbMigration: boolean;
#ApiProperty({ default: true })
#IsBoolean()
acceptedTermsAndConditions: boolean;
}
export class promoteProspectousDto {
#ApiProperty({ required: true, default: '' })
#IsNotEmpty()
prospectousId: string;
#ApiProperty({ required: true, default: '' })
#IsNotEmpty()
key: string;
}
What to do to achieve this? Thanks in advance!
Try using the below code. findOne() is to be replaced by findOneBy() as they say in their changelog.
let prospectus = await this.tenantsRepository.findOneBy({ id: Dto.prospectousId });
See the Changelog of TypeORM for more:
findOne() signature without parameters was dropped. If you need a single row from the db you can use a following syntax:
const [user] = await userRepository.find()
findOne(id) signature was dropped. Use following syntax instead:
const user = await userRepository.findOneBy({
id: id // where id is your column name
})
I'm trying to use 2 separate dto for query parameters in NestJs. However, on printing both dtos are showing same properties. Is there any way to separate them.
// dto code
export class PageDto {
#Type(() => Number)
#IsInt()
page: number;
#Type(() => Number)
#IsInt()
limit: number;
}
export class MyDto {
#ApiPropertyOptional()
#IsString()
#IsOptional()
status: string;
#ApiPropertyOptional()
#IsString()
#IsOptional()
source: string;
}
Api request:
GET url?page=1&limit=10&status='active'&source='firebase'
controller:
#Get()
async get(
#Query() myDto: MyDto,
#Query() pageDto: PageDto,
) {
console.log({pageDto}, {myDto});
}
Console statement prints:
{
pageDto: {
page: 1,
limit: 10,
status: 'active',
source: 'firebase'
}
}
{
myDto: {
page: 1,
limit: 10,
status: 'active',
source: 'firebase'
}
}
Required:
{
pageDto: {
page: 1,
limit: 10
}
}
{
myDto: {
status: 'active',
source: 'firebase'
}
}
Ideally, pageDto and myDto should show their respective properties.
You'll need to set whitelist: true in your ValidationPipe options to tell class-transformer and class-validator to strip out the properties that don't exist on the DTO.
Reference to the docs
I have an entity
#Column()
name: string;
#IsEmail()
email: string;
#Column({ select: false })
autogeneratedCode: string;
I'm getting name and string only in my GET request response which is expected.
But when I'm hit my POST Api with body, it is returning name, email, autogeneratedCode as well.
I need to hide autogeneratedCode in all CRUD responses.
Is there anyway to do that? or Am I missing something here?
You can use #Exclude() from 'class-transformer'
Example
import { Exclude } from 'class-transformer';
#Entity()
export class User {
#Column()
name: string;
#IsEmail()
email: string;
#Column({ select: false })
#Exclude()
autogeneratedCode: string;
constructor(entity: Partial<User>) {
Object.assign(this, entity);
}
}
Then you can use the constructor to create a new object excluding the #Exclude() properties.
export class UserService {
constructor(
#InjectRepository(User)
private userRepository: Repository<User>
) {}
public async createUser(user: User): Promise<User> {
return new User(
await this.userRepository.save(user)
);
}
}
NestJS Doc on Serialization
https://docs.nestjs.com/techniques/serialization#exclude-properties
I have a function that takes an option object as argument.
const newEvent = (option: Option): void => {
// does something with the config
}
Option should have a few optional and non-optional properties.
interface Option {
host: string;
date: Date;
onStart?: () => any;
pizza?: boolean;
...
}
It should also have at least 2 additional properties whose property name is dynamic and its value follow the shape of AttendeeOption.
interface AttendeeOption {
vip: boolean;
allergy?: string;
address?: string;
}
Intuitively, I tried to just put them together:
interface Option {
host: string;
date: Date;
onStart?: () => any;
pizza?: boolean;
[name: string]: AttendeeOption
}
Using string index signature like this doesn't work since all properties will now need to conform to AttendeeOption type.
Second attempt, I tried to make Option and Attendee intersect:
interface Attendee {
[name: string]: AttendeeOption
}
const newEvent = (option: Option & Attendee): void => {
// does something with the config
}
The interface error went away for now,
newEvent({
host: 'Professor Oak',
date: new Date(2021, 1, 3),
'Ash': {vip: true, address: 'Pallet Town'},
'Brock': {vip: true, address: 'Pewter City'},
'Misty': {vip: true, address: 'Cerulean City'},
})
When I test it, I got error on the Ash, Brock, May property: Object literal may only specify known properties, and ''Ash'' does not exist in type 'Option'
Any idea on how to approach this would be really appreciated. Thank you!
I don't know if this is what you want.But you can use array of AttendeeOption.you can add name to the AttendeeOption.
Option Interface
interface Option {
host: string;
date: Date;
onStart?: () => any;
pizza?: boolean;
Attendees: AttendeeOption[]
AttendeeOption Interface
interface AttendeeOption {
name?: string;
vip: boolean;
allergy?: string;
address?: string;
}
Your Object can be like this
{
host: 'Professor Oak',
date: new Date(2021, 1, 3),
Attendees : [
{name: 'name1',vip: true, address: 'Cerulean City'},
{name: 'name2',vip: true, address: 'Cerulean City'},
{name: 'name3',vip: true, address: 'Cerulean City'}
]
}
https://loopback.io/doc/en/lb4/HasMany-relation.html
I followed this steps and then tried to get data with include but I get 500.
500 Error: Invalid "filter.include" entries: {"relation":"ranks"}
What I want is to get games object with its related ranks.
Rank Model
import { Entity, model, property, belongsTo } from '#loopback/repository';
import { Game, GameWithRelations } from './game.model';
#model({ settings: { strict: 'filter' } })
export class Rank extends Entity {
#property({
type: 'string',
id: true,
})
id?: string;
#property({
type: 'string',
})
name?: string;
#property({
type: 'string',
})
shortName?: string;
#property({
type: 'string',
})
avatar?: string;
#belongsTo(() => Game)
gameId: string;
constructor(data?: Partial<Rank>) {
super(data);
}
}
export interface RankRelations {
game?: GameWithRelations;
}
export type RankWithRelations = Rank & RankRelations;
Game Model
import { Entity, model, property, embedsMany, hasMany } from '#loopback/repository';
import { Rank, RankWithRelations } from './rank.model';
import { HasMany } from 'loopback-datasource-juggler';
#model({ settings: { strict: 'filter' } })
export class Game extends Entity {
#property({
type: 'string',
id: true,
})
id?: string;
#property({
type: 'string',
required: true,
})
name?: string;
#property({
type: 'string',
})
shortName?: string;
#property({
type: 'string',
})
avatar?: string;
#hasMany<Rank>(() => Rank, { keyTo: 'gameId' })
ranks?: Rank[];
constructor(data?: Partial<Game>) {
super(data);
}
}
export interface GameRelations {
}
export type GameWithRelations = Game & GameRelations;
Game Controller
// in this method
// 500 Error: Invalid "filter.include" entries: {"relation":"ranks"}
#get('/games/{id}')
async findById(#param.path.string('id') id: string): Promise<Game> {
return await this.gameRepository.findById(id, { include: [{ relation: 'ranks' }] });
}
Please run your application with DEBUG=loopback:repository:relation-helpers, that way you will get a debug message explaining why filter.include entry was rejected.
You can find the code building the error message here:
https://github.com/strongloop/loopback-next/blob/97ba7893e253bfc2967ac08e408b211c9b9b7f40/packages/repository/src/relations/relation.helpers.ts#L96-L100
The most likely cause: your GameRepository does not have any InclusionResolver registered for ranks relation.
Please refer to our todo-list example to see how to register inclusion resolver. Cross-posting from https://github.com/strongloop/loopback-next/blob/97ba7893e253bfc2967ac08e408b211c9b9b7f40/examples/todo-list/src/repositories/todo-list.repository.ts#L41-L46:
this.todos = this.createHasManyRepositoryFactoryFor(
'todos',
todoRepositoryGetter,
);
this.registerInclusionResolver('todos', this.todos.inclusionResolver);