Error retrieving data from DB using typeorm and type-graphql - postgresql

I'm using type-graphql in conjunction with typeorm, apollo-server-express and postgreSQL. I have a User and a Customer entity in a 1:n relationship, meaning one user can have multiple customers.
I can create users and customers just fine, but when attempting to retrieve the user associated to a customer using Apollo Server playground, I get an error message stating "Cannot return null for non-nullable field Customer.user."
When I check the database, the associated user id on the customer table is definitely not null (see attached image).
query {
customers {
customerId
customerName
user {
userId
}
}
}
Does anyone know what I'm doing wrong?
User.ts
import { Field, ID, ObjectType } from "type-graphql";
import { BaseEntity, Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Customer } from "./Customer";
#ObjectType()
#Entity("users")
export class User extends BaseEntity {
#Field(() => ID)
#PrimaryGeneratedColumn("uuid")
userId: string;
#Field()
#Column({ unique: true })
email: string;
#Column({ nullable: false })
password: string;
#Field(() => Customer)
#OneToMany(() => Customer, customer => customer.user)
customers: Customer[]
}
Customer.ts
import { Field, ID, ObjectType } from "type-graphql";
import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { User } from "./User";
#ObjectType()
#Entity("customers")
export class Customer extends BaseEntity {
#Field(() => ID)
#PrimaryGeneratedColumn("uuid")
customerId: string;
#Field()
#Column()
customerName: string;
#Field(() => User)
#ManyToOne(() => User, user => user.customers)
user: User;
}
CustomerResolver.ts
export class CustomerResolver {
#Query(() => [Customer])
async customers():Promise<Customer[]> {
try {
return await Customer.find();
} catch (error) {
console.log(error);
return error;
}
}
....
Setup / Version
Node: v14.17.0
"apollo-server-express": "^2.24.0",
"type-graphql": "^1.1.1",
"typeorm": "0.2.32"
postgreSQL: 13.2

In your resolver change the find operation like below:
return Customer.find({
relations: ["user"]
});

You should write a #FieldResolver which will fetch customers based on root user data.
https://typegraphql.com/docs/resolvers.html#field-resolvers

Related

How to find user by location with posgreSQL and Graphql?

I dont' know where to start, I tried follow a answer by import Geometry from Geojson.
User.ts file
import { Geometry } from "geojson";
import { Field, ObjectType } from "type-graphql";
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
PrimaryGeneratedColumn,
} from "typeorm";
#ObjectType()
#Entity()
export class User extends BaseEntity {
#Field()
#PrimaryGeneratedColumn()
id!: number;
#Column({ unique: true })
userId!: string;
#Field(type => Geometry)
#Column({ nullable: true })
location: Geometry;
#Field()
#CreateDateColumn()
createdAt: Date;
}
but I got a error with PosgreSQL
DataTypeNotSupportedError: Data type "Object" in "User.location" is not supported by "postgres" database.
and a error with Graphql
'Geometry' only refers to a type, but is being used as a value here.

Find rows using foreign key in TypeORM

I have an OneToMany relation from User to Message.
When I insert register a user with a message, it adds the user to the User table, and the message to the Message table with a userId pointing to the user's id.
This is done automatically using the following setup.
User entity:
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column()
email: string;
#JoinTable()
#OneToMany((type) => Message, (message) => message.user, {
cascade: true,
})
messages: Message[];
}
Message entity:
#Entity()
export class Message {
#PrimaryGeneratedColumn()
id: number;
#Column()
text: string;
#ManyToOne((type) => User, (user) => user.messages, { eager: true })
user: User[];
}
If I want to find all messages from a user via userId with:
const existing_msgs = await this.messageRepository.find({
where: { userId: user.id },
});
It tells me that it cannot find the userId column, which is understandable as I did not specifically include userId to the Message entity.
But how would we query it in this case?
As mentionned in This post, you have to add the decorator #JoinColumn after your manytoone. This will join the column and you will be able to perform the query you want with :
const existing_msgs = await this.messageRepository.find({
where: { user: { id: user.id }},
});
Note that this will work only on primary column like id.
And your message table will be :
#Entity()
export class Message {
#PrimaryGeneratedColumn()
id: number;
#Column()
text: string;
#ManyToOne((type) => User, (user) => user.messages, { eager: true })
#JoinColumn() // <-- Add this
user: User[];
}
I was able to do it with the following querybuilder.
const msg_arr = await this.userRepository
.createQueryBuilder('user')
.leftJoinAndSelect('user.messages', 'messages')
.where('user.id = :userId', { userId: user.id })
.andWhere('messages.text LIKE :text', { text: message })
.select('messages.text')
.execute();

