How to define GraphQL 2D array of a custom input type? - graphql-js

I have a NestJS (core version 8.2.4) GraphQL input type. I am getting an error when trying to use a custom input type in a array field (doesn't happen if I change the custom input type to Object)
Caught exception: Error: Cannot determine a GraphQL output type
for the "arrayValue". Make sure your class is decorated with an
appropriate decorator. CannotDetermineOutputTypeError:
Cannot determine a GraphQL output type for the "arrayValue".
Make sure your class is decorated with an appropriate decorator.
Here is the relevant code:
#InputType()
export class ChildInputType {
#Field()
readonly val1: number;
#Field()
readonly val2: number;
}
#InputType()
export class ParentInputType {
#Field({ nullable: true })
readonly name: string;
#Field(type => [ChildInputType], { nullable: true }) // changing to [Object] works
readonly arrayValue?: ChildInputType[];
}
Any idea how I get this to work? It's basically the same code as in NestJS GraphQL Resolvers docs example of Author and its [Post] field but it still doesn't work.

Related

TypeORM Query entity with jsonb column by json property

I have an entity which holds a json object called "stats". Stats has multiple properties which Id like to order my findAll method by.
I tried everything but I can not get the query to work using my attempts.
This is how I am trying to find all Collection entities ordered by the stats.one_day_volume property
The error I am getting with my last approach shown below is
"missing FROM-clause entry for table "stats"
const result = await getRepository(Collection)
.createQueryBuilder('collection')
.orderBy('collection.stats.one_day_volume', "DESC")
.take(take)
.skip(skip)
.getMany();
This is the entity class
#Entity()
export class Collection {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column({nullable:true})
external_link: string;
#Column({nullable:true})
description: string;
#Column()
slug: string;
#Column({nullable:true})
image_url: string;
#Column({nullable:true})
banner_image_url: string;
#Column()
dev_seller_fee_basis_points: string;
#Column()
safelist_request_status: string;
#Column({nullable:true})
payout_address: string;
#Column('jsonb')
primary_asset_contracts: AssetContract[];
#Column("simple-json")
traits: object;
#Column("jsonb", {array:true, nullable:true})
payment_tokens: PaymentToken[];
#Column("simple-array", {nullable:true})
editors: string[];
#Column("jsonb")
stats: CollectionStats;
#Column({ type: 'timestamptz' })
created_date: Date;
}
I was also looking for a way to orderBy on jsonb column
But there's an issue when you try to getMany with it.
So my workaround was to have it this way (use getRawMany instead of getMany):
const result = await getRepository(Collection)
.createQueryBuilder('collection')
.orderBy("collection.stats->>'one_day_volume'", "DESC")
.take(take)
.skip(skip)
.getRawMany();
So apparently with getRawMany it's working well.
To get it as an entity you can try create it from repo like so:
const repo = await getRepository(Collection);
const resultAsEntities = repo.create(result);
But looks like there are sometimes this won't work as expected due to relations and other objects so you may try achieve it this way:
const resultIds = result.map(item => item.id);
const resultAsEntities = await getRepository(Collection).findByIds(resultIds);

Cannot set values on uuid or enum fields in Postgres using TypeORM

I've been stuck on this for a good hour, and unfortunately can't find anything relevant in the docs.
I'm unable to set values on fields that use types like enum or uuid. For instance:
export enum PhotoStatus {
WORKING = 'WORKING',
DONE = 'DONE'
}
export class Photo {
#PrimaryGeneratedColumn()
id: number:
#Column()
size: string;
#Column({
type: 'enum',
enum: PhotoStatus,
default: PhotoStatus.WORKING,
})
status: PhotoStatus;
}
But when I try to create a new record, like so:
const photo = new Photo();
photo.size = 'huge';
photo.status = PhotoStatus.DONE;
await connection.manager.save(photo);
It throws an error:
BadRequestException: ERROR: column "status" is of type content_status_enum but expression is of type character varying
Hint: You will need to rewrite or cast the expression.
This happens for uuid columns too.

replace ObjectId field with custom string for ObjectIdColumn in TypeORM/MongoDB

I have a NestJs REST API and use TypeORM with MongoDB. I want to create a entity called project. I started with a basic entity and I just read that I should use ObjectIdColumn instead of PrimaryColumn for MongoDB.
#Entity()
export class Project extends BaseEntity {
// The technical project name
#ObjectIdColumn({ generated: false })
public id: ObjectID;
// The display name
#Column({ unique: true })
public name: string;
// The project successor
#Column({ nullable: true })
public successorId: ObjectID;
// Configuration stuff for that project
#Column()
public configuration: object;
}
I would like to know if it's possible to replace that object id column with a primary column of type string. The id field is based on a special pattern, e.g. the name field
my awesome project
would result into
my-awesome-project
for the id field. Sure I made use of generated: false but I have to pass in a custom string instead of an ObjectID. Currently this is not possible because the docs say the ObjectID
Can be a 24 byte hex string, 12 byte binary string or a Number. http://mongodb.github.io/node-mongodb-native/2.1/api/ObjectID.html
So what needs to get done to use a custom string as an ID field? The only thing I can think of is creating a second field e.g. theRealId and treat it like the ID field and ignore the autogenerated ObjectId...
From what I've learnt, here is what you can do
#Entity()
export class UserEntity
{
#ObjectIdColumn()
_id: string;
#PrimaryColumn()
id: string;
// The display name
#Column({ unique: true })
public name: string;
#Column({ nullable: true })
public successorId: ObjectID;
#Column()
public configuration: object;
}
MongoDB will use _id as an internal id, that you do not expose through your program (and api, then)
You will work with the id, "normally', and it will be your primary key, generating automatically and so on
Source : personal learning, and Udemy course : NestJS Zero to Hero - Modern TypeScript Back-end Development

