Nestjs GraphQL query not running [duplicate] - graphql

This question already has an answer here:
Problem with multiple graphql resolver implementations in nestjs
(1 answer)
Closed 4 years ago.
I have the following schema:
type User {
id: String!
email: String
}
type Mutation {
createUser(input: CreateUserDto!): CreateUserResponseDto!
}
type Query {
user(id: Int!): User
}
input CreateUserDto {
email: String!
password: String!
}
type CreateUserResponseDto {
id: String!
}
And I have the following resolver:
import {Args, Mutation, Resolver} from '#nestjs/graphql';
import {UserService} from '../service/user.service';
import {CreateUserDto, CreateUserResponseDto, UserDto} from '../controller/dto/user.dto';
import {Query} from '#nestjs/common';
#Resolver('User')
export class UserResolver {
constructor(private readonly userService: UserService) {
}
#Mutation()
async createUser(#Args('input') userDto: CreateUserDto): Promise<CreateUserResponseDto> {
const userEntity = await this.userService.createUser(userDto);
return {
id: userEntity.id,
};
}
#Query('user')
async getUser(#Args('id') id: number): Promise<UserDto> {
const userEntity = await this.userService.findUserById(id);
return {
id: userEntity.id,
email: userEntity.email,
};
}
}
When running the following query:
{
user(id: 2) {
email
}
}
I am getting the following response:
{
"data": {
"user": null
}
}
And when trying to debug it seems like the call never reaches the resolver.
When trying to run the mutation, all works as expected.
What did I miss?

You're using the wrong Query decorator. You've imported it from #nestjs/common instead of #nestjs/graphql.
It's confusing that they both have the same name and if you're using your IDE to automatically fix imports for you it's possible to pull in the wrong one.

Related

GraphQL - How can I tell if my delete mutation has been created?