Can not Query all users because of MongoDB id

I am coding a CRUD API built in TypeScript and TypeGoose.
I get an error saying,
CannotDetermineGraphQLTypeError: Cannot determine GraphQL output type for '_id' of 'User' class. Is the value, that is used as its TS type or explicit type, decorated with a proper decorator or is it a proper output value?
I have a User entity.
import { Field, ObjectType } from 'type-graphql';
import { ObjectId } from 'mongodb';
import { prop as Property, getModelForClass } from '#typegoose/typegoose';
#ObjectType()
export class User {
#Field()
readonly _id: ObjectId;
#Field()
#Property({ required: true })
email: string;
#Field({ nullable: true })
#Property()
nickname?: string;
#Property({ required: true })
password: string;
constructor(email: string, password: string) {
this.email = email;
this.password = password;
}
}
export const UserModel = getModelForClass(User);
And this is how my query resolver looks like.
#Query(() => [User])
async users() {
const users = await UserModel.find();
console.log(users);
return users;
}
How can I solve this? It seems to be like TypeGraphQL doesn't understand what the MongoDB ID is?
Im not sure about this, but maybe ObjectId.toString() help you.
MongoDB doc about ObjectId.toString()

Insert into table via relation id's

Below I have the Equipt entity it has three columns createdById, tribeId, userId.
I am trying to save a new row using the id's of each entity, and not the entities themselves:
This doesn't work:
let e = connection.getRepository(Equipt);
const check = await e.save({
userId: 1,
tribeId: 1,
createdById: 1,
})
This works:
let e = connection.getRepository(Equipt);
const check = await e.save({
user: user,
tribe: tribe,
createdBy: adminUser,
})
Entity:
import {ManyToOne, RelationId, JoinColumn, Entity} from "typeorm";
import {User} from './User';
import { Base } from "../base";
import { Tribe } from "./Tribe";
#Entity('Equipts')
export class Equipt extends Base {
#ManyToOne(type => User, { nullable: false })
#JoinColumn()
createdBy: User;
#RelationId((Equipt: Equipt) => Equipt.createdBy)
createdById: number;
#ManyToOne(type => Tribe, { nullable: false })
#JoinColumn()
Tribe: Tribe;
#RelationId((Equipt: Equipt) => Equipt.Tribe)
TribeId: number;
#ManyToOne(type => User, { nullable: false })
#JoinColumn()
user: User;
#RelationId((Equipt: Equipt) => Equipt.user)
userId: number;
}
Is there any way to insert using id's without having the pass the entire entity?

TypeORM: [364] ERROR: missing FROM-clause entry for table "userorganisation" at character 401

