GraphQL Stitching - Why would fields from subschemas return null? - graphql

I'm attempting to stitch together two GraphQL schemas, one from contentful and one from neo4j.
Each subschema appears to be interrogated during queries across the combined schema but "foreign" fields always come back as null.
I just can't figure this one out.
Sample Query:
query {
//Request data exclusively from the neo4j schema
Product(id:"475e006f-b9cf-4f40-8712-271ceb46d14b"){
id,
name,
weight
},
//This is a contentful schema query which should return weight from neo4j
product(id:"[contentful-native-id]"){
id,
weight,
}
}
Result:
"data": {
"Product": [
{
"id": "475e006f-b9cf-4f40-8712-271ceb46d14b",
"name": "Test product name",
"weight": 14.9
}
],
"product": {
"id": "475e006f-b9cf-4f40-8712-271ceb46d14b",
"weight": null //This shouldn't be null! :(
}
}
Logging:
//First query being executed against neo4j database
neo4j-graphql-js MATCH (`product`:`Product` {id:$id}) RETURN `product` { .id , .name , .weight } AS `product`
neo4j-graphql-js {
"offset": 0,
"first": -1,
"id": "475e006f-b9cf-4f40-8712-271ceb46d14b"
}
//Triggered by the second query correctly trying to resolve weight from neo4j
neo4j-graphql-js MATCH (`product`:`Product` {id:$id}) RETURN `product` { .weight , .id } AS `product`
neo4j-graphql-js {
"offset": 0,
"first": -1,
"id": "475e006f-b9cf-4f40-8712-271ceb46d14b"
}
This seems to suggest something is working, but the result of weight never makes it to the final output.
ApolloServer doesn't report any errors via didEncounterErrors()
Stitching:
const gatewaySchema = stitchSchemas({
subschemas: [{
schema: neoSchema,
merge: {
Product: {
selectionSet: '{id}',
fieldName: 'Product',
args: ({
id
}) => ({
id
}),
}
}
},
{
schema: contentfulSchema,
merge: {
}
}
],
})
Schemas:
const executor = async ({
document,
variables,
context
}) => {
const query = print(document);
//console.log(query);
const fetchResult = await fetch('https://graphql.contentful.com/content/v1/spaces/[SPACE]', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer [AUTHTOKEN]`,
},
body: JSON.stringify({
query,
variables
})
});
return fetchResult.json();
};
const contentfulSchema = wrapSchema({
schema: await introspectSchema(executor),
executor: executor
});
const driver = neo4j.driver(
process.env.NEO4J_URI || 'bolt://localhost:7687',
neo4j.auth.basic(
process.env.NEO4J_USER,
process.env.NEO4J_PASS
), {
encrypted: process.env.NEO4J_ENCRYPTED ? 'ENCRYPTION_ON' : 'ENCRYPTION_OFF',
}
)
const neoSchema = makeAugmentedSchema({
typeDefs: typeDefs,
});
Server:
const server = new ApolloServer({
schema: gatewaySchema,
context: ({ req }) => {
return {
driver,
req
};
},
plugins:[
myPlugin
]
});
Any insight or ideas much appreciated!

This appears to be down to the fact that stitchSchemas is NOT supported in ApolloServer...
Does Apollo Server work with GraphQL Tools stitchSchemas?

Related

Displaying the password on a filtered request

Strapi Version: 4.4.5
Operating System: linux
Database: sqlite
Node Version: 16.17.0
NPM Version:
Yarn Version: 1.22.19
Hello,
I'm just trying to get all the information from my "Channel" table, namely the product_id and the "users" concerned in the channel. I simply overload my find method like this:
module.exports = createCoreController("api::channel.channel", ({ strapi }) => ({
async find(ctx) {
const { user } = ctx.state:
const entity = await strapi.service("api::channel.channel").find({
filters: {
users: {
id: {
$in: user.id,
},
},
},
populate: ["users"]
});
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
},
}));
And for some reason, I get all the user information and especially the hash of the passwords.
So I try to do a select on my populate like this, but it doesn't work :
module.exports = createCoreController("api::channel.channel", ({ strapi }) => ({
async find(ctx) {
const { user } = ctx.state;
const entity = await strapi.service("api::channel.channel").find({
filters: {
users: {
id: {
$in: user.id,
},
},
},
populate: {
users: {
select: ["id"]
}
}
});
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
},
}));
Does anyone have a solution to my problem?
It is not select, you would use fields
Strapi Population
const qs = require('qs');
const query = qs.stringify({
fields: ['title', 'body'],
}, {
encodeValuesOnly: true, // prettify URL
});
So in your case, fields in combination with populate.
populate: {
users: {
fields: ["id"]
}
}

Cannot pass custom result from resolver to Graphql

I am trying to fetch data with sequelize with an attribute and pass it to graphql.
The result is fine in console but the graphql query is returning null for the attribute field.
my resolver
getUnpayedLessons: async (_, args, { models }) => {
const { Attendance, Student } = models;
return await Attendance.findAll({
include: {
model: Student,
},
where: {
fk_lessonsSerieId: { [Op.is]: null },
},
attributes: ["id", [sequelize.fn("count", sequelize.col("absenceFlag")), "unpayedLessons"]],
group: ["student.id"],
});
},
query
getUnpayedLessons {
id
unpayedLessons
student {
id
firstName
lastName
}
}
schema
type UnpayedLessons {
id: Int
unpayedLessons: Int
student: Student
}
extend type Query {
getUnpayedLessons: [UnpayedLessons]
}
and this is the console.log of the resolver when I run the query
[
attendance {
dataValues: { id: 2, unpayedLessons: 8, student: [student] },
_previousDataValues: { id: 2, unpayedLessons: 8, student: [student] },
_changed: Set {},
_options: {
isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
include: [Array],
includeNames: [Array],
includeMap: [Object],
includeValidated: true,
attributes: [Array],
raw: true
},
]
and from graphql
{
"data": {
"getUnpayedLessons": [
{
"id": 2,
"unpayedLessons": null,
"student": {
"id": 2,
"__typename": "Student"
},
"__typename": "UnpayedLessons"
},
]
}
}
Any idea how I can have unpayedLessons passed to graphql?
To debug this you need to check what is returned from DB, the shape:
const values = await Attendance.findAll({...
console.log( values );
// adapt structure to match query requirements
// finally return
return values;

GraphQL Error: "Expected Iterable, but did not find one for field Query" when fetching data from CryptoCompare API

I'm using GraphQL/express/express-graphql/axios to retrieve specific coin data from the CryptoCompare API.
I have two endpoints:
1) https://min-api.cryptocompare.com/data/pricemultifull?fsyms=BTC,ETH,LTC,BCH,NEO,ETC,XMR&tsyms=USD
From endpoint 1, I want to retrieve the following in USD for 8 coins:
- FROMSYMBOL, CHANGEPCT24HOUR, PRICE, MKTCAP, TOTALVOLUME24HTO
2) https://min-api.cryptocompare.com/data/coin/generalinfo?fsyms=BTC&tsym=USD
From endpoint 2, I want to retrieve the following just for Bitcoin/BTC:
- Id, FullName, ImageUrl
I have setup my backend server with two files, as well as testing queries using graphiql.
File 1 - server.js
const express = require("express")
const graphqlHTTP = require("express-graphql")
const cors = require("cors")
const schema = require("./schema")
const app = express()
app.use(cors())
app.use(
"/graphql",
graphqlHTTP({
schema,
graphiql: true
})
)
const PORT = process.env.PORT || 4000
app.listen(PORT, console.log(`✅ Listening to port ${PORT}`))
File 2 - schema.js
const {
GraphQLObjectType,
GraphQLList,
GraphQLID,
GraphQLInt,
GraphQLString,
GraphQLSchema
} = require("graphql")
const axios = require("axios")
const CoinDataType = new GraphQLObjectType({
name: "CoinData",
fields: () => ({
FROMSYMBOL: { type: GraphQLString },
CHANGEPCT24HOUR: { type: GraphQLInt },
PRICE: { type: GraphQLInt },
MKTCAP: { type: GraphQLInt },
TOTALVOLUME24HTO: { type: GraphQLInt }
})
})
const CoinInfoType = new GraphQLObjectType({
name: "CoinInfo",
fields: () => ({
Id: { type: GraphQLID },
FullName: { type: GraphQLString },
ImageUrl: { type: GraphQLString }
})
})
const Query = new GraphQLObjectType({
name: "Query",
fields: {
CoinData: {
type: new GraphQLList(CoinDataType),
resolve(parent, args) {
return axios
.get(
"https://min-api.cryptocompare.com/data/pricemultifull?fsyms=BTC,ETH,LTC,BCH,NEO,ETC,XMR&tsyms=USD"
)
.then(res => res.data)
}
},
CoinInfo: {
type: new GraphQLList(CoinInfoType),
resolve(parent, args) {
return axios
.get(
"https://min-api.cryptocompare.com/data/coin/generalinfo?fsyms=BTC&tsym=USD"
)
.then(res => res.data)
}
}
}
})
module.exports = new GraphQLSchema({ query: Query })
When I use graphiql to test my queries with this:
{
CoinData {
FROMSYMBOL
}
CoinInfo {
Id
}
}
...I get this error:
{
"errors": [
{
"message": "Expected Iterable, but did not find one for field Query.CoinInfo.",
"locations": [
{
"line": 5,
"column": 3
}
],
"path": [
"CoinInfo"
]
},
{
"message": "Expected Iterable, but did not find one for field Query.CoinData.",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"CoinData"
]
}
],
"data": {
"CoinData": null,
"CoinInfo": null
}
}
How do I get around this error? Thanks.

I have an SQL syntax error in graphQL when i hit the query using join monster

This is My Schema
const graphql = require('graphql');
const joinMonster = require('join-monster');
const FilmDb = require('../model/Films');
const { GraphQLObjectType, GraphQLList, GraphQLString, GraphQLInt,GraphQLSchema } = graphql;
const Films = new GraphQLObjectType({
name: 'films',
fields: () => ({
id: {
type: GraphQLInt,
},
name: {
type: GraphQLString,
},
})
})
Films._typeConfig = {
sqlTable: 'films',
uniqueKey: 'id'
}
const QueryRoot = new GraphQLObjectType({
name: 'Query',
fields: () => ({
films: {
type: new GraphQLList(Films),
resolve: (parent, args, context, resolveInfo) => {
return joinMonster.default(resolveInfo, {}, sql => {
return FilmDb.query(sql).then(function(result) {
return result;
});
})
}
}
})
})
module.exports = new GraphQLSchema({
query: QueryRoot
})
When I hit the below query
{
films{
id
}
}
It is showing me an error
{
"errors": [
{
"message": "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.\"id\" AS \"id\"\nFROM films \"films\"' at line 1",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"films"
]
}
],
"data": {
"films": null
}
}
my SQL query what is generated in the terminal is
SELECT
"films"."id" AS "id"
FROM films "films"
Here I m using nodejs and in DB using Sequelize
What is the error here?? I m unable to find exact error.

GraphQL how to mutate data

I have a basic schema for mutating some data which looks like
const schema = new graphql.GraphQLSchema({
mutation: new graphql.GraphQLObjectType({
name: 'Remove',
fields: {
removeUser: {
type: userType,
args: {
id: { type: graphql.GraphQLString }
},
resolve(_, args) {
const removedData = data[args.id];
delete data[args.id];
return removedData;
},
},
},
})
});
Looking around google I cant find a clear example of the example query which needs to be sent to mutate.
I have tried
POST -
localhost:3000/graphql?query={removeUser(id:"1"){id, name}}
This fails with error:
{
"errors": [
{
"message": "Cannot query field \"removeUser\" on type \"Query\".",
"locations": [
{
"line": 1,
"column": 2
}
]
}
]
}
In order to post requests from the front-end application it is recommended to use apollo-client package. Say i wanted to validate a user login information:
import gql from 'graphql-tag';
import ApolloClient, {createNetworkInterface} from 'apollo-client';
client = new ApolloClient({
networkInterface: createNetworkInterface('http://localhost:3000/graphql')
});
remove(){
client.mutate({
mutation: gql`
mutation remove(
$id: String!
) {
removeUser(
id: $id
){
id,
name
}
}
`,
variables: {
id: "1"
}
}).then((graphQLResult)=> {
const { errors, data } = graphQLResult;
if(!errors && data){
console.log('removed successfully ' + data.id + ' ' + data.name);
}else{
console.log('failed to remove');
}
})
}
More information about apollo-client can be found here
Have you tried using graphiql to query and mutate your schema?
If you'd like to create a POST request manually you might wanna try to struct it in the right form:
?query=mutation{removeUser(id:"1"){id, name}}
(Haven't tried POSTing myself, let me know if you succeeded, i structured this out of the url when using graphiql)
You have to explicitly label your mutation as such, i.e.
mutation {
removeUser(id: "1"){
id,
name
}
}
In GraphQL, if you leave out the mutation keyword, it's just a shorthand for sending a query, i.e. the execution engine will interpret it as
query {
removeUser(id: "1"){
id,
name
}
}
cf. Section 2.3 of the GraphQL Specification
const client = require("../common/gqlClient")();
const {
createContestParticipants,
} = require("../common/queriesAndMutations");
const gql = require("graphql-tag");
const createPartpantGql = async (predictObj) => {
try {
let resp = await client.mutate({
mutation: gql(createContestParticipants),
variables: {
input: {
...predictObj,
},
},
});
let contestParticipantResp = resp.data.createContestParticipants;
return {
success: true,
data: contestParticipantResp,
};
} catch (err) {
console.log(err.message)
console.error(`Error creating the contest`);
return {
success: false,
message: JSON.stringify(err.message),
};
}
};

Resources