How to validate Dynamic key -> value DTO validation in nest js? - nestjs

import { ApiProperty } from '#nestjs/swagger';
import { IsString, ValidateNested } from 'class-validator';
export class TestDto {
#ApiProperty()
test: string;
}
export class UserReqDto {
#ApiProperty()
#IsString()
id: string;
#ApiProperty()
#ValidateNested({ each: true })
data: object;
}
const sampleData = {
id: 'asbd',
data: {
['any dynamic key 1']: {
test: '1',
},
['any dynamic key 2']: {
test: '2',
},
},
};
Here UserReqDto is my main DTO and TestDto is child DTO.
I need to validate sampleData type of data.
How can I do that?
in data field i need to validate object of TestDto type's objects

You can use Map<string, TestDto> as type for data field:
#ApiProperty()
#ValidateNested({ each: true })
data: Map<string, TestDto>
Note: Nested object must be an instance of a class, otherwise #ValidateNested won't know what class is target of validation so you can use class-transformer to transform data value to instance of TestDto.
#ApiProperty()
#ValidateNested({ each: true })
#Type(() => TestDto)
data: Map<string, TestDto>

Related

Nest.JS validate object where keys are enum and values ​have the same shape

I need to validate an object where each key is an enum and each value have the same shape.
Right now I was able to validate the object by explicitly setting each key and validate each nested object, but I would like something more usable so when I need to change the enum I don't have to update the dtos too.
This is the enum I would like to use
enum Countries {
ES = 'ES',
IT = 'IT'
}
This is my actual work.
class Country {
#IsString({ each: true })
#IsOptional()
readonly region?: string[];
#IsString({ each: true })
#IsOptional()
readonly province?: string[];
#IsString({ each: true })
#IsOptional()
readonly zipcode?: string[];
}
class Locations {
#IsObject()
#ValidateNested()
#Type(() => Country)
#IsOptional()
readonly ES?: Country;
#IsObject()
#ValidateNested()
#Type(() => Country)
#IsOptional()
readonly IT?: Country;
}
export class CreateDto {
... other props
#IsObject()
#ValidateNested()
#Type(() => Locations)
#IsOptional()
readonly locations?: Locations;
}
As you can see if I add a new country to the enum I have to update the class Locations with the corresponding prop.
## Example Payload
{
... other props
locations: {
IT: {
region: ['Lazio']
}
}
}
Data structures that frequently need to change keys are not good. I think the data structure should be designed like this:
enum Country {
ES = 'ES',
IT = 'IT'
}
class Location {
#IsEnum(Country)
country: Country;
#IsString({ each: true })
#IsOptional()
readonly region?: string[];
#IsString({ each: true })
#IsOptional()
readonly province?: string[];
#IsString({ each: true })
#IsOptional()
readonly zipcode?: string[];
}
export class CreateDto {
... other props
#ValidateNested({ each: true })
#Type(() => Location)
#IsOptional()
readonly locations?: Location[];
}

nestjsx/crud typeorm relations query

