Running a graphql query from inside a resolver - for nested data - graphql

I am implementing an apollo server graphql schema. All my schema definition are modules in .graphql files. All my resolvers are modules in .js files.
I have the following type :
productSchema.graphql
type Product {
_id: Int
company: Company
productSellingPrice: [PriceHistoryLog]
productName: String
category: String
productDetails: [ProductDetail]
globalId: Int
isActive: Boolean
}
extend type Query {
ProductList: [Product]
}
productDetailSchema.graphql
type ProductDetail {
_id: Int
company: Company
root: Product
catalogItem: CatalogItem
product: Product
isPerishable: Boolean
quantity: Float
isActive: Boolean
}
extend type Query {
ProductDetailsList(productId: Int!): [ProductDetail]
}
What I want to do is, when querying for ProductList, to run a ProductDetailsList query and resolve the field in product from there.
As you can see ProductDetail also have nested fields so I can't just query the DB for that field in the Product resolver.
Any ideas? I am kind of lost.
Edit:
this is my resolver code:
Product: {
company: product => product.companyId,
category: async product => {
try {
let res = await SaleModel.findOne({ productName:
product.productName }) ;
return res.productCategory;
} catch (err) {
console.log(err);
return "Mo Category found";
}
}
},
Query: {
async ProductList(obj, args, { companyId }) {
return await ProductModel.find({
companyId,
isActive: true
}).populate("companyId");
}
},

Related

Get other related records (with id different that queried)

As a newbie to GraphQL I would appreciate some help in the following:
I have query which retrieves its author and that author's books. I would like the author's books to be author's other books, meaning - except the one being queried. What does it involve?
apollo-angular query:
const getBookQuery = gql`
query($id: ID){
book(id: $id){
id
name
year
author {
id
firstName
lastName
books { # <-- give me all _except_ the one with $id
name
year
id
}
}
}
}
`;
and in the schema.js (node.js server) I have something like:
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
book: {
type: BookType,
args: { id: { type: GraphQLID } },
resolve(parent, args) {
const { id } = args;
return Book.findById(id);
},
},
books: {
type: GraphQLList(BookType),
resolve() {
return Book.find({});
},
},
// ... other queries ...
}
})
The solution I am looking for should, obviously, not break other queries for books.
You should be able to achieve the exclusion by adding an argument to the Author type def and then appropriately using that argument in the resolver for books (which should be nested resolver on your Author type). Will need to adapt syntax for apollo-angular.
type Author {
id:
firstName: String
lastName: String
books(exclude: ID): [Book]
}
const resolverMap = {
Query: {
book(arent, args, ctx, info) {
...
}
},
Author: {
books(obj, args, ctx, info) {
// Use args.exclude passed to filter results
},
},
};
const getBookQuery = gql`
query($id: ID){
book(id: $id){
id
name
year
author {
id
firstName
lastName
books(exclude: $id) {
name
year
id
}
}
}
}
`;

Is it possible to fetch data from multiple tables using GraphQLList

