Apollo makeExecutableSchema throws error "Cannot read property 'kind' of undefined" - javascript

I am trying to do schema stitching with apollo but I am getting an error every time I use makeExecutableSchema. The error is the following:
../node_modules/graphql-tools/dist/generate/concatenateTypeDefs.js:9:
-> if (typeDef.kind !== undefined)
TypeError: Cannot read property 'kind' of undefined
I have reproduced the problem even when just copying the basic example on Apollo's website
const { ApolloServer, gql, makeExecutableSchema } = require("apollo-server");
const { addMockFunctionsToSchema, mergeSchemas } = require("graphql-tools");
const chirpSchema = makeExecutableSchema({
typeDefs: `
type Chirp {
id: ID!
text: String
authorId: ID!
}
type Query {
chirpById(id: ID!): Chirp
chirpsByAuthorId(authorId: ID!): [Chirp]
}
`
});
addMockFunctionsToSchema({ schema: chirpSchema });
const authorSchema = makeExecutableSchema({
typeDefs: `
type User {
id: ID!
email: String
}
type Query {
userById(id: ID!): User
}
`
});
addMockFunctionsToSchema({ schema: authorSchema });
const schema = mergeSchemas({
schemas: [chirpSchema, authorSchema]
});
const server = new ApolloServer(schema);
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
What am I doing wrong? However I try I always get the same error when using makeExecutableSchema.

From what i see you are using apollo-server#2.0.0-rc
in this version
ApolloServer constructor receives an options parameter
constructor(options)
you should pass new ApolloServer({ schema: schema }) instead of new ApolloServer(schema)
i tried that with the example you gave and it worked :)

Related

Get populated data from Mongoose to the client

On the server, I am populating user-data and when I am printing it to the console everything is working fine but I am not able to access the data on the client or even on Playground of GraphQL.
This is my Schema
const { model, Schema } = require("mongoose");
const postSchema = new Schema({
body: String,
user: {
type: Schema.Types.ObjectId,
ref: "User",
},
});
module.exports = model("Post", postSchema);
const userSchema = new Schema({
username: String,
});
module.exports = model("User", userSchema);
const { gql } = require("apollo-server");
module.exports = gql`
type Post {
id: ID!
body: String!
user: [User]!
}
type User {
id: ID!
username: String!
}
type Query {
getPosts: [Post]!
getPost(postId: ID!): Post!
}
`;
Query: {
async getPosts() {
try {
const posts = await Post.find()
.populate("user");
console.log("posts: ", posts[0]);
// This works and returns the populated user with the username
return posts;
} catch (err) {
throw new Error(err);
}
},
}
But on the client or even in Playground, I can't access the populated data.
query getPosts {
getPosts{
body
user {
username
}
}
}
My question is how to access the data from the client.
Thanks for your help.
you are using this feature in the wrong way you should defined a Object in your resolvers with your model name and that object should contain a method that send the realated user by the parant value.
here is a full document from apollo server docs for how to use this feature
use lean() like this :
const posts = await Post.find().populate("user").lean();

Mongoose populate returns empty array or list of ObjectIds

