How to store big int in nest js using typeorm - nestjs

some.entity.ts
amount:number
But when I store a very large data in my postgres it throws error '''integer out of range'''
My question is how can I store Big Int as type in psql using typeorm

Define type bigint in #Column decorator,
#Column({type: 'bigint'})
columnName: string;
Note: that based on TypeOrm documentation bigint is mapped to string.

Great resonse from #Riajul Islam!
As an addition to his answer, if you want to store bigint in PrimaryGeneratedColumn, you should you the following:
#PrimaryGeneratedColumn( 'increment', {type: 'bigint'} )
id: number;

Just add { bigNumberStrings: false } to TypeORM's configuration, such as:
TypeOrmModule.forRoot({
bigNumberStrings: false,
...config.database,
}),
Then the bigint will return number type.

I used column transformer:
export class ColumnNumberTransformer {
public to(data: number): number {
return data;
}
public from(data: string): number {
// output value, you can use Number, parseFloat variations
// also you can add nullable condition:
// if (!Boolean(data)) return 0;
return parseInt(data);
}
}
Then in entity:
#Entity('accounts')
export class AccountEntity extends BaseEntity {
#Column({
type: 'bigint',
nullable: false,
transformer: new ColumnNumberTransformer()
})
public balance: number;
}

Related

Typeorm transformer always return true

I'm using typeorm and trying to transform a column in the database to bollean instead of string.
The field in the bank is bit.
But I want to return as boolean, but when using or transforming it always returns true, what to do?
export default class ColumnBooleanTransformer implements ValueTransformer {
public from(value?: string | null): boolean | undefined {
return Boolean(Number(value));
}
public to(value?: boolean | null): string | undefined {
return value ? '1' : '0';
}
My column:
#Column({
nullable: false,
transformer: new ColumnBooleanTransformer(),
})
STAProvado: boolean;
I found the solution, add type property to column, use the same type created in the bank
#Column({
type: 'bit',
nullable: false,
transformer: new ColumnBooleanTransformer(),
})
isAdmin: boolean;

Two validators for one single entity in DTO

Are there any ways or would it be possible to have two validator in one single entity? Like for the given example code below, the identifier would accept an email as its payload but it would also accept
number/mobile number as its payload as well.
#ApiProperty()
#IsString()
#IsNotEmpty()
#IsEmail()
identifier: string;
EDIT:
I have tried,
#ApiProperty()
#IsString()
#IsNotEmpty()
#IsEmail()
#IsPhoneNumber('US')
identifier: string;
But it does not work.
EDIT 2:
I found a reference code based on this previous thread, How to use else condition in validationif decorator nestjs class-validator?, and I copied his validation class.
import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from "class-validator";
import { IdentifierType } from "../interface/access.interface";
#ValidatorConstraint({ name: 'IdentifierValidation', async: false })
export class IdentifierValidation implements ValidatorConstraintInterface {
validate(identifier: string, args: ValidationArguments) {
if (JSON.parse(JSON.stringify(args.object)).type === IdentifierType.MOBILE) {
var regexp = new RegExp('/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im');
// "regexp" variable now validate phone number.
return regexp.test(identifier);
} else {
regexp = new RegExp("^[a-zA-Z0-9_.+-]+#[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$");
// "regexp" variable now validate email address.
return regexp.test(identifier);
}
}
defaultMessage(args: ValidationArguments) {
if (JSON.parse(JSON.stringify(args.object)).type === IdentifierType.MOBILE) {
return 'Enter a valid phone number.'
} else {
return 'Enter a valid email address.'
}
}
}
DTO -
export class VerifyOtpDto {
#Validate(IdentifierValidation)
#ApiProperty()
#IsNotEmpty()
identifier: string;
#ApiProperty({ enum: IdentifierType })
#IsNotEmpty()
identifierType: IdentifierType;
}
ENUM -
export enum IdentifierType {
EMAIL = 'email',
MOBILE = 'mobile',
}
It does work with email but trying to feed a mobile number still does not work.
You have two ways to do this, first with regex:
#Matches(/YOUR_REGEX/, {message: 'identifier should be email or phone'})
identifier: string;
Or you can get the idea from this:
#IsType(Array<(val: any) => boolean>)
#IsType([
val => typeof val == 'string',
val => typeof val == 'boolean',
])
private readonly foo: boolean | string;
Of course it can get more than one validator in one DTO column.
Did you check https://www.npmjs.com/package/class-validator here?
if you want to check mobile number, you can use to #IsMobilePhone(locale: string).

TypeORM getRawOne<T> not returning type T