In GraphQL we can write the object type in GraphQLList and fetch all the fields. I am using Association and it is joining the two tables but I am unable to fetch the field of both the tables. It only takes the fields what I have written in GraphQLList.As I want the list of data.
Here is the code
films table:
module.exports =(sequelize, DataTypes) => {
const films = sequelize.define(
'films',
{
id:{
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false,
},
name: {
type: DataTypes.STRING,
},
},
);
films.associate = (models) => {
films.hasMany(models.movie_stream, {
foreignKey: 'movie_id',
});
};
return films;
}
movie_stream table:
module.exports = (sequelize, DataTypes) => {
const movie_streams = sequelize.define('movie_streams', {
id:{
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false,
},
movie_id: {
type: DataTypes.STRING,
foreignKey: "movie_id",
},
});
movie_streams.associate = (models) => {
movie_streams.hasMany(models.films, {
foreignKey: 'id',
});
};
return movie_streams;
};
Schema file:
movieList:{
type: new GraphQLList(Films),
resolve: (parent,args)=>{
return newdb.films.findAll({attributes:['id','name','permalink'],
where: {content_category_value:parent.id },
include: [{
model:newdb.movie_stream,
attributes:['id','movie_id'],
}],
}).then(data=>{
return data;
})
}
Can I write here type: new GraphQLList(Films, MovieStream)??
I have tried but It does not work. Please give me some idea how do I fetch the fields of both the table???
There's two main ways of achieving this in GraphQL: unions and interfaces.
An interface is where two or more object types in your GraphQL schema share certain fields (characteristics). For example you might have a Product interface for all the items in your shop, where every product has a price, barcode, and shelfLocation. All your products, such as Shampoo, Bread, LawnChair would then implement this interface.
interface Product {
price: Float
barcode: Int
shelfLocation: ShelfLocation
}
type Bread implements Product {
price: Float
barcode: Int
shelfLocation: ShelfLocation
brand: String
numberOfSlices: Int
calories: Float
bestBefore: Date
}
extend type Query {
searchProducts(phrase: String!): [Product!]
}
A union is where you declare that something can return more than one object type, but those types don't have to have any properties in common.
type Shark {
name: String
numberOfTeeth: Int
}
type Shoe {
brand: String
size: String
}
union SharkOrShoe = Shark | Shoe
extend type Query {
searchSharksAndShoes(phrase: String!): [SharkOrShoe!]
}
In both cases you can query type specific fields using fragments or inline fragments:
query {
searchProducts(phrase: "tasty") {
# shared fields
__typename
price
barcode
shelfLocation { aisle, position }
# type specific fields
... on Bread { brand }
...breadFrag
}
searchSharksAndShoes(phrase: "sleek") {
# only the introspection fields are shared in a union
__typename
# type specific fields
... on Shark { name, numberOfTeeth }
...shoeFrag
}
}
fragment breadFrag on Bread {
barcode
bestBefore
}
fragment shoeFrag on Shoe {
brand
size
}
You can learn more about this in the GraphQL schema documentation and read about GraphQLInterfaceType and GraphQLUnionType in the GraphQL.js documentation.

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

GraphQL - Prisma - resolvers using external API

I'am having this schema:
type Invoice {
id: ID! #unique
description: String
charge: Charge
}
type Charge {
id: ID! #unique
amount: Float
dataFromAPI: DataFromAPI
}
type DataFromAPI {
id: ID! #unique
status: String
}
in the Query Resolver, I have:
async function charge(parent, args, ctx, info) {
chargeData = await ctx.db.query.charge(args, info)
chargeData.dataFromAPI = await DO_THE_API_CALL_TO_RETRIEVE_DATA()
return chargeData
}
and
async function invoice(parent, args, ctx, info) {
invoiceData = await ctx.db.query.invoice(args, info)
return invoiceData
}
the query:
query ChargeQuery {
charge {
id
amount
dataFromAPI
}
}
will return
{
charge {
id: '232323'
amount: 323
dataFromAPI: 'GREAT! DATA IS FROM API'
}
}
but this query:
query InvoiceQuery {
invoice {
id
description
charge {
id
amount
dataFromAPI
}
}
}
will return
{
Invoice {
id: '7723423',
description:'yeah',
charge {
id: '232323'
amount: 323
dataFromAPI: null
}
}
}
dataFromAPI is null because I have not called the API in this resolver.
Where should I call the function DO_THE_API_CALL_TO_RETRIEVE_DATA().
In every resolvers? I guess it is not scalable to do that.
The solution is:
We should use a resolver on the field level.
schema.graphql
type Charge {
id: ID!
invoice: Invoice!
messageErrorPayment: String
stripeChargeId: String!
dateErrorPayment: DateTime
createdAt: DateTime!
chargeData: ChargeData
}
/resolvers/index.js
const { Query } = require('./Query')
const { Mutation } = require('./mutation/Mutation')
const { Charge } = require('./Charge')
module.exports = {
Query,
Mutation,
Charge,
}
charge.js
async function chargeData(parent, args, ctx, info) {
return {
dataFromAPI: await DO_THE_API_CALL_TO_RETRIEVE_DATA()
}
}
const Charge = {
chargeData,
}
module.exports = {
Charge,
}
source: https://www.prisma.io/forum/t/how-to-use-field-resolvers-to-get-aggregates-of-inner-relation-types/2930/2?u=alan345

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