I am practicing my express.js skills by building a relational API and am struggling to populate keys in a schema.
I am building it so I have a list of properties, and those properties have units. The units have a propertyId key.
This is currently returning an empty array, whereas if i remove the populate({}) it returns an array of ObjectIds.
I've read a number of posts and some people solved this by using .populate({path: 'path', model: Model}); but this doesn't seem to be doing the trick. I think it might be the way I am adding a propertyId to the unit but I'm not sure. Can anyone see where I am going wrong? Any help will be massively appreciated.
Here are the schemas.
Property:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const PropertySchema = new Schema({
title: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
units: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'unit'
}
]
});
module.exports = Property = mongoose.model('property', PropertySchema);
Unit:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const UnitSchema = new Schema({
title: {
type: String,
required: true
},
propertyId: {
type: Schema.Types.ObjectId,
ref: 'property'
}
});
module.exports = Unit = mongoose.model('unit', UnitSchema);
I am then creating the unit like this:
-- api/properties/:id/units --
router.post('/:id/units', async (req, res) => {
// Get fields from req.body
const { title } = req.body;
// Get current property
const property = await Property.findById(req.params.id);
try {
// Throw error if no property
if (!property) {
return res.status(400).json({ msg: 'Property not found' });
}
// Create new unit
const newUnit = new Unit({
title,
propertyId: req.params.id
});
// Add new unit to property's units array
property.units.unshift(newUnit);
// Save property
await property.save();
// Return successful response
return res.status(200).json(property);
} catch (error) {
console.error(error.message);
return res.status(500).send('Server error');
}
});
And trying to populate in the GET request
-- /api/properties/:id/units --
const Unit = require('../../models/Unit');
router.get('/:id/units', async (req, res) => {
const property = await Property.findOne({ _id: req.params.id }).populate({path: 'units', model: Unit});
const propertyUnits = property.units;
return res.status(200).json(propertyUnits);
});
If i remove the .populate({path: 'units', model: Unit});, I get a list of unit id's like this:
[
"5ff7256cda2f5bfc1d2b9108",
"5ff72507acf9b6fb89f0fa4e",
"5ff724e41393c7fb5a667dc8",
"5ff721f35c73daf6d0cb5eff",
"5ff721eb5c73daf6d0cb5efe",
"5ff7215332d302f5ffa67413"
]
I don't know, why you don't try it like this:
await Property.findOne({ _id: req.params.id }).populate('units')
I've been try that code above and it's working.
Note: Make sure to check your req.params.id is not null or undefined and make sure the data you find is not empty in your mongodb.
Updated: I've been try your code and it's working fine.
The issue was caused by inconsistent naming and not saving the new created unit as well as the updated property.
I double checked all my schema exports and references and noticed I was using UpperCase in some instances and LowerCase in others, and saved the newUnit as well as the updated property in the POST request and it worked.

info argument is empty in Apollo GraphQL resolver type signature

I'm working on this library https://github.com/ilyaskarim/wertik-js called Wertik JS to make GraphQL + Rest API more easily, In resolvers, when I console log info, it shows undefined. For each module, I have created dynamic resolvers to make things more easy for developers who will use this library.
let object = {
create: async (_:any, args:any, context:any,info: any) => {
console.log(info); // This will be undefined
let v = await validate(validations.create,args.input);
let {success} = v;
if (!success) {
throw new ApolloError("Validation error",statusCodes.BAD_REQUEST.number,{list: v.errors})
}
try {
let createModel = await model.create(args.input);
pubsub.publish(`${camelCase(moduleName)}Created`, { [`${camelCase(moduleName)}Created`]: createModel });
return createModel;
} catch (e) {
return internalServerError(e);
}
},
}
Line: https://github.com/ilyaskarim/wertik-js/blob/ec813f49a14ddd6a04680b261ae4ef2aadc2b1a5/src/framework/dynamic/resolvers.ts#L102
The info is described in Apollo Server Documentation https://www.apollographql.com/docs/apollo-server/essentials/data/#resolver-type-signature, Which says: This argument contains information about the execution state of the query, including the field name, the path to the field from the root, and more. For me, unfortunately, it is getting undefined.
To reproduce the issue:
Download https://github.com/ilyaskarim/wertik-js/tree/development
Yarn install
Go to examples/demo
Run node index.js
Now go to http://localhost:1209/
Enter this mutation for example:
mutation {
createRole(input: {name: "Asd"}) {
name
}
}
This line executes on this mutation https://github.com/ilyaskarim/wertik-js/blob/ec813f49a14ddd6a04680b261ae4ef2aadc2b1a5/src/framework/dynamic/resolvers.ts#L102
And returns undefined on the console.
This is how I setup the application:
const { ApolloServer } = require('apollo-server');
import mutations from "./loadAllMutations";
import queries from "./loadAllQueries";
import resolvers from "./loadAllResolvers";
import subscriptions from "./loadAllSubscriptions";
import schemas from "./loadAllSchemas";
import generalSchema from "./../helpers/generalSchema";
export default function (rootDirectory: string,app: any,configuration: object) {
let allMutations = mutations(rootDirectory);
let allQueries= queries(rootDirectory);
let allSchemas = schemas(rootDirectory);
let allResolvers = resolvers(rootDirectory);
let allSubscriptions = subscriptions(rootDirectory);
let {validateAccessToken} = require(`${rootDirectory}/framework/predefinedModules/user/auth`).default;
let mainSchema = `
${generalSchema}
${allSchemas}
type Subscription {
${allSubscriptions}
}
type Mutation {
${allMutations}
}
type Query {
${allQueries}
}
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
`;
const server = new ApolloServer({
typeDefs: mainSchema,
resolvers: allResolvers,
context: async (a: any) => {
await validateAccessToken(a.req);
}
});
server.listen(1209).then(({ url, subscriptionsUrl }) => {
console.log(`Server ready at ${url}`);
console.log(`Subscriptions ready at ${subscriptionsUrl}`);
});
}
What could be a possible reason?
You're truncating the parameters received by the resolvers inside this module. If you need to assign a function to some object property, it's much better to just do it like this:
mutations: {
[`create${moduleName}`]: mutations[`create${moduleName}`],
},
This is not only more succinct, but it also means you don't risk accidentally leaving off a parameter, which is what happened here.