I'm working on refactoring a koa api to nest and am kinda stuck on refactoring the queries from native psql to typeorm. I have the following table, view and dto.
#Entity()
export class Challenge {
#PrimaryGeneratedColumn()
id!: number;
#Column()
endDate!: Date;
#CreateDateColumn()
createdAt!: Date;
}
#ViewEntity({
expression: (connection: Connection) => connection.createQueryBuilder()
.select('SUM(cp.points)', 'score')
.addSelect('cp.challenge', 'challengeId')
.addSelect('cp.user', 'userId')
.addSelect('RANK() OVER (PARTITION BY cp."challengeId" ORDER BY SUM(cp.points) DESC) AS rank')
.from(ChallengePoint, 'cp')
.groupBy('cp.challenge')
.addGroupBy('cp.user')
})
export class ChallengeRank {
#ViewColumn()
score!: number;
#ViewColumn()
rank!: number;
#ViewColumn()
challenge!: Challenge;
#ViewColumn()
user!: User;
}
export class ChallengeResultReponseDto {
#ApiProperty()
id!: number;
#ApiProperty()
endDate!: Date;
#ApiProperty()
createdAt!: Date;
#ApiProperty()
score: number;
#ApiProperty()
rank: number;
test() {
console.log("test")
}
}
As the object I want to return is not of any entity type, I'm kinda lost on how to select it and return the correct class. I tried the following:
this.challengeRepository.createQueryBuilder('c')
.select('c.id', 'id')
.addSelect('c.endDate', 'endDate')
.addSelect('c.createdAt', 'createdAt')
.addSelect('cr.score', 'score')
.addSelect('cr.rank', 'rank')
.leftJoin(ChallengeRank, 'cr', 'c.id = cr."challengeId" AND cr."userId" = :userId', { userId })
.where('c.id = :id', { id })
.getRawOne<ChallengeResultReponseDto>();
Which returns an object that has the correct fields, but that is not of the class type "ChallengeResultReponseDto". If I try to call the function "test" the application crashes. Further it feels weird to use the challengeRepository but not return a challenge, should I use the connection or entity manager for this instead?
I'm rather certain that getRawOne<T>() returns a JSON that looks like whatever you give the generic (T), but an not instance of that class. You should try using getOne() instead to get the instance of the returned entity

Sequelize TypeScript: How to use `createdAt` in scope's `where` condition if it is not as part of model's attributes?

I try to use Sequelize with TypeScript. I copy the code from official document TypeScript section like this:
// These are all the attributes in the User model
interface UserAttributes {
id: number;
name: string;
preferredName: string | null;
}
interface UserCreationAttributes extends Optional<UserAttributes, "id"> {}
class User extends Model<UserAttributes, UserCreationAttributes>
implements UserAttributes {
public id!: number;
public name!: string;
public preferredName!: string | null;
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
}
However, when I initialize User table, I want define a default scope, like updatedAt filed have to satisfy some conditions:
User.init(userTableAttributes,
{
tableName: "user",
sequelize,
paranoid: true,
defaultScope: {
where: {
updatedAt: {
[Op.eq]: null
}
}
}
}
);
But this code is wrong, it seems the because the updatedAt field in not defined in UserAttributes.
So how can I solve this problem? I only come up with one way: define the updatedAt field in UserAttributes? But it doesn't make any sense, because I think the UserAttributes only include business attributes, right?
I found out if I added paranoid, it will auto add deletedAt is NULL condition in query string

Sequelize-typescript 'HasManyCreateAssociationMixin' is not a function

I have a model in sequelize-typescript, Door.ts:
import { Table, Model, Column, AutoIncrement, PrimaryKey, ForeignKey, DataType, AllowNull, BelongsTo, HasMany } from 'sequelize-typescript';
import { Location } from '#modules/location';
import { AkilesServiceV1, AkilesServiceV0, IDoorService } from '#services/DoorService';
import { BelongsToGetAssociationMixin } from 'sequelize/types';
import { DoorLog } from '#modules/door_log';
import { HasManyCreateAssociationMixin } from 'sequelize';
#Table({ tableName: 'door' })
class Door extends Model<Door> {
#PrimaryKey
#AutoIncrement
#Column
id!: number;
#AllowNull(false)
#Column
type!: string;
#Column
button_id!: string;
#Column
gadget_id!: string;
#Column
action_id!: string;
#AllowNull(false)
#Column(DataType.ENUM('vehicular','pedestrian'))
access_type!: 'vehicular' | 'pedestrian';
#AllowNull(false)
#Column
description_tag!: string;
#Column(DataType.VIRTUAL)
description!: string;
#ForeignKey(() => Location)
#AllowNull(false)
#Column
location_id!: number;
#BelongsTo(() => Location)
location!: Location;
#HasMany(() => DoorLog)
door_logs!: DoorLog[];
public getLocation!: BelongsToGetAssociationMixin<Location>;
public createDoorLog!: HasManyCreateAssociationMixin<DoorLog>;
public async open () {
let doorService: IDoorService;
switch(this.type) {
case 'akiles-v0':
doorService = new AkilesServiceV0();
break;
case 'akiles-v1':
doorService = new AkilesServiceV1();
break;
default:
doorService = new AkilesServiceV1();
break;
}
//await doorService.open(this);
return await this.createDoorLog({ door_id: this.id, timestamp: new Date() });
}
public async getParking() {
const location: Location = await this.getLocation();
return await location.getParking();
}
}
export default Door
As you can see it has these two functions associated with Mixins:
public getLocation!: BelongsToGetAssociationMixin<Location>;
public createDoorLog!: HasManyCreateAssociationMixin<DoorLog>;
The first works perfectly using it like this: await this.getLocation(). However, the second when I call it like this: await this.createDoorlog ({door_id: this.id, timestamp: new Date ()}) returns the following error:
TypeError: this.createDoorLog is not a function
I've also tried calling the function without parameters but got the same result. I don't understand why the two functions, while created almost identically, behave differently. Am I missing something with HasManyCreateAssociationMixin?
Thank you.
For when I inevitably come across this question again perplexed by the same problem. The answer Is to ad "as" to the #HasMany mixin. Sequelize appears to have issues with camelcase classes.
So in this case adding
#HasMany(() => DoorLog, options: {as: "doorLog" })
door_logs!: DoorLog[];
or something along these lines should allow you to use this mixin

Resources