How do I have to write the query in order to get relations of the entity populated with?
Checked the below options, but none of them works.
localhost:3000/reservations?join=concert_id
localhost:3000/reservations?join=concert
If I change the concert_id key within the join object of #Crud() options to concert (as the name of the property in the Reservation.entity.ts file is just concert, not concert_id), then I get an error error: column reference "Reservation_id" is ambiguous.
Any help is much appreciated. Thanks in advance!
Reservation.entity.ts
#Entity('reservations')
export class Reservation {
#PrimaryGeneratedColumn()
id: number;
#Column({ name: 'booking_reference' })
bookingReference: string;
#ManyToOne(() => Concert, (concert) => concert.reservations)
#JoinColumn({ name: 'concert_id' })
concert: Concert;
}
Concert.entity.ts
#Entity('concerts')
export class Concert {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#OneToMany(() => Reservation, (reservation) => reservation.concert)
reservations: Reservation[];
}
Reservation.controller.ts
#Crud({
model: {
type: Reservation
},
dto: {
create: CreateReservationDto
},
validation: { always: true },
query: {
join: {
concert_id: {
eager: true,
alias: "concert"
}
}
}
})
#ApiTags('Reservation')
#Controller('reservation')
export class ReservationController implements CrudController<Reservation> {
constructor(public service: ReservationService) {}

TypeORM - How to insert into table with foreign key without fetching relation first

I am creating a small app using NestJS and TypeORM and I am having trouble inserting into a table that has a composite foreign key.
Whenever the insert is executed, all columns get filled, except for the foreign keys, which stay as null.
Here are my entities:
Employee
import { Column, Entity, PrimaryColumn, PrimaryGeneratedColumn } from 'typeorm';
#Entity({ name: 'employees' })
export class Employee {
#PrimaryGeneratedColumn('uuid')
id: string;
#PrimaryColumn()
version: number;
#Column({ name: 'employee_name', type: 'varchar' })
employeeName: string;
#Column({ name: 'employee_salary', type: 'numeric' })
employeeSalary: string;
}
Employee Payroll
import {
Column,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { Employee } from '../../employee/entities/employee.entity';
#Entity({ name: 'employee_payrolls' })
export class EmployeePayroll {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column({ name: 'employee_payroll_name', nullable: true })
employeePayrollName: string;
#ManyToOne(() => Employee)
#JoinColumn([
{ name: 'employee_id', referencedColumnName: 'id' },
{ name: 'employee_version', referencedColumnName: 'version' },
])
employee: Employee;
}
Employee Payroll Service
And here is the code that is doing the insert:
export class EmployeePayrollDTO {
employeePayrollName: string;
employeeId: string;
employeeVersion: number;
}
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { EmployeePayrollDTO } from 'src/employee-payroll/dto/employee-payroll.dto';
import { EmployeePayroll } from 'src/employee-payroll/entities/employee-payroll.entity';
import { Repository } from 'typeorm';
#Injectable()
export class EmployeePayrollService {
constructor(
#InjectRepository(EmployeePayroll)
private readonly employeeRepository: Repository<EmployeePayroll>,
) {}
async addEmployeePayroll(employeePayroll: EmployeePayrollDTO) {
return await this.employeeRepository
.createQueryBuilder()
.insert()
.into(EmployeePayroll)
.values(employeePayroll)
.execute();
}
}
The Problem
The problem is that while the insert is successful, the values of the columns: employee_id and employee_version are null:
id
employee_payroll_name
employee_id
employee_version
53de51fd-6c9e-4b96-8906-edd1f6eea26c
Payroll 1
null
null
64b8a147-acee-4f43-9ea1-b64c2c036369
Payroll 2
null
null
Can you help me?
As per #Michael Levi's comment, the problem was that I wasn't setting the employee object correctly.
Here is what worked for me:
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { EmployeePayrollDTO } from 'src/employee-payroll/dto/employee-payroll.dto';
import { EmployeePayroll } from 'src/employee-payroll/entities/employee-payroll.entity';
import { Repository } from 'typeorm';
#Injectable()
export class EmployeePayrollService {
constructor(
#InjectRepository(EmployeePayroll)
private readonly employeeRepository: Repository<EmployeePayroll>,
) {}
async addEmployeePayroll(employeePayroll: EmployeePayrollDTO) {
return await this.employeeRepository
.createQueryBuilder()
.insert()
.into(EmployeePayroll)
.values({
employeePayrollName: employeePayroll.employeePayrollName,
employee: {
id: employeePayroll.employeeId,
version: employeePayroll.employeeVersion,
},
})
.execute();
}
}
Please note that this only inserts records in the EmployeePayroll table, and if I try to use a new id or version inside the employee object, I get a foreign key violation error.

Storing GeoJson points and finding points within a given distance/radius | NODEJS, Postgres, NestJs, TypeOrm