TypeORM Apollo nested query resolver

I have a schema (with the appropriate database tables and entity classes defined) like
type User {
id: Int!
phoneNumber: String!
}
type Event {
id: Int!
host: User
}
and I'm trying to use Apollo to write a query like
Query{
event(id:1){
host{
firstName
}
}
}
But I can't figure out how to get the Apollo library to resolve the User type in the host field to the hostId that is stored on the event object.
I modified the event to return the hostId field, and it works perfectly fine, but Graphql won't resolve the id to the appropriate user type. What am I missing?
edit: missing resolver code
event: async (parent: any, args: { id: number }) => {
const eventRepository = getConnection().getRepository(Event);
const event = await eventRepository.findOne(args.id);
return event;
},
I managed to get a working version by using findOne(args.id, { relations: ['host']}), but I don't like that because it seems like something that would be appropriate to delegate to graphql to handle.
Your resolver should be like that
const resolver = {
Query: {
event: async (_: any, args: any) => {
return await event.findOne(args.id);
}
},
event: {
host: async (parent: any, args: any, context: any) => {
return await user.find({ id: parent.id });
}
}
};

Graphql error on subfields with graphql-yoga

I'm trying to query a graphql API via a proxy of another graphql API and am receiving an error. I'm using graphql yoga for my server and connecting to another graphql API from a CMS. Here is my code:
server.js
const { GraphQLServer } = require('graphql-yoga');
const Prismic = require('./prismic.config.js');
const gql = require('graphql-tag');
const typeDefs = `
type Query {
page(lang: String, uid: String): Page
}
type Page {
page_title: [TextField]
}
type TextField {
text: String
}
`
const resolvers = {
Query: {
page: (parent, args, context, info) => {
const query = gql`${context.request.body.query}`;
const result = context.Prismic.query({
query,
variables: { ...args }
})
.then(resp => {
return resp.data.page;
})
.catch(err => console.log(err));
return result;
}
}
}
const server = new GraphQLServer({
typeDefs,
resolvers,
context: req => ({ ...req, Prismic })
})
server.start(() => console.log('Server is running on localhost:4000'))
Here is my query below from graphql playground that comes with Graphql Yoga:
query {
page(lang: "en-gb", uid: "homepage") {
page_title {
text
}
}
}
The error i'm receiving is:
'Query does not pass validation. Violations:\n\nField \'page_title\'
of type \'Json\' must not have a sub selection. (line 3, column 5):\n
page_title {\n ^' } },
The strange thing is I can get a valid response if I hardcode the query without the nested text field as the error suggests on the server like so:
// const query = gql`${context.request.body.query}`;
const query = gql`
query($uid: String!) {
page(lang: "en-gb", uid: $uid) {
page_title
}
}
`;
Attempting to modify my query in graphql playground to not include the nested text field like so:
query {
page(lang: "en-gb", uid: "homepage") {
page_title
}
}
Gives my the following error and does not allow me to make the request at all:
field "page_title" of type "[TextField]" must have a selection of
subfields. Did you mean "page_title { ... }"?
The error suggests that I need to add the nested subfield of text which is intended but when I use this query instead of the hardcoded one on the server it gives me the error mentioned before.
Not sure if i've gone wrong somewhere in my setup?
Thanks
In your GraphQL schema
page_title: [TextField] is not one of the Scalar Types
As a result, during making a query you need to define what exactly fields you need to fetch?
And your fields in the query should be expanded to the level of having only scalar types, so GraphQL will know how to resolve your query.
So this is the only query that should be from the first level (from graphql playground that comes with Graphql Yoga) :
query {
page(lang: "en-gb", uid: "homepage") {
page_title {
text
}
}
}
But the error from the server throws from your approach to make graphql query inside graphql resolver:
const result = context.Prismic.query({
query,
variables: { ...args }
})
So I'm 100% sure that the page_title in Prismic has the custom scalar - JSON. As a result, you can't use the same query for this request.

Categories

Resources