resolve Field graphql apollo nestjs in ApolloGatewayDriver not work - graphql

I am developing an application that uses ApolloGatewayDriver.
I have two services. A user service that runs on port 3001.
Another service is the image that runs on port 3002
Both services are added in another service called api-gateway. and are available in the following path
http://localhost:3000/graphql
Notice the structure below
UserOutput.ts
#ObjectType()
export class UserOutput {
#Field()
id: number;
#Field()
firstName: string;
#Field({ nullable: true })
lastName?: string;
#Field({ nullable: true })
photo?: PhotoOutput;
}
PhotoOutput.ts
#ObjectType()
export class PhotoOutput {
#Field()
id: number;
#Field()
url: string;
}
serviceOne.ts
#Query(() => UserOutput)
async user(#Args('id', { type: () => Int }) id: number) {
return await this.profileService.user(id);
}
serviceTwo.ts
#Resolver(() => PhotoOutput)
export class PhotoResolver {
#ResolveField('url')
url(#Parent() user: UserOutput) {
return url
}
#ResolveField()
id(#Parent() user: UserOutput) {
return id;
}
}
app
-- api-gateway port 3000 -> graphql
-- service-one port 3001
-- service-two port 3002
request :
{
user(id:1468){
video{
id
lastName
firstName
photo{
id
url
}
}
}
}
error :
"Error: Cannot return null for non-nullable field VideoOutput.url.",

Related

Typegoose and TypeGraphQL - #FieldResolver not working

I have try to get the user by the userid which stored in the notice, but I get undefined in the FieldResolver.
Notice Schema
#ObjectType({description: "Notice Schema"})
export class Notice extends Base{
#NField() #prop() userId: string;
#NField((type: any) => User) #prop({ref: () => User,require: true}) user: Ref<User>;
#NField() #prop() title: string;
#NField() #prop() type: string;
#NField() #prop() lostDate: Date;
#NField({nullable: true}) #prop({default: null}) foundDate: Date;
#NField({nullable: true}) #prop({default: null}) found_user_id: string;
#NField() #prop() description: string;
#NField() #prop() venue: string;
#NField() #prop() contact: string;
#NField({nullable: true}) #prop() imageDir: string;
}
Notice Resolver
#Resolver((of) => Notice)
export class NoticeResolver{
constructor() {}
#FieldResolver()
async user(#Root() notice: Notice){
console.log(notice.userId)
return await UserModel.findById(notice.userId)
}
#Mutation(() => Notice)
async createNotice(
#Arg("noticeInfo") noticeInfo: NoticeInput
) {
let notice = new NoticeModel()
for (let key in noticeInfo){
notice[key] = noticeInfo[key]
}
return (await NoticeModel.create(notice)).save()
}
#Query(() => [Notice])
async findNoticeByUserId(
#Arg("user_id") user_id: string
) {
let result:any = await NoticeModel.find({userId: user_id})
return result
}
#Query(() => [Notice])
async findNoticeById(
#Arg("notice_id") notice_id: string
) {
return NoticeModel.aggregate([
{
$match: { _id: notice_id },
},
])
}
#Query(() => [Notice])
async allNotice() {
return await NoticeModel.find({})
}
}
And the query
query{
allNotice{
_id
userId
title
type
lostDate
foundDate
found_user_id
description
venue
contact
imageDir
user{
name
phone
email
country
}
}
}
When a run the query the console.log in the Field Resolver shown "undefined"
I do not really know what happen, I am also follow the docmentation in the type-graphql
and the version is ^1.1.1

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?

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);
}
}

Schema must contain uniquely named types named "Project"

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'

Resources