Tuesday, May 14, 2024
 Popular · Latest · Hot · Upcoming
20
rated 0 times [  21] [ 1]  / answers: 1 / hits: 6127  / 4 Years ago, fri, march 20, 2020, 12:00:00

I'm having some trouble hitting a POST endpoint that triggers a typeorm repository.save() method to my postgres DB.



Here's my DTO object:



import { ApiProperty } from '@nestjs/swagger/';
import { IsString, IsUUID} from 'class-validator';

import { Client } from '../../../models';
import { User } from '../../../user.decorator';


export class ClientDTO implements Readonly<ClientDTO> {
@ApiProperty({ required: true })
@IsUUID()
id: string;


@ApiProperty({ required: true })
@IsString()
name: string;

public static from(dto: Partial<ClientDTO>) {
const cl = new ClientDTO();
cl.id = dto.id;
cl.name = dto.name;
return cl;
}

public static fromEntity(entity: Client) {
return this.from({
id: entity.id,
name: entity.name,
});
}

public toEntity = (user: User | null) => {
const cl = new Client();
cl.id = this.id;
cl.name = this.name;
cl.createDateTime = new Date();
cl.createdBy = user ? user.id : null;
cl.lastChangedBy = user ? user.id : null;
return cl;
}
}


My controller at POST - /client:



import { 
Body,
Controller,
Get, Post
} from '@nestjs/common';

import { ClientDTO } from './dto/client.dto';
import { ClientService } from './client.service';
import { User } from 'src/user.decorator';

@Controller('client')
export class ClientController {
constructor(
private clientService: ClientService
) { }

@Get()
public async getAllClients(): Promise<ClientDTO[]> {
return this.clientService.getAllClients();
}

@Post()
public async createClient(@User() user: User, @Body() dto: ClientDTO): Promise<ClientDTO> {
return this.clientService.createClient(dto, user);
}
}


And my service:



import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

import { Client } from '../../models';
import { ClientDTO } from './dto/client.dto';
import { User } from '../../user.decorator';


@Injectable()
export class ClientService {
constructor(
@InjectRepository(Client) private readonly clientRepository: Repository<Client>
) {}

public async getAllClients(): Promise<ClientDTO[]> {
return await this.clientRepository.find()
.then(clients => clients.map(e => ClientDTO.fromEntity(e)));
}

public async createClient(dto: ClientDTO, user: User): Promise<ClientDTO> {
return this.clientRepository.save(dto.toEntity(user))
.then(e => ClientDTO.fromEntity(e));
}
}


I get a 500 internal server error with log message stating that my ClientDTO.toEntity is not a function.



TypeError: dto.toEntity is not a function
at ClientService.createClient (C:...nest-backenddistfeaturesclientclient.service.js:29:47)
at ClientController.createClient (C:...nest-backenddistfeaturesclientclient.controller.js:27:35)
at C:...nest-backendnode_modules@nestjscorerouterrouter-execution-context.js:37:29
at process._tickCallback (internal/process/next_tick.js:68:7)


I'm confused because this only happens via http request. I have a script that seed my dev database after I launch it fresh in a docker container called seed.ts:



import * as _ from 'lodash';

import { Client } from '../models';
import { ClientDTO } from '../features/client/dto/client.dto';
import { ClientService } from '../features/client/client.service';
import { configService } from '../config/config.service';
import { createConnection, ConnectionOptions } from 'typeorm';
import { User } from '../user.decorator';

async function run() {

const seedUser: User = { id: 'seed-user' };

const seedId = Date.now()
.toString()
.split('')
.reverse()
.reduce((s, it, x) => (x > 3 ? s : (s += it)), '');

const opt = {
...configService.getTypeOrmConfig(),
debug: true
};

const connection = await createConnection(opt as ConnectionOptions);
const clientService = new ClientService(connection.getRepository(Client));

const work = _.range(1, 10).map(n => ClientDTO.from({
name: `seed${seedId}-${n}`,
}))
######################## my service calls ClientDTO.toEntity() without issue ###########################
.map(dto => clientService.createClient(dto, seedUser)
.then(r => (console.log('done ->', r.name), r)))

return await Promise.all(work);
}

run()
.then(_ => console.log('...wait for script to exit'))
.catch(error => console.error('seed error', error));


It makes me think I am missing something simple/obvious.



Thanks!


More From » typescript

 Answers
13

The fact that the dto is declared like this dto: ClientDTO in the controller is not enough to create instances of the class. This is just an indication for you and the other developers on the project, to prevent misuses of the dto object in the rest of the application.



In order to have instances of classes, and use the methods from the class, you have to explicitly set a mapping like this:



@Post()
public async createClient(@User() user: User, @Body() dto: ClientDTO): Promise<ClientDTO> {
const client = ClientDTO.from(dto);
return this.clientService.createClient(client, user);
}


Assuming ClientDTO.from is the right function to use for the data contained in dto. If not, adapt it, create a new one, or add a constructor.


[#4431] Wednesday, March 18, 2020, 4 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
mckenna

Total Points: 445
Total Questions: 109
Total Answers: 109

Location: Virgin Islands (U.S.)
Member since Sun, May 16, 2021
3 Years ago
;