Schema must contain uniquely named types named "Project" - graphql

I am creating a Apollo Graphql backend using type-orm. I create an entity called Project:
import { Field, ObjectType } from "type-graphql";
import { BaseEntity, Column, Entity, ObjectID, ObjectIdColumn } from "typeorm";
#ObjectType()
#Entity()
export class Project extends BaseEntity {
#Field(() => String)
#ObjectIdColumn()
id: ObjectID;
#Field()
#Column({ unique: true })
name!: string;
#Field()
#Column()
startDate!: Date;
#Field()
#Column({nullable: true})
endDate!: Date
#Field()
#Column({unique:true})
githubUrl: string;
}
and the resolver project:
import { Arg, Mutation, Query, Resolver } from 'type-graphql'
import {Project} from '../entities/project'
import {ProjectInput, ProjectResponse} from '../types/ProjectTypes'
#Resolver()
export class ProjectResolver {
#Query(() => [Project])
async getProjects(): Promise<Project[] | null> {
let projects = await Project.getRepository().find();
return projects;
}
#Mutation(() => ProjectResponse)
async createProject(
#Arg("input") input: ProjectInput
): Promise<ProjectResponse>{
let project : Project;
if(input.name == ""){
throw Error("Invalid input")
}
try{
project = await Project.create({
name: input.name,
startDate: input.startDate,
}).save();
}catch (error) {
if (error.code === 11000) {
return {
errors: [
{
field: "project",
message: "The project name is already in use",
},
],
};
} else return error;
}
return {project: project};
}
#Mutation(() => ProjectResponse)
async setProjectEndDate(
#Arg("projectId") projectId: string,
#Arg("endDate") endDate: Date
): Promise<ProjectResponse>{
let project = await Project.getRepository().findOne(projectId)
if(project){
if(project?.startDate > endDate){
return {
errors:[{
field:"EndDate",
message:"The end date must be a date after the start date of a project."
}]
}
}
project.endDate = endDate;
project.save();
}
return {
errors:[{
field:"Project",
message:"Project could not be found."
}]
}
}
}
this is the code of the 2 auxiliary classes for the input and response of the resolver:
#InputType()
export class ProjectInput{
#Field()
name: string
#Field()
startDate: Date
#Field(()=> Date,{nullable:true})
endDate?: Date | null
#Field(()=> String, {nullable:true})
githubUrl?: string
}
#ObjectType()
export class ProjectResponse{
#Field(() => [FieldError], { nullable: true })
errors?: FieldError[]
#Field(() => Project, { nullable: true })
project?: Project | null
}
this is the code I use to create the ApolloServer object:
const apolloServer = new ApolloServer({
introspection: true,
playground: true,
schema: await buildSchema({
resolvers: [ProjectResolver],
validate: false, // Disable default GraphQL errors
}),
context: ({ req, res }) => ({ req, res}), // Enables use of context (with request) in resolvers
})
And the error I get is the following:
Error: Schema must contain uniquely named types but contains multiple types named "Project".
at new GraphQLSchema (C:\Users\User\Desktop\UPV\Proyectos\Cv web\myweb-backend\node_modules\graphql\type\schema.js:194:15)
at Function.generateFromMetadataSync (C:\Users\User\Desktop\UPV\Proyectos\Cv web\myweb-backend\node_modules\type-graphql\dist\schema\schema-generator.js:31:32)
at Function.generateFromMetadata (C:\Users\User\Desktop\UPV\Proyectos\Cv web\myweb-backend\node_modules\type-graphql\dist\schema\schema-generator.js:16:29)
at Object.buildSchema (C:\Users\User\Desktop\UPV\Proyectos\Cv web\myweb-backend\node_modules\type-graphql\dist\utils\buildSchema.js:10:61)
at C:\Users\User\Desktop\UPV\Proyectos\Cv web\myweb-backend\dist\index.js:42:38
at Generator.next ()
at fulfilled (C:\Users\User\Desktop\UPV\Proyectos\Cv web\myweb-backend\dist\index.js:5:58)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
I don't know what the problem is so I would appreciate your help

I have managed to solve the problem by changing the import of the class 'Project' made in the 'ProjectResolver' class.
Instead of:
import {Project} from '../entities/project'
Now looks like this:
import {Project} from '../../src/entities/project'

