Apollo + GraphQL: Fragment on interface works for one concrete type but not on the other - graphql

I have an interface CommonUser with PublicUser and User that implements CommonUser.
I have two endpoints: one for PublicUser and one for User:
interface CommonUser {
id
...
}
type User implements CommonUser {
id
...
}
type PublicUser implements CommonUser {
id
...
}
extend type Query {
publicUser(id: ID!): PublicUser
}
extend type Mutation {
fetchCreateUser(id: ID!): User!
}
On the front end, I want to use a fragment to share common fields,
const COMMON_FIELDS = gql`
fragment CommonUserFields on CommonUser {
id
...
}
`
const FETCH_CREATE_USER = gql`
${COMMON_FIELDS}
mutation fetchCreateUser {
fetchCreateUser(id: 123) {
...CommonUserFields
...
}
}
`
const PUBLIC_USER = gql`
${COMMON_FIELDS}
query publicUser {
publicUser(id: 123) {
...CommonUserFields
someOtherField
}
}
`
When I use the FETCH_CREATE_USER mutation, it works as intended, returning all the common user fields.
However, when I use the PUBLIC_USER query, it only returns someOtherField in the response.
I have verified on the backend resolver that graphQLFields(info) does contain all the requested fields, and the object being returned contains all requested fields.

Related

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.

Handle types with identical properties in GraphQL

I've joined a codebase that used GraphQL and is in dire need of DRYing up. Example code would look like this:
/plugins/abc/schemas/user.js
const { gql } = require('apollo-server-express')
module.export.defTypes = gql`
type User {
name: String
id: String
location: String
}
`
/plugins/def/schemas/user.js
const { gql } = require('apollo-server-express')
module.export.defTypes = gql`
type User {
name: String
id: String
location: String
}
`
I don't know GraphQL terribly well, but given that this was just exporting string content, I assumed I could do this:
/plugins/def/schemas/user.js
const { gql } = require('apollo-server-express');
const { user } = require('../shared/schemas');
module.export.defTypes = gql`
type User {
${user}
}
But graphQL throws up an error:
(node:78551) UnhandledPromiseRejectionWarning: MissingSchemaError: Schema hasn't been registered for model "<name of unrelated model>".
Is there a correct, GraphQL-y way to do this?

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!]!
}

Write resolvers for nested type definitions

Suppose I have following type definition for my GraphQL API:
const typeDef = `
type Book {
title: String
author: Author
likes: Int
}
type Author {
id: String
name: String
age: Int
books: [Book]
}
type Query{
books(authorid: String!): Book
}
`
Then, how many resolvers do I need for this? Should I handle this query request with only one resolver books and return all books and author info or should I make many resolvers such as Query -> books, Book -> author and Author -> books? I am not sure how the modular schema and resolver works together.
No matter how many type(Book, Author etc) or input you use you need to provide .
const schema = `
type Mutation {
mutatePost(postId:Int) :Int
}
type Query {
hello: String
posts: [String]
books(authorId: String!): Book
}
`
You need to use same name as you defined in Query must be same in resolver
const resolvers = {
Query: {
async hello() {
return 'Hello';
},
async posts() {
return ['Hello', 'World];
},
async books(_, { authorId }) {
//Return data which you is defined in type Book
//return Book
}
},
Mutation: {
async mutatePost(_, {
postId
}, context) {
//return Integer
}
},
}
Only thing every Query and Mutation need queryResolver and mutationResolver

Schema Stitching in Apollo GraphQL doesn't resolve types from other parts

I'm trying to make my GraphQL schema composable through schema stitching, but I'm struggling with how to resolve properties of types from a different part.
Here's the schema before decomposing:
type Referee {
id: ID!
stringProp: String!
}
type Referer {
id: ID!
pointer: Referee!
}
type Query {
referers: [Referer]
}
The types both have resolvers, in their respective schemas, that expand object { id } into { id, stringProp } or { id, pointer: { id } }, so that a query
query FromSingleSchema {
referers: {
id
pointer {
id
stringProp
}
}
}
resolves as expected; Query.referers resolves to a list of [{id}] objects, and each of those in turn resolve first into a Referer and then fetches the pointed-to Referee through type resolvers.
Now, I try to decompose the schema:
// schema A
type Referee {
id: ID!
stringProp: String!
}
// schema B
type Referer {
id: ID!
}
type Query {
referers: [Referer]
}
// schema Extensions
extend type Referer {
pointer: Referee!
}
and compose it again:
// both schemaA and schemaB have been created with makeExecutableSchema
import schemaA from './A'
import schemaB from './B'
// schemaExtensions is just a raw GraphQL string
// resolverExtensions is shown below
import { schemaExtensions, resolverExtensions } from './B'
const schema = mergeSchemas({
schemas: [schemaA, schemaB, schemaExtensions],
resolvers: Object.assign({}, resolverExtensions)
})
// resolverExtensions defined as follows:
{
Referer: {
pointer: {
fragment: 'fragment IdFragment on Referee { id }',
resolve: o => ({ id: o.pointerId })
}
}
}
With this, I can run this query without problems:
query OnlyIdFromDecomposedSchemas {
referers: {
id
pointer {
id
}
}
}
but this fails
query FullRefereeFromDecomposedSchemas {
referers: {
id
pointer {
id
stringProp
}
}
}
with the error message
Cannot return null for non-nullable field Referee.stringProp.
What do I need to do for the type resolver for Referee to be able to fill in the rest of the properties once { id } is available, like it does in a single, non-decomposed, schema?
I think you are looking for schema delegation. Schema Delegation is a way to automatically forward a query (or a part of a query) from a parent schema to another schema (called a subschema) that is able to execute the query.
You can use delegateToSchema method like this in your resolver:
{
Referer: {
pointer : {
resolve(parent, args, context, info) {
return info.mergeInfo.delegateToSchema({
schema: schemaA,
operation: 'query',
fieldName: 'referee', // modify according to your query for referee
context,
info,
});
}
}
}
}

Resources