I just hit a pretty interesting problem when using TypeORM and joining tables that I've set up for a Postgres database. I figured it out but thought I'd post the info here for anyone else that had a similar problem.
I have 3 tables set up on my database: user, organisation, user_organisation.
The idea for this is that a user can belong to many organisations and the table called user_organisation maps users to these organisations. So my entities look like this,
user.entity.ts
import { TimestampedEntity } from '#shared/entities/timestamped.entity';
import { Organisation } from '#user/entities/organisation.entity';
import { UserOrganisation } from '#user/entities/user-organisation.entity';
import { Column, Entity, Index, JoinTable, ManyToMany, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
#Entity()
#Index(['email', 'password'])
export class User extends TimestampedEntity {
#PrimaryGeneratedColumn()
userId: number;
#Column({
length: 64
})
username: string;
#Column({
length: 500
})
email: string;
#Column({
length: 255
})
password: string;
#Column({
type: 'json',
})
details: any;
#Column({
nullable: true
})
refreshToken: string;
#OneToMany(type => UserOrganisation, userOrganisation => userOrganisation.user)
#JoinTable()
userOrganisations: UserOrganisation[];
}
user-organisation.entity.ts
import { Organisation } from '#user/entities/organisation.entity';
import { User } from '#user/entities/user.entity';
import { Column, Entity, JoinColumn, ManyToOne, OneToOne, PrimaryColumn, PrimaryGeneratedColumn } from 'typeorm';
#Entity()
export class UserOrganisation {
#ManyToOne(type => User, user => user.userOrganisations, { primary: true })
user: User;
#ManyToOne(type => Organisation, organisation => organisation.userOrganisations, { primary: true })
organisation: Organisation;
}
organisation.entity.ts
import { TimestampedEntity } from '#shared/entities/timestamped.entity';
import { UserOrganisation } from '#user/entities/user-organisation.entity';
import { User } from '#user/entities/user.entity';
import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany, OneToOne, PrimaryGeneratedColumn } from 'typeorm';
#Entity()
export class Organisation extends TimestampedEntity {
#PrimaryGeneratedColumn()
orgId: number;
#Column({
length: 255
})
name: string;
#Column({
type: 'json'
})
details: any;
#OneToMany(type => UserOrganisation, userOrganisation => userOrganisation.organisation)
userOrganisations: UserOrganisation[];
}
I was then trying to run the following query,
this.userRepository.createQueryBuilder('user')
.where('user.email = :email', { email })
.innerJoin(UserOrganisation, 'userOrganisation', 'user.userId = userOrganisation.userUserId')
.getOne();
And this is the error message I got,
ERROR: missing FROM-clause entry for table "userorganisation" at character 401
The final query printed out like this,
SELECT "user"."createdAt" AS "user_createdAt"
, "user"."updatedAt" AS "user_updatedAt"
, "user"."userId" AS "user_userId"
, "user"."username" AS "user_username"
, "user"."email" AS "user_email"
, "user"."password" AS "user_password"
, "user"."details" AS "user_details"
, "user"."refreshToken" AS "user_refreshToken"
FROM "user" "user"
INNER JOIN "user_organisation" "userOrganisation"
ON "user"."userId" = userOrganisation.userUserId
WHERE "user"."email" = $1
The way I fixed it is described below.
What I noticed in the query,
SELECT "user"."createdAt" AS "user_createdAt", "user"."updatedAt" AS "user_updatedAt", "user"."userId" AS "user_userId", "user"."username" AS "user_username", "user"."email" AS "user_email", "user"."password" AS "user_password", "user"."details" AS "user_details", "user"."refreshToken" AS "user_refreshToken" FROM "user" "user" INNER JOIN "user_organisation" "userOrganisation" ON "user"."userId" = userOrganisation.userUserId WHERE "user"."email" = $1
Was that there was a difference between the user table and the userOrganisation table in the join criteria,
"user"."userId" = userOrganisation.userUserId
The user table was automatically wrapped in quotation marks but userOrganisation was not... So I changed my code to the following,
this.userRepository.createQueryBuilder('user')
.where('user.email = :email', { email })
.innerJoin(UserOrganisation, 'userOrganisation', '"user"."userId" = "userOrganisation"."userUserId"')
.getOne();
If you look above, I've added the quotation marks in the join criteria. It's all working now :)
Hope this helps!
Well done. However when using joins in typeorm you have to write the conditions as they are defined then setting objects.
this.userRepository.createQueryBuilder('user')
.where('user.email = :email', { email })
.innerJoin(UserOrganisation, 'userOrganisation', 'user.userId=userOrganisation.user.userId')
.getOne();
With this you do not need to add quotation marks.
The generated sql is not showing the quotes because it does not know how to populate the conditions.
I hope this helps.