Thank you in advance.
I have scavenged the internet for a working example/documentation for a way to store location point (longitude, latitude), find distance between two points, find points within a given distance.
I am using typeorm, nestjs, postgresql.
(I already tried Mariadb but St_distance_sphere is not working there so am going with postgresql)
this is my entity
#ApiProperty({
type: String,
title: 'current_location',
example: '{"type":"Point","coordinates":[28.612849, 77.229883]}',
})
#Index({ spatial: true })
#Column({
type: 'geometry',
srid: 4326,
nullable: true,
spatialFeatureType: 'Point',
transformer: {
to: (v: Point) => {
console.log(JSON.stringify(v));
return eval(`ST_GeomFromGeoJSON(${JSON.stringify(v)})`);
},
from: (v: any) => {
return { type: 'Point', coordinates: [v.x, v.y] } as Point;
},
},
})
current_location: string;
there seem to be too much postgres/postgis documentation but nothing useful for my case.
any help is much appreciated. I have been stuck on this for more than a week.
*note: I don't want to use JSONB datatype for its slower speed.
The following code will store in the DB and finds the locations within the range
Tech Stack nestJS,typeorm,postgres,postgis extension,#types/geojson
testlocation.entity.ts
import { Column, Entity, Index, PrimaryGeneratedColumn} from 'typeorm';
import { Geometry, Point } from 'geojson';
#Entity({ name: 't_test_location' })
export class TestLocation {
#PrimaryGeneratedColumn('increment')
pk_id: number;
#Column({ type: 'varchar', name: 's_city' })
city: string;
#Column({ type: 'double precision', name: 'd_lat' })
lat: number;
#Column({ type: 'double precision', name: 'd_long' })
long: number;
#Index({ spatial: true })
#Column({
type: 'geography',
spatialFeatureType: 'Point',
srid: 4326,
nullable: true,
})
location:Point
}
location.service.ts
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { TestLocation } from 'src/model/testlocation.entity';
import { getManager, QueryBuilder, Repository } from 'typeorm';
import { Geometry, Point } from 'geojson';
#Injectable()
export class LocationService {
constructor(
#InjectRepository(TestLocation) private readonly repo: Repository<TestLocation>,
) {}
public async getAll() {
return await this.repo.find();
}
public async create(location:TestLocation){
const pointObject :Point= {
type: "Point",
coordinates: [location.long,location.lat]
};
location.location = pointObject;
return await this.repo.save(location)
}
public async getRange(lat:number,long:number,range:number = 1000) {
let origin = {
type: "Point",
coordinates: [long, lat]
};
let locations = await this.repo
.createQueryBuilder('t_test_location')
.select(['t_test_location.city AS city','ST_Distance(location, ST_SetSRID(ST_GeomFromGeoJSON(:origin), ST_SRID(location)))/1000 AS distance' ])
.where("ST_DWithin(location, ST_SetSRID(ST_GeomFromGeoJSON(:origin), ST_SRID(location)) ,:range)")
.orderBy("distance","ASC")
.setParameters({
// stringify GeoJSON
origin: JSON.stringify(origin),
range:range*1000 //KM conversion
})
.getRawMany();
return locations;
}
}
location.controller.ts
import { Body, Controller, Get, Post } from '#nestjs/common';
import { TestLocation } from 'src/model/testlocation.entity';
import { LocationService } from './location.service';
#Controller('location')
export class LocationController {
constructor(private serv: LocationService) {}
#Get()
public async getAll() {
return await this.serv.getAll();
}
#Post()
createLocation(#Body() location : TestLocation): void{
this.serv.create(location);
}
#Post('range')
public async getRange(#Body() location : {
lat:number,
long:number,
range:number
}){
return await this.serv.getRange(location.lat,location.long,location.range);
}
}

mongoose Document doesn't map a property correctly

I have a nodeJS Server with express project with a mongoDB.
I want to map this object, coming from an endpoint:
{
userId: '5ca14305e73fc8453843d3e1',
bookingId: '29b0c5e0-e504-43bc-b5a1-9326d7d41d45'
}
Its defined through a class like this:
export class CreateBookerDto {
#IsString() readonly userId: string;
#IsString() readonly bookingId: string;
}
This is my actual code doing the mapping:
export interface IBooker extends Document {
userId: string;
bookingId: string;
createdAt?: number;
}
constructor(
#InjectModel('Booker') private readonly bookerModel: Model<IBooker>
) { }
async createBooker(booker: CreateBookerDto) {
let createdBooker = new this.bookerModel(booker);
createdBooker.createdAt = moment.now();
return createdBooker.save();
}
This is my output from createdBooker before the save:
{
_id: 5ca146e4ba2c08380c435453,
bookingId: '29b0c5e0-e504-43bc-b5a1-9326d7d41d45'
}
Where and why does he drop the properties userId and createdAt?

Resources