How do I give parameters to function in GraphQL - node.js

Actually, I'm a newbie to graphQL so I wasn't able to pass parameters rightly in function updateMessage() in graphiQL. I'm trying to update the database using
mutation {
createMessage(input: {
author: "Pawan",
content: "hope is a dangerous thing",
}) {
id,content,author,
}
updateMessage(id:{cfe934d60b9997a4507e},input:{
author: "Pawan",
content: "hope is a dangerous thing",
})
}
but the error is displayed as
{
"errors": [
{
"message": "Syntax Error: Expected :, found }",
"locations": [
{
"line": 8,
"column": 40
}
]
}
]
}
Beside I'm also not able to show fakeDatabase .Can I do that ?
if yes How can I show every time I add a message to the fakeDatabase?
mutation.js
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
// Construct a schema, using GraphQL schema language
var schema = buildSchema(`
input MessageInput {
content: String
author: String
}
type Message {
id: ID!
content: String
author: String
}
type Query {
getMessage(id: ID!): Message
}
type Mutation {
createMessage(input: MessageInput): Message
updateMessage(id: ID!, input: MessageInput): Message
}
`);
// If Message had any complex fields, we'd put them on this object.
class Message {
constructor(id, {content, author}) {
this.id = id;
this.content = content;
this.author = author;
}
}
// Maps username to content
var fakeDatabase = {};
var root = {
getMessage: function ({id}) {
if (!fakeDatabase[id]) {
throw new Error('no message exists with id ' + id);
}
return new Message(id, fakeDatabase[id]);
},
createMessage: function ({input}) {
// Create a random id for our "database".
var id = require('crypto').randomBytes(10).toString('hex');
fakeDatabase[id] = input;
return new Message(id, input);
},
updateMessage: function ({id, input}) {
if (!fakeDatabase[id]) {
throw new Error('no message exists with id ' + id);
}
// This replaces all old data, but some apps might want partial update.
fakeDatabase[id] = input;
return new Message(id, input);
},
};
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
console.log(fakeDatabase)
app.listen(4000, () => {
console.log('Running a GraphQL API server at localhost:4000/graphql');
});

On your mutation updateMessage try updating the parameters and send $id as a string instead of an object, like:
updateMessage(id:"cfe934d60b9997a4507e",input:{
author: "Pawan",
content: "hope is a dangerous thing",
})
The issue is that mutation updateMessage requires an ID and MessageInput, but you're sending Object and MessageInput.

Related

I have a Joi schema and I want that when I do a POST method the id is included and required and with PUT method the id is not taken into account