Related

TypeGraphQL: ArgumentValidationError on mutation

I'm playing around with TypeGraphQL and have a simple api setup to query projects and their associated clients.
For my createClient mutation, I'm receiving an ArgumentValidation Error, 'an unknown value was passed to the validate function' and can't pinpoint whats going wrong.
Client Schema
import { ObjectType, Field, ID } from "type-graphql";
#ObjectType()
class ClientSchema {
#Field(type => ID)
id: string
#Field()
name: string
#Field()
email: string
#Field()
phone: string
}
export default ClientSchema
Client Resolver
import { Arg, Field, InputType, Mutation, Query, Resolver } from "type-graphql";
import exampleClients from "../utils/clients";
import ClientSchema from "../schemas/ClientsSchema";
#InputType()
class CreateClassInput {
#Field()
name: string
#Field()
email: string
#Field()
phone: string
}
#Resolver()
class ClientResolver {
#Query((returns) => [ClientSchema])
async clients() {
return exampleClients;
}
#Query((returns) => ClientSchema)
async client(#Arg("id") id: string) {
const client = exampleClients.find((client) => client.id === id);
if (!client) throw new Error("No such client");
return client;
}
#Mutation((returns) => ClientSchema)
async createClient(#Arg("data") createClassData: CreateClassInput) {
const { name, email, phone } = createClassData;
const newClient: ClientSchema = {
id: String(exampleClients.length + 1),
name,
email,
phone
};
exampleClients.push(newClient);
return newClient;
}
}
export default ClientResolver;
GraphQL Query
mutation createClient {
createClient(data: { name: "Test Name", email: "Test Email", phone: "Test phone" }) {
name
}
}
If i pass the arguments to the mutation directly, without de-structuring an object, I don't get the error however with it I do. Is there something I'm doing wrong?

Prisma / Graphql resolver

Good morning all,
I m currently back in the famous world of web development and I have in mind to develop a tool by using Nest/Prisma/Graphl.
However, I'm struggling a little bit on key element like the following one.
Basically, I can see that, by using the "include" function in Prisma (module.service.ts), I'm getting subModules list: this is the expected behavior.
However, on Graphl side, to cover field resolver (module.resolver.ts), I can see that the same request is executing again to cover SubModules field.....
What am I missing?????
See below the code:
module.module.ts
import { Field, ID, ObjectType } from '#nestjs/graphql'
import { SubModule } from './submodule.model'
#ObjectType()
export class Module {
// eslint-disable-next-line #typescript-eslint/no-unused-vars
#Field((type) => ID)
id: number
name: string
description: string
icon: string
active: boolean
position: number
subModules?: SubModule[]
}
submodule.model.ts
import { Field, ObjectType, ID } from '#nestjs/graphql';
import { Module } from './module.model';
#ObjectType()
export class SubModule {
#Field((type) => ID)
id: number;
name: string;
description: string;
icon: string;
active: boolean;
position: number;
module: Module;
}
module.resolver.ts
import {
Resolver,
Query,
ResolveField,
Parent,
Args,
InputType,
} from '#nestjs/graphql'
import { Module } from 'src/models/module.model'
import { ModuleService } from './module.service'
import { SubModuleService } from './sub-module.service'
#InputType()
class FilterModules {
name?: string
description?: string
icon?: string
active?: boolean
}
// eslint-disable-next-line #typescript-eslint/no-unused-vars
#Resolver((of) => Module)
export class ModuleResolver {
constructor(
private moduleService: ModuleService,
private subModuleService: SubModuleService,
) {}
// eslint-disable-next-line #typescript-eslint/no-unused-vars
#Query((returns) => Module)
async module(#Args('ModuleId') id: number) {
return this.moduleService.module(id)
}
// eslint-disable-next-line #typescript-eslint/no-unused-vars
#Query((returns) => [Module], { nullable: true })
async modules(
#Args({ name: 'skip', defaultValue: 0, nullable: true }) skip: number,
#Args({ name: 'filterModules', defaultValue: '', nullable: true })
filterModules: FilterModules,
) {
return this.moduleService.modules({
skip,
where: {
name: {
contains: filterModules.name,
},
},
})
}
#ResolveField()
async subModules(#Parent() module: Module) {
const { id } = module
return this.subModuleService.subModules({ where: { moduleId: id } })
}
}
module.service.ts
import { Injectable } from '#nestjs/common'
import { PrismaService } from 'src/prisma.service'
import { Prisma, Module } from '#prisma/client'
#Injectable()
export class ModuleService {
constructor(private prisma: PrismaService) {
prisma.$on<any>('query', (event: Prisma.QueryEvent) => {
console.log('Query: ' + event.query)
console.log('Params' + event.params)
console.log('Duration: ' + event.duration + 'ms')
})
}
async module(id: number): Promise<Module | null> {
return this.prisma.module.findUnique({
where: {
id: id || undefined,
},
})
}
async modules(params: {
skip?: number
take?: number
cursor?: Prisma.ModuleWhereUniqueInput
where?: Prisma.ModuleWhereInput
orderBy?: Prisma.ModuleOrderByWithRelationInput
}): Promise<Module[]> {
const { skip, take, cursor, where, orderBy } = params
return this.prisma.module.findMany({
skip,
take,
cursor,
where,
orderBy,
})
}
async updateModule(params: {
where: Prisma.ModuleWhereUniqueInput
data: Prisma.ModuleUpdateInput
}): Promise<Module> {
const { where, data } = params
return this.prisma.module.update({
data,
where,
})
}
}
Thanks in advance for your help

