Cannot pass custom result from resolver to Graphql - 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;

Related

GraphQL Stitching - Why would fields from subschemas return null?

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?

How to create a new key and add or append an array of objects on AWS DynamoDB

I have this object in my dynamodb table which looks like
{
"id": "b31de483"
}
I want to add another object that looks like
{
"id": "b31de483",
"players": [{"playerId": "1234"}]
}
This is my code
const addPlayerToGame = async (gameId, playerId) => {
const params = {
TableName: process.env.DYNAMODB_GAMES_TABLE,
Key: {
id: gameId
},
UpdateExpression: 'set players = list_append(if_not_exists(players, :players)',
ExpressionAttributeValues: {
':players': {
"L": [
{ "S": playerId }
]
}
}
};
return await documentClient.update(params);
}
This throws an error but I cannot understand how to fix it. I am looking at documentation here
Figured it out
const addPlayerToGame = async (gameId, playerId) => {
const params = {
TableName: process.env.DYNAMODB_GAMES_TABLE,
Key: {
id: gameId
},
UpdateExpression: 'set players = list_append(players, :players)',
ExpressionAttributeValues: {
':players': [{
"playerId": playerId
}]
}
};
return await documentClient.update(params).promise();
}

Apollo Server / GraphQL - Properties of Nested Array Returning Null

