Handle types with identical properties in GraphQL - 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?

Related

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

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.

Graphql Api Chuck Norris

I need help building a GraphQL Api that wraps the ChuckNorris.io API
The API sholud have aQuery type that resolves all Categories
(https://api.chuckmorris.io/jokes/categories)
The Api should have Query type that resolves a random joke given as an argument (https://api.chucknorris.io/jokes/random?category={category})
const express=require('express');
const {ApolloServer,gql}=require('apollo-server-express');
const fetch=require('node-fetch');
const typeDefs=gql`
type Joke{
icon_url:String,
id:String,
url:String
value: String
}
type Category{
animal:String
career:String
celebrity:String
dev:String
explicit:String
fashion:String
food:String
history:String
money:String
movie:String
music:String
political:Strig
religion:String
science:String
sport:String
travel:String
}
type Query{
getCategory(category:String!):Joke
category:Category
}
`
const resolvers={
Query:{
getCategory: async(_,{category})=>{
const response=await fetch(`https://api.chucknorris.io/jokes/random?category=${category}`)
return response.json();
},
category: async(_,{})=>{
const response=await fetch('https://api.chucknorris.io/jokes/categories')
return response.json();
}
}
}
const server= new ApolloServer({typeDefs,resolvers});
const app=express();
server.applyMiddleware({app});
app.listen({port:4000},()=>
console.log('Now browse to http://localhost:4000' + server.graphqlPath)
)
your query for type category should return a list of strings (array)
so
export const typeDefs = gql`
type Joke {
value: String!
id:ID!
icon_url:String!
}
type Query {
getAllCategories:[String!]!
randomJoke(category: String!):Joke
}
`;
for your resolver, you don't need fetch. apollo provides datasources to connect to external REST APIs like the one you have.
so install the npm package "apollo-datasource-rest" and add it to your instance of apollo server like so
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: ()=>({
jokeinstance : new Jokenorris
})
})
then create the datasource class for Jokenorris and import appropriately or do everything in one src file as you did.
import pkg from "apollo-datasource-rest";
const { RESTDataSource } = pkg;
export class Jokenorris extends RESTDataSource {
constructor() {
super();
this.baseURL = "https://api.chucknorris.io/jokes";
}
async getAllCategories() {
const res = await this.get("categories");
return res;
}
async getRandomJoke({ category }) {
const response = await this.get("random", { category: category });
return response;
}
}
then your resolveer can look like so, you can ignore the exports and imports if you chunked everything in one file
export const resolvers = {
Query: {
allJokeCategories: (_, __, { dataSources }) =>
dataSources.jokeinstance.getAllCategories(),
randomJoke: (_, {category}, {dataSources})=>
dataSources.jokeinstance.getRandomJoke({category:category})
},
};

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

Why does GraphQL require me to have a resolver that returns an empty object?

So, when I have a schema:
type Query { me: Me }
type Me { hello: String }
I expect a satisfying resolver to be:
const resolvers = {
Me: {
hello() { return "Hi, me!"; }
}
}
Alas, this isn't the case, I have to add a dummy me-resolver (see below).
Why is this the case?
I'd say it should traverse the query, and if it can't find a satisfying field-resolver, it should look for a corresponding type-resolver.
const { graphql } = require("graphql");
const { makeExecutableSchema } = require("graphql-tools");
const compose = require("compose-function");
const typeDefs = `
type Query {
me: Me
}
type Me {
hello: String!
}
`;
const resolvers = {
Query: {
// ===========================>
me() {
return {};
}
// <===========================
},
Me: {
hello() {
return "Hi, me!";
}
}
};
const schema = makeExecutableSchema({
typeDefs,
resolvers
});
graphql(schema, "{ me { hello } }").then(
compose(
console.log,
JSON.stringify
)
); // {"data":{"me":{"hello":"Hi, me!"}}}
Alright, I've figured it out. It actually makes a lot of sense.
null is a valid response. Only if there is an object with properties that the query wants, it should look for the next resolver to satisfy the query.
It's also written in the spec under Errors and Non‐Null Fields https://graphql.github.io/graphql-spec/June2018/#sec-Executing-Selection-Sets

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

Resources