Resolve one to many relation Nest.js / GraphQL Federation with Mercurius

I am using Nest.js / GraphQL Federation with Mercurius. I have two entities Products and Store that reside in different apps. The store should have multiple products as one to many relationship. I have a StoresQueries resolver that has a method findAll that returns a StoresPagination object. Each returned store object does not have the fields mentioned in the products app from the apps/products/src/products/resolvers/Store.resolver.ts and same apps/products/src/products/models/Store.model.ts I want to keep stuff decoupled and more or less the same structure works fine on other entities.
Project structure
apps/
store/
src/
store/
models/
Store.model.ts
resolvers/
Store.resolver.ts
StoresQueries.resolver.ts
services/
Store.service.ts
Store.module.ts
App.module.ts
main.ts
products/
src/
store/
models/
Store.model.ts
Product.model.ts
resolvers/
Store.resolver.ts
Product.resolver.ts
services/
Product.service.ts
Product.module.ts
App.module.ts
main.ts
apps/store/src/models/Store.model.ts
#ObjectType()
#Directive('#key(fields: "id")')
export class Store {
#Field(() => ID)
id: string;
#Field()
#TextSearch()
name: string;
}
apps/store/src/resolvers/Store.resolver.ts
#Resolver(() => Store)
export class StoreResolver {
constructor(private readonly storeService: StoreService) {}
#Query(() => StoresQueries, { name: 'stores' })
async storesQueries(): Promise<StoresQueries> {
return {};
}
#ResolveReference()
async resolveReference(reference: {
__typename: string;
id: string;
}): Promise<Store> {
return this.storeService.findById(reference.id);
}
}
apps/store/src/resolvers/StoresQueries.resolver.ts
#Resolver(() => StoresQueries)
export class StoresQueriesResolver {
constructor(private storeService: StoreService) {}
#ResolveField(() => StorePagination)
async findAll(
#CurrentUser() user: JwtUser,
#Args('input', { nullable: true }) input: PaginationInput | null,
): Promise<StorePagination> {
return this.storeService.findAll(input?.cursor, input?.limit);
}
}
app/products/src/products/models/Store.model.ts
#ObjectType()
#Directive('#key(fields: "id")')
#Directive('#extends')
export class Store {
#Directive('#external')
#Field(() => ID)
id: string;
// these both fields are not shown when querying StoresQueries for each store
#Field(() => ProductsPagination, { nullable: true })
products?: ProductsPagination;
#Field(() => Number, { nullable: true })
dummyCount?: number;
}
app/products/src/products/models/Product.model.ts
#ObjectType()
#Directive('#key(fields: "id")')
#Label('product', ['id'])
export class Product {
#Field(() => ID)
id: string;
}
app/products/src/products/resolvers/Product.resolver.ts
#Resolver(() => Product)
export class ProductsResolver {
constructor(
private productsService: ProductsService,
) {}
#ResolveReference()
async resolveReference(reference: {
__typename: string;
id: string;
}): Promise<Product> {
return this.productsService.findById(reference.id);
}
}
app/products/src/products/resolvers/Store.resolver.ts
#Resolver(() => Store)
export class StoreResolver {
constructor(
private productsService: ProductsService,
) {}
#ResolveField(() => ProductsPagination, { nullable: true })
async products(
#Parent() parent: Store,
#Args('input', { nullable: true }) input: PaginationInput | null,
): Promise<ProductsPagination> {
const wp = this.productsService.getProductsByStoreId(
parent.id,
kind,
input,
);
return { __typename: 'ProductsPagination', ...wp };
}
}

