#DTO Field defined as number but treated as a string?

34 messages · Page 1 of 1 (latest)

keen fiber
#

I have a problem I have a DTO that I created:

export class UpdateScoreDto {
    @IsString()
    readonly username: string;

    @IsNotEmpty()
    readonly score: number;
}

(I also tried adding @IsNumber())
But when using this DTO and accessing score to update my value in prisma I get:
Argument score: Invalid value provided. Expected Int or IntFieldUpdateOperationsInput, provided String.

(This is the code I run:

this.prisma.score.update({
                where: { id: player.score.id },
                data: {
                    score: updateScoreDto.score,
                },
            });

)

I can't cast it into an int because it is already an int and parseInt gives me an error (that it expects a string and I give a number).

BUT what did seems to work is:

score: parseInt(String(updateScoreDto.score))

So... even after declaring my score to be a number, it is treated as a string, but it is NOT a string, so I can't use parseInt on it, BUT, I can use String to convert the number into a string and then convert it back into a number!

Please help me, this doesn't make sense to me...

plucky stag
#

Can you not use parseInt because Typescript says so, or because Javascript says so? You've told Tyopescript it'll be a number, but you say it's still coming in as a string, meaning that something in the deserialization of the data is not going how you want it to

#

How are you sending the data?

keen fiber
#

I use Insomania and pass it as json, I printed the json and it seems to be treated as a number upon sending:
{username: 'Test', score: 999}

plucky stag
#

Hmm, usually the application/json parser is good about properly treating numbers as numbers. That's strange indeed

keen fiber
#

and it seems that its TS that blocks me

#

Argument of type 'number' is not assignable to parameter of type 'string'.ts

plucky stag
#

You could end up using transform: true from the ValidationPipe so that you get the transformed data, which class-transformer should probably handle, but I would think the json parser would take care of this as well

keen fiber
#

Actually tried that too

#

Its not working too :\

plucky stag
#

I'd be curious to see what a middleware says the req.body is

keen fiber
#

tried @Transform even and to cast into a number

plucky stag
#

Oh really? Hmm. Can you provide a minimum reproduction?

keen fiber
#

Yeah I think let me put something together

#
// leaderboard/update-score.dto.ts

import { IsNotEmpty, IsString } from 'class-validator';

export class UpdateScoreDto {
    @IsString()
    readonly username: string;

    @IsNotEmpty()
    readonly score: number;
}

// leaderboard.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { LeaderboardService } from './leaderboard.service';
import { UpdateScoreDto } from './dto';

@Controller('leaderboard')
export class LeaderboardController {
    constructor(private readonly leaderboardService: LeaderboardService){}

    @Post('update')
    updateScore(@Body() updateScoreDto: UpdateScoreDto){
        return this.leaderboardService.updateScore(updateScoreDto);
    }
}

// leaderboard.service.ts
import { ForbiddenException, Injectable, NotFoundException } from '@nestjs/common';
import { UpdateScoreDto } from './dto';
import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class LeaderboardService {
    constructor(private readonly prisma: PrismaService) { }
    
    async updateScore(updateScoreDto: UpdateScoreDto) {
         return await this.prisma.score.update({
                where: { id: 1 }, // Just for example
                data: {
                    score: updateScoreDto.score
                },
            });
    }
}
#

And this is the prisma service:

// service
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient {
    constructor(config: ConfigService) {
        super({
            datasources: {
                db: {
                    url: config.get('DATABASE_URL')
                }
            }
        });
    }

    cleanDb() {
        return this.$transaction([
            this.score.deleteMany(),
            this.player.deleteMany(),
        ]);
    }
}

// module
import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Global()
@Module({
  providers: [PrismaService],
  exports: [PrismaService]
})
export class PrismaModule {}

And this is my schema:

model Player {
  id        Int      @id @default(autoincrement())
  username  String   @unique
  createdAt DateTime @default(now())
  score     Score? // Define the relationship as optional

  @@map("players")
}

model Score {
  id        Int      @id @default(autoincrement())
  score     Int
  timestamp DateTime @default(now())

  playerId Int    @unique // Make the playerId unique
  player   Player @relation(fields: [playerId], references: [id])

  @@map("scores")
}

Hope this is enough @plucky stag

plucky stag
#

I'll read through this, but could you make a git repo that I can clone?

keen fiber
#

Yeah I'll try, it will take some time though

#

Thank you!

plucky stag
#

Oh wait, are you sending it as Form? Isn't that set as mutlipart/form-data? Or can you set that up with Insomnia to ba application/json?

keen fiber
#

Its Form URL Encoded so the Content-Type is: application/json; charset=utf-8

plucky stag
#

Ah! II think that charset in the content-type might be messing with the content parser. Let me go take a look at body-parser and see if I can figure out what's going on/how to fix it

keen fiber
#

Alright 🫡

#

Ok - think I got something

#

I changed into this and now its working fine

#

Ok - so maybe Insomania did send the score as a string?

#

@plucky stag (Just a notification in case you are looking around, I think this should do it?)

plucky stag
#

Interesting. Yeah, I'd blame Insomnia then. Good to know

keen fiber
#

Thanks for the help!!

kindred shale
#

@keen fiber also, make sure to use an IsNumber decorator on the score

#

Non-desired datatypes should be passing through your dtos

keen fiber
#

@kindred shale Thanks 🙂