Bear with me, I will explain this the best I can. Please let me know if more information is needed, I am trying to keep this as brief as possible.
I am using Apollo Server and the 'apollo-datasource-rest' plugin to access a REST API. When attempting to get the property values from a nested array of objects I get a null response for each field/property. In addition, the array being queried is only showing a single iteration when multiple are available.
The field in question is the 'cores' field within the Rocket type, i.e., launch.rocket.firstStage.cores
I have attempted various ways of mapping through 'cores' (thinking this was what it wanted) with no success.
To keep things short and simple I'm only including the code for the specific issue. All other parts of the query are operating as expected.
You can view the API response I am hitting here: https://api.spacexdata.com/v3/launches/77
schema.js
const { gql } = require('apollo-server');
const typeDefs = gql`
type Query {
singleLaunch(flightNumber: Int!): Launch
}
type Launch {
flightNumber: Int!
rocket: Rocket
}
type Rocket {
firstStage: Cores
}
type Cores {
cores: [CoreFields]
}
type CoreFields {
flight: Int
gridfins: Boolean
legs: Boolean
reused: Boolean
landingType: String
landingVehicle: String
landingSuccess: Boolean
}
`;
module.exports = typeDefs;
Data Source - launch.js
const { RESTDataSource } = require('apollo-datasource-rest');
class LaunchAPI extends RESTDataSource {
constructor() {
super();
this.baseURL = 'https://api.spacexdata.com/v3/';
}
async getLaunchById({ launchId }) {
const res = await this.get('launches', {
flight_number: launchId,
});
return this.launchReducer(res[0]);
}
launchReducer(launch) {
return {
flightNumber: launch.flight_number || 0,
rocket: {
firstStage: {
cores: [
{
flight: launch.rocket.first_stage.cores.flight,
gridfins: launch.rocket.first_stage.cores.gridfins,
legs: launch.rocket.first_stage.cores.legs,
landingType: launch.rocket.first_stage.cores.landing_type,
landingVehicle: launch.rocket.first_stage.cores.landing_vehicle,
landingSuccess: launch.rocket.first_stage.cores.landing_success,
},
],
},
};
}
}
module.exports = LaunchAPI;
resolvers.js
module.exports = {
Query: {
singleLaunch: (_, { flightNumber }, { dataSources }) =>
dataSources.launchAPI.getLaunchById({ launchId: flightNumber }),
},
};
Query
query GetLaunchById($flightNumber: Int!) {
singleLaunch(flightNumber: $flightNumber) {
flightNumber
rocket {
firstStage {
cores {
flight
gridfins
legs
reused
landingType
landingVehicle
landingSuccess
}
}
}
}
}
Expected Result
{
"data": {
"singleLaunch": {
"flightNumber": 77,
"rocket": {
"firstStage": {
"cores": [
{
"flight": 1,
"gridfins": true,
"legs": true,
"reused": true,
"landingType": "ASDS",
"landingVehicle": "OCISLY",
"landSuccess": true,
},
{
"flight": 1,
"gridfins": true,
"legs": true,
"reused": false,
"landingType": "RTLS",
"landingVehicle": "LZ-1",
"landSuccess": true
},
{
"flight": 1,
"gridfins": true,
"legs": true,
"reused": false,
"landingType": "RTLS",
"landingVehicle": "LZ-2",
"landSuccess": true
},
]
}
},
}
}
}
Actual Result (Through GraphQL Playground)
{
"data": {
"singleLaunch": {
"flightNumber": 77,
"rocket": {
"firstStage": {
"cores": [
{
"flight": null,
"gridfins": null,
"legs": null,
"reused": null,
"landingType": null,
"landingVehicle": null,
"landingSuccess": null
}
]
}
},
}
}
}
Any suggestions as to what I am doing wrong here would be greatly appreciated. Again, let me know if more information is needed.
Thank you!
Missing base url
There should be
await this.get( this.baseURL + 'launches'
IMHO there should be a map used within launchReducer to return an array, sth like:
launchReducer(launch) {
return {
flightNumber: launch.flight_number || 0,
rocket: {
firstStage: {
cores: launch.rocket.first_stage.cores.map(core => ({
flight: core.flight,
gridfins: core.gridfins,
legs: core.legs,
landingType: core.landing_type,
landingVehicle: core.landing_vehicle,
landSuccess: core.land_success,
})),
},
},
};
}
.map(core => ({ is for returning object [literal], the same as/shorter version of .map(core => { return {

Issues with GraphQL Nested Mutation

I am trying to achieve nesting mutation by adding player name in Team (Parent) and struggling trying to fetch list of player name...
Inside GraphiQL tool (localhost:4000/graphiql), this is the Add Mutation variable that I have included...
mutation AddPlayerToTeam($name: String!, $teamId: ID!){
addPlayerToTeam(player: $name, teamId: $teamId){
id
players{
name
}
}
}
The query variables, adding teamID and name...
{
"teamId": "5aff545371fc930a4c43b2b9",
"name": "John Doe"
}
The result shown...
{
"data": {
"addPlayerToTeam": {
"id": "5b072774e385740c38483111",
"players": []
}
}
}
But I was expecting for player name to show up like this....
{
"data": {
"addPlayerToTeam": {
"id": "5b072774e385740c38483111",
"players": [
{
"name": "John Doe"
}
]
}
}
}
The mutation code...
AddPlayerToTeam: {
type: TeamType,
args: {
name: { type: new GraphQLNonNull(GraphQLString) },
teamId: { type: new GraphQLNonNull(GraphQLID) }
},
resolve(parent, { name, teamId }) {
let addPlayer = new Player({ name, teamId });
return addPlayer.save();
}
},
I've struggled to find reason why I am getting "players": [] instead of "players": [ {"name": "John Doe" } ].
Need I include .then(...) after .save() to get result? Any examples? Your help is appreciated.
BTW, I using mongoDB/mongoose method. Saving them in local mongoDB.
Found solution for this... Thank #andrewingram from graphql.slack for helping. Just include .then(...) to return result.
AddPlayerToTeam: {
type: TeamType,
args: {
name: { type: new GraphQLNonNull(GraphQLString) },
teamId: { type: new GraphQLNonNull(GraphQLID) }
},
async resolve(parent, { name, teamId }) {
let addPlayer = new Player({ name, teamId });
await addPlayer.save();
return Team.findById(teamId);
}
},
or in promise version
resolve(parent, { name, teamId }) {
let addPlayer = new Player({ name, teamId });
return addPlayer.save().then(() => Team.findById(teamId));
}
Hope that help.

how to set many-to-many relation in graphql mutation?

I may be missing something, but can not find any information on Apollo docs about the way to set a many-to-many relation when creating a new entry.
When the relation is one-to-many it is as simple as setting the ID of the one-side of the relationship in the many-side object.
But let's pretend I am working with Books and Authors, how would I write a graphql query that creates a Book for one (or many?) Authors?
This should probably happen at the API layer on the GraphQL server (i.e. schema). For many-to-many relationships, you should have a "join" type to denote the BookAuthor many-to-many relationship, and then add an entry to that join type.
Essentially then you'll have a type called Book, another called Author, and finally one more called BookAuthor. And you can add a few mutations to be able to manage that relationship. Perhaps...
addToBookAuthorConnection
updateBookAuthorConnection
removeFromBookAuthorConnection
This is a conventional setup using a Relay-spec compliant API. You can read more about how to structure your API for many-to-many relationships here.
Then, you only need to call the addToBookAuthorConnection mutation from Apollo instead to be able to add to that many-to-many connection on your frontend.
Hope this helps!
If u r using apollo graph server with one to many relations then connectors.js, resolvers.js and schema.js files as given formats
schema.js
const typeDefinitions = `
type Author {
authorId: Int
firstName: String
lastName: String
posts: [Post]
}
type Post {
postId: Int
title: String
text: String
views: Int
author: Author
}
input postInput{
title: String
text: String
views: Int
}
type Query {
author(firstName: String, lastName: String): [Author]
posts(postId: Int, title: String, text: String, views: Int): [Post]
}
type Mutation {
createAuthor(firstName: String, lastName: String, posts:[postInput]): Author
updateAuthor(authorId: Int, firstName: String, lastName: String, posts:[postInput]): String
}
schema {
query: Query
mutation:Mutation
}
`;
export default [typeDefinitions];
resolvers.js
import { Author } from './connectors';
import { Post } from './connectors';
const resolvers = {
Query: {
author(_, args) {
return Author.findAll({ where: args });
},
posts(_, args) {
return Post.findAll({ where: args });
}
},
Mutation: {
createAuthor(_, args) {
console.log(args)
return Author.create(args, {
include: [{
model: Post,
}]
});
},
updateAuthor(_, args) {
var updateProfile = { title: "name here" };
console.log(args.authorId)
var filter = {
where: {
authorId: args.authorId
},
include: [
{ model: Post }
]
};
Author.findOne(filter).then(function (product) {
Author.update(args, { where: { authorId: args.authorId } }).then(function (result) {
product.posts[0].updateAttributes(args.posts[0]).then(function (result) {
//return result;
})
});
})
return "updated";
},
},
Author: {
posts(author) {
return author.getPosts();
},
},
Post: {
author(post) {
return post.getAuthor();
},
},
};
export default resolvers;
connectors.js
import rp from 'request-promise';
var Sequelize = require('sequelize');
var db = new Sequelize('test', 'postgres', 'postgres', {
host: '192.168.1.168',
dialect: 'postgres',
pool: {
max: 5,
min: 0,
idle: 10000
}
});
const AuthorModel = db.define('author', {
authorId: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true, field: "author_id" },
firstName: { type: Sequelize.STRING, field: "first_name" },
lastName: { type: Sequelize.STRING, field: "last_name" },
},{
freezeTableName: false,
timestamps: false,
underscored: false,
tableName: "author"
});
const PostModel = db.define('post', {
postId: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true, field: "post_id" },
text: { type: Sequelize.STRING },
title: { type: Sequelize.STRING },
views: { type: Sequelize.INTEGER },
},{
freezeTableName: false,
timestamps: false,
underscored: false,
tableName: "post"
});
AuthorModel.hasMany(PostModel, {
foreignKey: 'author_id'
});
PostModel.belongsTo(AuthorModel, {
foreignKey: 'author_id'
});
const Author = db.models.author;
const Post = db.models.post;
export { Author, Post };

Resources