Cannot return null for non-nullable field - Typegoose and TypeGraphQL

I have this Album model set up using Typegoose and TypeGraphQL, which contains multiple songs from the Song model using the AlbumSong ObjectType:
import {
prop as Property,
getModelForClass,
modelOptions,
Ref,
} from "#typegoose/typegoose";
import { Field, ObjectType, ID } from "type-graphql";
import { AlbumCategory, } from "./albumCategory.model";
import { Song } from "./song.model";
#ObjectType()
export class AlbumSong {
#Field(() => ID)
#Property({ required: true })
id!: string;
#Field(() => Song)
#Property({ type: () => Song, ref: () => Song, required: true })
song!: Song;
}
#ObjectType({ description: "The Album Model" })
#modelOptions({ schemaOptions: { collection: "albums", timestamps: true } })
export class Album {
#Field(() => ID)
id: string;
#Field()
#Property({ type: () => String })
title: string;
#Field(() => [AlbumSong])
#Property({ type: AlbumSong })
albumSongs!: Partial<AlbumSong>[];
#Field()
#Property({ required: true, default: Date.now })
createdAt: Date;
#Field()
#Property({ required: true, default: Date.now })
updatedAt: Date;
}
export const AlbumModel = getModelForClass(Album);
When trying to query the album using:
#Query((_returns) => Album, { nullable: false, name: "album" })
async getAlbumById(#Arg("id") id: string) {
return await AlbumModel.findById({ _id: id });
}
With the following GraphQL:
query Album($albumId: String!) {
album(id: $albumId) {
id
albumSongs {
id
song {
id
}
}
}
}
I get: "Cannot return null for non-nullable field AlbumSong.song."
To me it seems like the reference is not working, when i only query the albumSong's id it returns just fine...
Setup a FieldResolver to resolve the song within an AlbumSong
#Resolver(() => AlbumSong)
export class AlbumSongFieldResolver {
#FieldResolver(() => Song)
async song(#Root() parent: AlbumSong): Promise<Song> {
return Song.findOne(parent.song);
}
}

how I can add a custom scalar with different formats in type-graphql to generate GraphQL schema

I tried to generate a GraphQl schema using type-graphql lib I want the field value to accept any data type but it only takes a string. I have created a custom scalar with the name SettingValueScalar but it's not working as well
import { ObjectID } from 'mongodb'
import 'reflect-metadata'
import { Field, InputType, Int, ObjectType } from 'type-graphql'
import { DateScalar } from '../scalars/date.scalar'
import { SettingValueScalar } from '../scalars/setting-value.scalar'
#ObjectType()
#InputType('SettingInput')
export class Setting implements MSetting {
readonly _id!: ObjectID
#Field(() => Int)
id!: number
#Field({ nullable: true })
slug?: string
#Field({ nullable: true })
domain?: string
#Field(()=>SettingValueScalar,{ nullable: true })
value?: string| number | string[] | number[]
#Field(() => DateScalar, { nullable: true })
createdAt?: Date
#Field(() => DateScalar, { nullable: true })
updatedAt?: Date
}
SettingValueScalar
import { GraphQLScalarType, Kind } from 'graphql'
export const SettingValueScalar = new GraphQLScalarType({
name: 'SettingValue',
description: 'Setting value scalar type',
serialize(value: any): any {
return value
},
parseValue(value: any): any {
return value
},
parseLiteral(ast) {
return last
},
})

Resources