My react, apollo, prisma, nextjs typescript app seems to think I have not made a deleteIssueGroup mutation. I think I have.
I am using:
"devDependencies": {
"#graphql-codegen/add": "3.2.1",
"#graphql-codegen/cli": "2.13.7",
"#graphql-codegen/typescript": "2.8.0",
"#graphql-codegen/typescript-operations": "2.5.5",
"#graphql-codegen/typescript-react-apollo": "3.3.5",
"#types/cookie": "0.5.1",
"#types/react": "17.0.51",
"#types/react-dom": "17.0.17",
"eslint-config-next": "12.3.1"
It's strange because I defined a deleteGroup mutation at the same time as I made the createGroup mutation and the AllGroups query, but my app thinks I don't have a deleteGroup mutation (it's right, the #generated file doesn't have it in there - except for it being listed in the list of mutations - there are no individual line items in graphql.tsx that define useDeleteIssueMutation in the same way that there are such lines for the create type). I don't know why. Is there a way to force the creation, or the recognition that there is only one of everything?
I can see in my graphql.tsx that I have:
export type MutationDeleteGroupArgs = {
id: Scalars['String'];
};
which I think means I should have the delete mutation, but in the form, when I'm trying to use it, I get an error in the terminal that says it doesn't exist.
When I run my yarn db:migrate script, it goes through the motions of running the prisma migrations (they are all up to date, but it then runs the code gen and concludes successfully).
My code is (I made a new one called IssueGroup to try and see if it was just a random unlucky twist that is causing this mess):
Prisma model
model IssueGroup {
id String #id #default(dbgenerated("gen_random_uuid()")) #db.Uuid
title String
description String
issues Issue[]
createdAt DateTime #default(now()) #db.Timestamptz(6)
updatedAt DateTime #default(now()) #updatedAt #db.Timestamptz(6)
}
model
import * as Prisma from "#prisma/client"
import { Field, ObjectType } from "type-graphql"
import { BaseModel } from "../shared/base.model"
#ObjectType()
export class IssueGroup extends BaseModel implements Prisma.IssueGroup {
#Field()
title: string
#Field()
description: string
#Field(type => String)
issues: Prisma.Issue[];
}
group service:
import { prisma } from "../../lib/prisma"
import { Service } from "typedi"
import { IssueGroupInput } from "./inputs/create.input"
import { Resolver } from "type-graphql"
import { IssueGroup } from "./issueGroup.model"
#Service()
#Resolver(() => IssueGroup)
export class IssueGroupService {
async createIssueGroup(data: IssueGroupInput) {
return await prisma.issueGroup.create({
data,
})
}
async deleteIssueGroup(id: string) {
return await prisma.issueGroup.delete({ where: { id } })
}
async updateIssueGroup(id: string, data: IssueGroupInput) {
const issueGroup = await prisma.issueGroup.findUnique({ where: { id } })
if (!issueGroup) {
throw new Error("Issue not found")
}
return await prisma.issueGroup.update({ where: { id }, data })
}
async getAllIssueGroups() {
return await (await prisma.issueGroup.findMany({orderBy: {title: 'asc'}}))
}
async getIssueGroup(id: string) {
return await prisma.issueGroup.findUnique({
where: {
id,
},
})
}
}
resolver
import { Arg, Mutation, Query, Resolver } from "type-graphql"
import { IssueGroup } from "./issueGroup.model"
import { IssueGroupService } from "./issueGroup.service"
import { IssueGroupInput } from "./inputs/create.input"
import { Inject, Service } from "typedi"
#Service()
#Resolver(() => IssueGroup)
export default class IssueGroupResolver {
#Inject(() => IssueGroupService)
issueGroupService: IssueGroupService
#Query(() => [IssueGroup])
async issueGroups() {
return await this.issueGroupService.getAllIssueGroups()
}
#Query(() => IssueGroup)
async issueGroup(#Arg("id") id: string) {
return await this.issueGroupService.getIssueGroup(id)
}
#Mutation(() => IssueGroup)
async createIssueGroup(#Arg("data") data: IssueGroupInput) {
return await this.issueGroupService.createIssueGroup(data)
}
// : Promise<IssueGroup[]> {
#Mutation(() => IssueGroup)
async updateIssueGroup(
#Arg("id") id: string,
#Arg("data") data: IssueGroupInput
) {
return this.issueGroupService.updateIssueGroup(id, data)
}
#Mutation(() => IssueGroup)
async deleteIssueGroup(#Arg("id") id: string) {
return this.issueGroupService.deleteIssueGroup(id)
}
}
I have seen this post and can see the warning about naming queries uniquely - but I cannot see how I have offended the principle.
I have seen this post and tried following the suggestion to change my codegen.yml from:
documents:
- "src/components/**/*.{ts,tsx}"
- "src/lib/**/*.{ts,tsx}"
- "src/pages/**/*.{ts,tsx}"
to:
documents:
- "src/components/**/!(*.types).{ts,tsx}"
- "src/lib/**/!(*.types).{ts,tsx}"
- "src/pages/**/!(*.types).{ts,tsx}"
but I still get the same issue - my vsCode is suggesting I might want to use create instead of delete.

How can I make GraphQL support int8 type in Supabase?

I'm creating a simple CRUD app to learn GraphQL and am using a Supabase postgres instance. All queries and mutations work fine except for one thing, I can't get the id field from my schemas because they are of type int8 on Supabase, and GraphQL only supports Int.
I'm getting this error when I try to get a row's id using the gql Int type in my type defs: GraphQLError: Int cannot represent non-integer value: 1
I know the solution involves creating a custom scalar type as in this example, but I'm not sure how to implement this type. Also, I cannot change this on Supabase's side, so I must find a way to handle this in gql. How can I handle this type in GraphQL?
TypeDefs:
export const typeDefs = `#graphql
type User {
id: Int!
name: String!
email: String!
age: Int!
verified: Boolean!
}
type Todo {
id: Int!
title: String!
description: String!
}
type Query {
# users queries
getAllUsers: [User]
getUser(email: String!): User
# todo queries
getAllTodos: [Todo]
getTodo(id: String!): Todo
}
type Mutation {
createUser(name: String!, email: String!, age: Int!): User
createTodo(title: String!, description: String!): Todo
}
`;
Resolvers:
import { GraphQLScalarType } from 'graphql';
import { prisma } from '../lib/db.js';
const BigInt = new GraphQLScalarType({
// how do I implement this type?
});
export const resolvers = {
BigInt,
Query: {
getAllUsers() {
return prisma.user.findMany();
},
getUser(parent, args) {
return prisma.user.findUnique({
where: {
email: args.email,
},
});
},
getAllTodos() {
return prisma.todo.findMany();
},
getTodo(parent, args) {
return prisma.todo.findUnique({
where: {
id: args.id,
},
});
},
},
// parent, arge are other arguments that get passes to resolvers automatically
Mutation: {
createUser(parent, args) {
return prisma.user.create({
data: args,
});
},
createTodo(parent, args) {
return prisma.todo.create({
data: args,
});
},
},
};
Solved this by using the graphql-type-ints package. You can just install it and then add the type you need to your schemas and resolvers. However, I don't quite understand why we need to do this. If someone could explain why Supabase uses int8 and that doesn't conform to graphql's Int I would appreciate it.

How to make GraphQL enum data in resolver with nestjs/graphql?

In this way, it can pass enum data in resolver:
enum AuthType {
GOOGLE = 'google-auth',
GITHUB = 'github-auth',
OUTLOOK = 'outlook-auth',
}
interface UsersArgs {
first: number,
from?: string,
status?: String,
authType?: AuthType,
}
export const resolvers = {
AuthType,
Query: {
users: (_record: never, args: UsersArgs, _context: never) {
// args.authType will always be 'google-auth' or 'github-auth' or 'outlook-auth'
// ...
}
}
}
There is also good example for pure GraphQL syntax as:
https://www.graphql-tools.com/docs/scalars#internal-values
In NestJS, the code like
import { Args, Query, Resolver } from '#nestjs/graphql';
import { AuthType } from '#enum/authEnum';
#Resolver()
export class AuthResolver {
constructor(private readonly authRepo: AbstractAuthSettingRepository) {}
#Query(() => AuthSetting)
findAuth(
#Args('input')
id: string,
): Promise<AuthSetting | undefined> {
return this.authRepo.findOne({ id });
}
}
How can I use AuthType in the AuthResolver class?
In order to be able to use enums in NestJS GraphQL, you need to register them once:
import { registerEnumType } from '#nestjs/graphql';
import { AuthType } from '#enum/authEnum';
registerEnumType(AuthType, { name: 'AuthType' });

Apollo Graphql: Rename schema for backward compatibility

What I want do ?
In Apollo Graphl server, I want to change an entity Person to Human in schema but i don't want to break my clients (frontend that are querying graphql). So if client is making query for Person i want to map it to Human.
Example:
CLIENT QUERY
query {
Person {
ID
firstName
}
}
REWRITE TO
query {
Human {
ID
name
}
}
REWRITE THE RESPONSE
{
data: {
Person: {
Id: 123,
name:"abc"
}
}
}
Things that I have tried
graphql-rewriter provides something similar to what i am looking for. I went through it documentation but it doesn't have the option to rewrite the field name.
In apollo graphql documentation Apollow graphql directives, They have mentioned about rename directive but i did not find rename-directive-package the node module.
apollo-directives-package I have tried this as well but it doesn't have the option to rename the scaler field e.g
import { makeExecutableSchema } from "graphql-tools";
import { RenameDirective } from "rename-directive-package";
const typeDefs = `
type Person #rename(to: "Human") {
name: String!
currentDateMinusDateOfBirth: Int #rename(to: "age")
}`;
const schema = makeExecutableSchema({
typeDefs,
schemaDirectives: {
rename: RenameDirective
}
});
Any suggestions/help would be appreciated.
Here i hope this gives helps you, first we have to create the schema-directive
import { SchemaDirectiveVisitor } from "graphql-tools";
import { GraphQLObjectType, defaultFieldResolver } from "graphql";
/**
*
*/
export class RenameSchemaDirective extends SchemaDirectiveVisitor {
/**
*
* #param {GraphQLObjectType} obj
*/
visitObject(obj) {
const { resolve = defaultFieldResolver } = obj;
obj.name = this.args.to;
console.log(obj);
}
}
type-defs.js
directive #rename(to: String!) on OBJ
type AuthorizedUser #rename(to: "Human1") {
id: ID!
token: ID!
fullName: String!
roles: [Role!]!
}

