Nest JS DTO and Entity Model JSON mismatch issue - node.js

I have requirement to accept type custom JSON from front end and I’m trying to achieve this in NestJS but I’m unable to resolve.
My entity model
import { GraphQLJSONObject } from “graphql-type-json”
export class User {
#Field(()=> GraphQLJSONObject)
#Column({name: meta_data, type: simple-json})
metaData: object
}
DTO class:
export class CreateUserDto {
#Field()
readonly metaData: object;
}
I’m keep getting error
Error: undefined type error. Make sure you are providing an explicit type for the metaData of CreateUserDto class
I’m new to Nestjs and Graphql.

Your usage of graphql-type-json was correct. The error happens because Nest.js can't infer the type of the CreateUserDto.metadata field, You need to use a similar annotation.
#Field(() => GraphQLJSONObject)
readonly metaData: object;
For reference, such error can happen when you use an #Args field too (say in your mutation)
#Mutation((returns) => MyResultSchema)
async createUser(#Args('user', { type: () => GraphQLJSONObject }) user: object)
There's indeed some repetition, but that's because some type information is lost after Typescript is transpiled.

Related

nestjs + graphql + typeorm, how to transform a lazy relation (promise) into solid array for the model

I am using nestjs with graphql (apollo) and typeorm. My entity has a lazy relation roles and the model represents it as an array. The issue is that it cannot seem to "magically" transform the Promise<[]> into []. Here's in more detail,
I have the UserModel which looks like this
#ObjectType({ description: 'User' })
export class UserModel {
#Field()
id: string;
#Field(() => [UserRolesModel], { nullable: true })
roles?: UserRolesModel[]
}
and the UserEntity which looks like that
#Entity({ name: 'user' })
export class UserEntity {
#PrimaryGeneratedColumn('uuid')
id: string;
#OneToMany(() => UserRolesEntity, (userRoles) => userRoles.userId)
roles: Promise<UserRolesEntity[]>;
}
typescript complains that the Promise<UserRolesEntity[]> is not compatible with UserRolesModel[].
TS2322: Type 'UserEntity[]' is not assignable to type 'UserModel[]'.
Type 'UserEntity' is not assignable to type 'UserModel'.
Types of property 'roles' are incompatible.
Type 'Promise<UserRolesEntity[]>' is missing the following properties from type 'UserRolesModel[]': length, pop, push, concat, and 29 more.
I suspect there is some trivial way of doing that with nestjs and the class-transformer?
What is the recommended way of solving this?

Rename property in a json body

I am using NestJs to create an API, I currently have an endpoint post that receives parameters, processes it, and responds. The problem I have is that I want to change the name of a parameter, but only at the Json level, I tried with the Expose decorator, but it changes the attribute throughout the project.
tokens.controllers.ts
#Post()
async create(#Body() createTokenDto: CreateTokenDto) {
return await this.tokensService.create(createTokenDto);
}
create-token.dto.ts
export class CreateTokenDto {
#IsString()
#IsNotEmpty()
#Length(36)
#Expose({ name: 'product_uuid' })
productUuid: string;
}
The problem with this, is that now the CreateTokenDto object has the value productUuid is always null, is there any way to change the attribute only to the json it receives?

Typescript compiler error about incompatble types

I'm working on an API written in Typescript 3.9.7 running on Node 10. I've removed unnecessary details, but I'm basically performing the following operations:
Pulling user data from the database.
Adding a 'state' field to each user object
Sending the data to the UI.
I'm trying to use interfaces to add some type safety, but I seem to be misusing them since the TS compiler gives me some errors. Advice on how to resolve this would be helpful.
I've trimmed out other details, but here's my method where I fetch the user data and add the state field:
public async getUsers(
parameter: string
): Promise<AugmentedUser[]> {
//return an array of User objects based on some parameter
const userData = await this.userService.getAll<User>(parameter);
return userData.forEach((userRow: User) => {
userRow.state = "inactive";
});
}
and my interfaces are
export interface User {
id: number;
firstName: string;
//some other fields
}
export interface AugmentedUser extends User {
state: string;
}
I get the following error from the compiler:
error TS2322: Type 'UserData[]' is not assignable to type 'AugmentedUser[]'. Property 'state' is missing in type 'User' but required in type 'AugmentedUser'.
What am I doing wrong? I added the state field in the forEach loop so why am I getting this error? Thanks.
forEach does not return anything. Try the map function:
return userData.map((userRow: User) => {
return {...userRow, state: 'inactive'};
});
This will generate a list of objects with all the User properties plus the state present in AugmentedUser.

type-graphql entities how to omit fields from JSON.stringify?

We are using https://github.com/MichalLytek/type-graphql to define our graphql schema When we serialize the raw typescript entity object this doesn't respect the various field annotations in our GQL entities and ends up leaking unwanted data. Example below a Profile entity class
import { Field, Int, ObjectType } from 'type-graphql'
import { Column, Entity, ManyToOne, OneToMany } from 'typeorm'
import { Account } from '../account/account.entity'
export class Profile {
#Field()
#Column({ unique: true })
public username: string
#Field()
#Column()
public name: string
// Relations
#Column()
public accountId: string
#ManyToOne(type => Account, account => account.profiles, { eager: true })
public account: Account
}
account has sensitive data. When we JSON.stringify a Profile reference we don't want account output. Account is not annotated with #Field and we expect it would not be output.
The decorators used by type-graphql only exist to instruct type-graphql how to translate your class to a GraphQL type -- they are not going to somehow impact how an instance of the class is serialized by a native function like JSON.stringify.
In the context of your schema, the account won't ever be returned in the response unless you explicitly create a field for it, even if the Profile instance used by your resolvers has that property. This is a symptom of how field resolution works in GraphQL.js. However, a Profile instance will always have an account property on it because that's what you've defined as part of your class.
It's unclear from your question why you're calling stringify in the first place, but assuming it's to use in some other context, like logging, then you'll want to expose your own method for serializing the instance that limits which properties are returned. This can be done easily using something like lodash's pick or omit.
serialize () {
return _.omit(this, ['account'])
}

Schema Generator cannot determine GraphQL output type for custom object within DTO

Coming from a laravel background I try to get into node.js/graphQL/MongoDB and stumbled over nest.js framework which looks pretty nice. So I tried to set up a simple GraphQL API for testing and understanding how it all works. Therefore I created a mongoose Schema for a user as well as a model (type-graphql) and a DTO for creating such a user via mutation.
This works pretty fine but then I wanted to add a nested object called settings within the user to try how this would work and I simply don't get it and also don't find any nest.js or type-graphql specific examples for such an implementation. Is this simply not feasible using the "code first" approach of nest.js? Because the schema generator always gives me an error while compilation saying :
"UnhandledPromiseRejectionWarning: Error: Cannot determine GraphQL output type for settings".
user.model.ts
import { Field, ID, ObjectType } from 'type-graphql'
import UserSettingsType from './user.settings.type'
#ObjectType()
export class User {
#Field((type) => ID)
id: string
#Field()
email: string
#Field((type) => UserSettingsType)
settings: {}
}
user.settings.type.ts
import { Field, ObjectType } from 'type-graphql'
#ObjectType()
export class UserSettings {
#Field()
theme: string
#Field()
description?: string
#Field((type) => [String])
visited: string[]
}
new-user.input.ts
import { IsOptional, IsEmail, IsBoolean, Length, MaxLength, IsArray } from 'class-validator'
import { Field, InputType } from 'type-graphql'
import UserSettingsType from '../graphql/user.settings.type'
#InputType()
export class NewUserInput {
#Field()
#IsEmail()
email: string
#Field((type) => UserSettingsType)
#IsOptional()
#IsArray()
settings: {}
}
As you can see I defined a new type UserSettings in a separate file (following the best practices of nest.js) and allocated it as a type within the User model as well as the new-user DTO class. The error is thrown for the DTO class (NOT for the model) and if I change it there to something like [String] instead of UserSettingsType it is compiling.
I'm not sure if you need the resolver or the service/mongoose logic for this actual problem if so I can also post that of course!
Had similar issue migrating from express to nestjs.
After reading the docs realised that problem isn't the nested objects, but wrong imports.
You should use #nestjs/graphql imports instead of type-graphql.
E.G.
// changes this
import { Field, ID, ObjectType } from 'type-graphql'
// to
import { Field, ID, ObjectType } from '#nestjs/graphql'
Same applies to any other import from type-graphql

Resources