I have a schema joi object:
const commonFields = {
id: joi.string().required(),
name: joi.string().required().min(5).max(50),
nif: joi.string().length(9).required(),
diet: joi.bool().required().strict(),
vegetarian: joi.bool().required().strict(),
};
const aluno = joi.object({
...commonFields,
num: joi
.string()
.required()
.regex(/^\d{1,4}\/\d{2}$/),
regime: joi.string().required().valid("externo", "interno"),
});
(...)
When i make a PUT request to update a user i want to ignore the field "id" but when i make a POST request i want to make it required.
I tried the following:
In schema I added alter() to the field "id":
id: joi.string().alter({
post: (schema) => schema.required(),
put: (schema) => schema.forbidden(),
}),
And in my functions i did this:
async function updateUser(req, res, type, db) {
try {
const { error } = requestValidation[type].validate(req.body, {
context: { method: req.method }
});
if (error) {
return res.status(400).send({ error: error.message });
}
const id = req.params.id;
const { name, nif, vegetarian, diet } = req.body;
(..)
But when i call this function in my PUT endpoint to update a user and add the field id into the requesition body it doesn't throw an error like it should throw. The response should be like this when i add the id to the body:
{
"error": "\"id\" is not allowed"
}
I want to ignore the id because I want to receive it by req.params.id.
I may not be doing the best way but I'm open to new suggestions!
The documentation has an example using tailor with alter
const { error } = requestValidation[type].tailor(req.method.toLowerCase()).validate(req.body, {
context: { method: req.method }
});

Graphql is returning "Cannot read property 'id' of undefined", [apollo server, node.js]

I am trying to display one course and to update course topic, using graphql and apollo-server.
This is my code:
const { ApolloServer, gql } = require('apollo-server');
var typeDefs=gql`
input CourseInput {
id: Int
title: String
author: String
description: String
topic: String
url: String
}
type Course {
id: Int
title: String
author: String
description:String
topic:String
url: String
}
type Query {
course(id: Int!): Course
courses(topic:String!):[Course]
allcourses:[Course]
hello: String
}
type Mutation {
updateCourseTopic(id: Int !, topic: String !): Course
createCourse(input: CourseInput): [Course]
deleteCourse(id: Int !): [Course]
}
`;
var coursesData = [
{
id: 1,
title: 'The Complete Node.js Developer Course',
author: 'Andrew Mead, Rob Percival',
description: 'Learn Node.js by building real-world applications with Node, Express, MongoDB, Mocha, and more!',
topic: 'Node.js',
url: 'https://codingthesmartway.com/courses/nodejs/'
},
{
id: 2,
title: 'Node.js, Express & MongoDB Dev to Deployment',
author: 'Brad Traversy',
description: 'Learn by example building & deploying real-world Node.js applications from absolute scratch',
topic: 'Node.js',
url: 'https://codingthesmartway.com/courses/nodejs-express-mongodb/'
},
{
id: 3,
title: 'JavaScript: Understanding The Weird Parts',
author: 'Anthony Alicea',
description: 'An advanced JavaScript course for everyone! Scope, closures, prototypes, this, build your own framework, and more.',
topic: 'JavaScript',
url: 'https://codingthesmartway.com/courses/understand-javascript/'
}
]
var getCourses = function(args){
console.log("delila2")
if(args.topic){
console.log("delila2")
var topic=args.topic;
return coursesData.filter(course=>
course.topic===topic
);
}
else return coursesData
}
var resolvers= {
Query:{
course:getcourse,
courses:getCourses,
allcourses:getAllCourses,
hello: ()=>"Delila"
},
Mutation: {
updateCourseTopic:updateCourseTopic,
createCourse:createCourse,
deleteCourse: deleteCourse,
}
};
function getcourse(args){
var id=args.id;
return coursesData.filter(course=>{
return course.id==id
})[0]
}
function getAllCourses(){
console.log("all courses")
return coursesData;
}
function updateCourseTopic (id, topic){
console.log("id je" ,id)
coursesData.map(course=>{
if(course.id===id){
course.topic=topic;
return course
}
});
console.log("svi", coursesData);
return coursesData.filter(course=>course.id===id)[0]
}
function createCourse(input){
var id = 4;
coursesData = [...coursesData, input.input];
console.log("input je" ,input.input)
console.log("coursesdata" ,coursesData)
//coursesData[id] = input;
return coursesData;
}
function deleteCourse(args){
var id=args;
coursesData.splice(id, 1);
return coursesData;
}
// The ApolloServer constructor requires two parameters: your schema
// definition and your set of resolvers.
const server = new ApolloServer({ typeDefs, resolvers });
// The `listen` method launches a web server.
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
On my localhost server http://localhost:4000 using graphql and this code I am trying to fetch data to get one specific course and to update course topic.
Get single course:
query getSingleCourse($courseID: Int !){
course(id:$courseID){
title
author
description
url
topic
}
}
{
"courseID": 3
}
Update course topic:
mutation updateCourse($id: Int!, $topic: String !) {
updateCourseTopic(id:$id, topic:$topic)
{
title
url
description
author
topic
}
}
{
"id": 1,
"topic": "something new"
}
The problem is with getSingleCourse is that
"errors": [
{
"message": "Cannot read property 'id' of undefined",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"course"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"TypeError: Cannot read property 'id' of undefined",
" at getcourse (/Users/admin/newProject/server4.js:116:17)",
with updateCourse is similar that id and topic are undefined
{ "data": {
"updateCourseTopic": null } }
You can look at image
Anyone help would be appreciated.
Thank you
A resolver can optionally accept four positional arguments: (parent, args, context, info).
In order to work just need to specify first argument like this:
function getcourse(_,args){
var id=args.id;
return coursesData.filter(course=>{
return course.id==id
})[0]
}
function updateCourseTopic (_,args){
coursesData.map(course=>{
if(course.id===args.id){
course.topic=args.topic;
return course
}
});
return coursesData.filter(course=>course.id===args.id)[0]
}
In My case on the input field i had extra , at the end for the JSON which caused issue. simple but took time to identify.
{
"input": {
"title": "Welcome to ReactjS",
"description": "react js is awesome for frontend web development",
"type": "frontend",
}
}
once i removed the , at the end of json problem resolved

Graphql query is always returning null

I am trying to get course data using graphql, but server is always returning null as a response This is my code in file server.js :
var express=require('express');
const { graphqlHTTP } = require('express-graphql')
var {buildSchema}=require('graphql');
//graphql schema
var schema=buildSchema(`
type Query {
course(id: Int!): Course
courses(topic:String!):[Course]
}
type Course {
id: Int
title: String
author: String
description:String
topic:String
url: String
}
`);
var coursesData = [
{
id: 1,
title: 'The Complete Node.js Developer Course',
author: 'Andrew Mead, Rob Percival',
description: 'Learn Node.js by building real-world applications with Node, Express, MongoDB, Mocha, and more!',
topic: 'Node.js',
url: 'https://codingthesmartway.com/courses/nodejs/'
},
{
id: 2,
title: 'Node.js, Express & MongoDB Dev to Deployment',
author: 'Brad Traversy',
description: 'Learn by example building & deploying real-world Node.js applications from absolute scratch',
topic: 'Node.js',
url: 'https://codingthesmartway.com/courses/nodejs-express-mongodb/'
},
{
id: 3,
title: 'JavaScript: Understanding The Weird Parts',
author: 'Anthony Alicea',
description: 'An advanced JavaScript course for everyone! Scope, closures, prototypes, this, build your own framework, and more.',
topic: 'JavaScript',
url: 'https://codingthesmartway.com/courses/understand-javascript/'
}
]
//root resolver
var root= {
course:getCourse,
courses:getCourses
};
var getCourse= function (args){
var id=args.id;
console.log("delila")
return coursesData.filter(course=>{
return course.id==id
})[0]
}
var getCourses = function(args){
if(args.topic){
var topic=args.topic;
return coursesData.filter(course=>
course.topic===topic
);
}
else return coursesData
}
//create an experess server and graphql endpoint
var app=express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue:root,
graphiql:true
}));
app.listen(4000,()=>console.log("delila express graphql server running on localhost 4000"))
When I go to localhost:4000/graphql to get data I am using
query getSingleCourse($courseID: Int !){
course(id:$courseID){
title
author
description
url
topic
}
}
{
"courseID": 3
}
But I am constantly getting result null. Look at image
Anyone idea why is happening this? Server should return course with id 3 but obviously there is something that I am missing
You should define function expression first and then use them. That's the reason.
Function expressions in JavaScript are not hoisted, unlike function declarations. You can't use function expressions before you create them:
See Function expression
E.g.
//...
var getCourse = function (args) {
var id = args.id;
console.log('delila');
return coursesData.filter((course) => {
return course.id == id;
})[0];
};
var getCourses = function (args) {
if (args.topic) {
var topic = args.topic;
return coursesData.filter((course) => course.topic === topic);
} else return coursesData;
};
//root resolver
var root = {
course: getCourse,
courses: getCourses,
};
//...

Cyclic schema giving error as "Error: Schema must contain unique named types but contains multiple types named "Node"."

I am new to 'GraphQL' using nodejs. I am stucked into bi-directional schema mapping. posts <-> authors. Using graphql and graphql-relay module.
Following are the two schema we are using.
--posts.js // here we are requiring authors.js
const {
AuthorType,
schema: AuthorSchema,
AuthorsConnection
} = require('./authors');
class Post {}
const {
nodeInterface,
nodeField
} = nodeDefinitions(
globalId => {
const {
type,
id
} = fromGlobalId(globalId);
// return based on the id
return DataSource['posts'][0];
},
obj => {
console.log(" : PostType : ", PostType);
// type to be return
return Post;
}
);
const PostType = new GraphQLObjectType({
"name": "PostType",
"description": "Posts type and it's relevant fields",
"fields": () => ({
"id": globalIdField('Post'),
"title": {
"type": GraphQLString
},
"body": {
"type": GraphQLString
},
"author": {
"type": AuthorsConnection,
"resolve": (parent, argument, root, currentSdl) => {
console.log("v1, v2, v3, v4 :", parent);
if (parent.author)
return connectionFromArray(DataSource['authors'], {})
return [];
}
}
}),
isTypeOf: Post,
interfaces: [nodeInterface]
});
const {
connectionType: PostsConnection,
edgeType: GQLPostEdge
} = connectionDefinitions({
name: "Post",
nodeType: PostType
});
module.exports = exports = {
PostType,
PostsConnection,
schema: {
post: nodeField,
posts: {
type: PostsConnection,
resolve: (root, v2, v3) => {
return connectionFromArray(DataSource['posts'], {});
}
}
}
};
--authors.js // here we have required posts.js
const {
PostType,
PostsConnection
} = require('./posts');
class Author {}
const {
nodeInterface,
nodeField
} = nodeDefinitions(
globalId => {
const {
type,
id
} = fromGlobalId(globalId);
// return based on the id
return DataSource['authors'][0];
},
obj => {
console.log(" : Authorype : ", Authorype);
// type to be return
return Author;
}
);
const AuthorType = new GraphQLObjectType({
"name": "AuthorType",
"description": "Author type and it's relevant fields",
"fields": () => ({
"id": globalIdField('Author'),
"firstName": {
"type": GraphQLString
},
"lastName": {
"type": GraphQLString
},
authorPosts: {
type: PostsConnection,
resolve: (parent, args, root, context) => {
return connectionFromArray(DataSource['posts'], {});
}
}
}),
isTypeOf: null,
interfaces: [nodeInterface]
});
const {
connectionType: AuthorsConnection,
edgeType: GQLAuthorEdge
} = connectionDefinitions({
name: "Author",
nodeType: AuthorType
});
module.exports = exports = {
AuthorType,
AuthorsConnection,
schema: {
author: nodeField,
authors: {
type: AuthorsConnection,
resolve: (root, v2, v3) => {
return connectionFromArray(DataSource['authors'], {});
}
}
}
};
Once I merge above schema for GraphQL I am getting following error.
Error: Schema must contain unique named types but contains multiple types named "Node".
I tried to debugged this issue, following is I observed following.
Once I change "authors" field from posts schema to other than
"AuthorsConnection" it starts working.
Or if removed "authors" field
from posts schema it starts working.
Please let me know what is issue here, is it relevant to nodeDefinitions function?
It is indeed related to the nodeDefinitions function. From the graphql-relay docs:
nodeDefinitions returns the Node interface that objects can implement, and returns the node root field to include on the query type. To implement this, it takes a function to resolve an ID to an object, and to determine the type of a given object.
You're calling this twice, which is resulting in the Node type being defined twice, and you're referencing one of each:
schema: {
post: nodeField,
// ...
schema: {
author: nodeField,
This is causing the error - there's now two independent instances of Node which is invalid.
The solution is to only call nodeDefinitions once, and then pass the reference to the generated nodeField and nodeInterface to the relevant places. Then your globalId => {...} function will need to look at the type to figure out how to get the relevant record, be it an author or a post.
Along with above answer given by #Benjie. I find out the way to overcome issues which was resulting into error of Error: Schema must contain unique named types but contains multiple types named "Node"..
Following are the key points to be check when we are making graphql in modular way.
Don't create new instances of type, For eg: const PostType = new GraphQLObjectType({}) it should always send single object rather than new object every time.
use nodeDefinations only once.
Check for the cyclic issues in common javascript issue which will occurs.
Thanks.

How should I write a resolver while using apollo graphql server backed by neo4j database?

I am using neo4j dB and I have set up apollo graphql server (using graphql-server-express). Lets say my schema has 3 types namely "Country", "State" and "People" where 1 country can have multiple states and 1 state can have multiple people.
//Sample schema.js
import { makeExecutableSchema } from 'graphql-tools';
import resolvers from './resolvers';
const typeDefs = `
type Country {
id: Int!
name: String
state: [State]
people: [People]
}
type State {
id: Int!
name: String
countryID: CountryID
people: [People]
}
type People {
id: Int!
name: String
SSN: String
stateid:StateID
countryid:CountryID
}
type Query {
Countries: [Country]
States: [State]
Peoples: [People]
}
schema {
query: Query
}
`;
export default makeExecutableSchema({
typeDefs: typeDefs,
resolvers,
});
So, how should I write my resolver function in resolver.js file such that it would help me to fetch the data properly from any of the above types ?
I tried to use the following query in resolver.js file (to query the Neo4j database using Cypher query language), but got the type error and i am unable to fix it.
//Sample resolver.js file.
let neo4j = require('neo4j-driver').v1;
let driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j",
"******"));
const resolver = {
Query: {
Countries(_, params) {
let session = driver.session();
let query = "MATCH (country:Country) RETURN country;"
return session.run(query, params)
.then( result => { return result.records.map(record => { return
record.get("country").properties })})
},
},
State:{
state(State) {
let session = driver.session(),
params = {countryid: Country.id},
query = `
MATCH (s:State-[:PRESENT]->(c:Country)
WHERE s.countryid = $countryid
RETURN s;
`
return session.run(query, params)
.then( result => { return result.records.map(record => { return
record.get("state").properties })})
},
},
};
export default resolver;

Resources