Nested query and mutation in type-Graphql

I found a feature in graphql to write nested query and mutation, I tried it but got null. I found the best practices of building graphqL schema on Meetup HolyJs and the speaker told that one of the best ways is building "Namespaced" mutations/queries nested, in this way you can write some middlewares inside the "Namespaced" mutations/queries and for get the Child mutation you should return an empty array because if you return an empty array, Graphql understand it and go one level deep.
Please check the example code.
Example in graphql-tools
const typeDefs = gql`
type Query { ...}
type Post { ... }
type Mutation {
likePost(id: Int!): LikePostPayload
}
type LikePostPayload {
recordId: Int
record: Post
# ✨✨✨ magic – add 'query' field with 'Query' root-type
query: Query!
}
`;
const resolvers = {
Mutation: {
likePost: async (_, { id }, context) => {
const post = await context.DB.Post.find(id);
post.like();
return {
record: post,
recordId: post.id,
query: {}, // ✨✨✨ magic - just return empty Object
};
},
}
};
This is my Code
types
import { ObjectType, Field } from "type-graphql";
import { MeTypes } from "../User/Me/Me.types";
#ObjectType()
export class MeNameSpaceTypes {
#Field()
hello: string;
#Field({ nullable: true })
meCheck: MeTypes;
}
import { Resolver, Query } from "type-graphql";
import { MeNameSpaceTypes } from "./MeNamespace.types";
#Resolver()
export class MeResolver {
#Query(() => MeNameSpaceTypes)
async Me() {
const response = {
hello: "world",
meCheck:{}
};
return response;
}
}
Result of code
query {
Me{
hello
meCheck{
meHello
}
}
}
--RESULT--
{
"data": {
"Me": {
"hello": "world",
"meCheck": {
"meHello": null
}
}
}
}
I got a null instead a meHello resolver. Where am I wrong?
Namespaced mutations are against GraphQL spec as they are not guarranted to run sequentially - more info in this discussion in GitHub issue related to your problem:
https://github.com/MichalLytek/type-graphql/issues/64

Resources