How to make belongsTo relation work in loopback4

i'm trying to use loopback4 belongsTo relation but i get the same error " TypeError: Cannot read property 'target' of undefined"
source model
every room should have a type
export class Rooms extends Entity {
#property({
type: 'string',
id: true,
})
_id: string;
#property({
type: 'number',
})
rating?: number;
// relation ###
#belongsTo(() => RoomsTypes)
roomsTypesId: string;
constructor(data?: Partial<Rooms>) {
super(data);
}
}
target model
the room types model
#model({settings: {strict: false}})
export class RoomsTypes extends Entity {
#property({
type: 'string',
id: true,
})
_id: string;
#property({
type: 'string',
required: true,
})
name: string;
#property({
type: 'number',
required: true,
})
numOfPeople: number;
#property({
type: 'array',
itemType: 'string',
required: true,
})
features: string[];
constructor(data?: Partial<RoomsTypes>) {
super(data);
}
}
source repository
i think that the error is here
import {
DefaultCrudRepository,
juggler,
repository,
BelongsToAccessor,
} from '#loopback/repository';
import { Rooms, RoomsRelations, RoomsTypes, RoomsTypesRelations} from '../models';
import {inject, Getter} from '#loopback/core';
import { RoomsTypesRepository } from './rooms-types.repository';
export class RoomsRepository extends DefaultCrudRepository<
Rooms,
typeof Rooms.prototype._id,
RoomsRelations
> {
public readonly roomTypes: BelongsToAccessor<
RoomsTypes,
typeof Rooms.prototype._id
>;
constructor(
#inject('datasources.db') protected DbDataSource: juggler.DataSource,
#repository.getter('RoomsTypesRepository')
RoomsTypesRepositoryGetter: Getter<RoomsTypesRepository>,
) {
super(Rooms, DbDataSource);
this.roomTypes = this.createBelongsToAccessorFor(
'roomsTypesId',
RoomsTypesRepositoryGetter,
);
}
}
i get the same error " TypeError: Cannot read property 'target' of undefined"
When creating a createBelongsToAccessorFor you need to specify the relation name, not the relation id:
this.roomTypes = this.createBelongsToAccessorFor('roomsTypesId', RoomsTypesRepositoryGetter);
should be
this.roomTypes = this.createBelongsToAccessorFor('roomTypes', RoomsTypesRepositoryGetter);
Additionally should be mentioned that loopback has some asumptions to the naming of properties which are used for relations. For example the ID of an relation is constructed by the ModelName + Id. This is also used for lookup the target model.
If you need a different naming, you need to specify keyTo or keyFrom in the relation definition.
The official documentation was recently updated with a detailed description and examples of the #belongsTo relation:
The #belongsTo decorator takes three parameters:
target model class (required)
relation definition (optional) - has three attributes, keyFrom, keyTo, name
keyFrom is the property name of the foreign key on the “source” model. It is always set to the decorated property name (in the given
example it is, “customerId” property on Order model).
keyTo is the property name of the foreign key on the “target” model, it is typically the primary key of the “target” model. keyTo
attribute defaults to the id property on the target model (in the
given example, “id” property on Customer).
name is the name of the relation as defined in the repository. The relation name is used in the repository constructor to define a
BelongsToAccessor and map it to the relation using a inclusion
resolver.
property definition (optional) - creates a property decorator implicitly. The name attribute in the definition can be used to customize datasource column name.

Populate a query in Mongoose with Schema First approach and NestJS

First off I want to say this question is similar to this one which references this one. I have the exact same question as the second link except a notable difference. I'm trying to extend a class generated by NestJS which defines a property.
I'm using NestJs with the Schema first approach found here. I'm also generating a classes file based on my GraphQL Schema.
Here is the Schema:
type Location {
name: String!
owner: User!
}
Which generates the class:
export class Location {
name: string;
owner: User;
}
Now, I want to extend this class so I don't have to repeat the data (there are a lot more fields not shown). I also I want to add fields that live on a document but are not in the schema (_id in this example). Here is my LocationDocument and my schema.
export interface LocationDocument extends Location, Document {
_id: Types.ObjectId
}
export const LocationSchema: Schema = new Schema(
{
name: {
type: String,
required: true,
},
owner: {
type: Types.ObjectId,
ref: 'User',
}
);
Now here is my issue. The generated Location class from the GraphQL schema defines the owner property as a User type. But in reality it's a just a mongodb id until it is populated by Mongoose. So it could be a Types.ObjectId or a User on a UserDocument. So I attempted to define it as:
export interface LocationDocument extends Location, Document {
_id: Types.ObjectId
owner: User | Types.ObjectId;
}
But this throws an error in the compiler that LocationDocument incorrectly extends Location. This makes sense. Is there any way to extend the User Class but say that owner property can be a User Type (once populated by Mongoose) or a mongo object ID (as is stored in the database).
I decided that having a property that can be both types, while easy with Mongoose and JS, isn't the typed way. In my schema I have an owner which is a User type. In my database and the document which extends it, I have an OwnerId. So to people accessing the API, they don't care about the ownerId for the relationship. But in my resolver, I use the Id. One is a Mongo ID type, the other is a User type.