How to use query parameter in Graphiql tool - node.js

I am using zomato api to make an restaurant review app with React, node and Graphql. As per the docuemnt, I am passing query parameters, but in graphiql tool I am getting 'Not defined'.
Here is the error:
graphiql tool error
Here is the api documentation reference:
curl -X GET --header "Accept: application/json" --header "user-key: abc" "https://developers.zomato.com/api/v2.1/restaurant?res_id=2"
My query is how can we test the api using query parameters in graphiql tool? Also let me know how can we update the query parameter in api to catch user input and correspondingly show the result.
Below is the code for schema.js which defined graphql schema
const axios = require("axios");
const {
GraphQLObjectType,
GraphQLString,
GraphQLInt,
GraphQLBoolean,
GraphQLList,
GraphQLFloat,
GraphQLSchema
} = require("graphql");
const RestaurantType = new GraphQLObjectType({
name: "Restaurant",
fields: () => ({
res_id: {
type: GraphQLInt
},
name: {
type: GraphQLString
},
url: {
type: GraphQLString
},
photos_url: {
type: GraphQLString
},
menu_url: {
type: GraphQLString
},
rating: {
type: UserRatingType
},
location: {
type: LocationType
}
})
});
const UserRatingType = new GraphQLObjectType({
name: "UserRating",
fields: () => ({
aggregate_rating: {
type: GraphQLFloat
}
})
});
const LocationType = new GraphQLObjectType({
name: "Location",
fields: () => ({
address: {
type: GraphQLString
},
locality: {
type: GraphQLString
},
zipcode: {
type: GraphQLInt
}
})
});
const userKey = 'abc';
const RootQuery = new GraphQLObjectType({
name: "RootQueryType",
fields: {
restaurants: {
type: new GraphQLList(RestaurantType),
resolve(parent, args) {
return axios
.get(`https://developers.zomato.com/api/v2.1/restaurant`, {
headers: {
'user-key': {userKey}
}})
.then(res => res.data)
.catch((error) => {
console.log('error is ' + error);
});
}
},
restaurant: {
type: RestaurantType,
args: {
res_id: {
type: GraphQLInt
}
},
resolve(parent, args) {
return axios
.get(`https://developers.zomato.com/api/v2.1/restaurant?
res_id`, {
params: {
res_id
},
headers: {
'user-key': {
userKey
}
}
})
.then(res => {
console.log(res.data)})
.catch((error) => {
console.log('error is ' + error);
});
}
}
}
});
module.exports = new GraphQLSchema({
query: RootQuery
});

Related

I have this error Unauthorised admin to execute command mongoose + Graphql

I used mongoose and Graphql to send my queries to the database but for some reason it doesn't let me create documents. I have tried creating a new user with full admin privileges it hasn't worked I tried changing the default user password but it didn't work.
I rechecked my mongoose model no errors so what might be the problem.
FYI the problem arose with the return (author.save()) and the database connects normally
Author Model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const authorSchema = new Schema({
name: String,
age: Number
});
module.exports = mongoose.model('Author', authorSchema);
schema.js
const graphql = require('graphql');
const Book = require('../models/book');
const Author = require('../models/Author');
const _ = require('lodash');
const {
GraphQLObjectType,
GraphQLString,
GraphQLSchema,
GraphQLID,
GraphQLInt,
GraphQLList
} = graphql;
const BookType = new GraphQLObjectType({
name: 'Book',
fields: ( ) => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
genre: { type: GraphQLString },
author: {
type: AuthorType,
resolve(parent, args){
//return _.find(authors, { id: parent.authorId });
}
}
})
});
const AuthorType = new GraphQLObjectType({
name: 'Author',
fields: ( ) => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
age: { type: GraphQLInt },
books: {
type: new GraphQLList(BookType),
resolve(parent, args){
//return _.filter(books, { authorId: parent.id });
}
}
})
});
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
book: {
type: BookType,
args: { id: { type: GraphQLID } },
resolve(parent, args){
//return _.find(books, { id: args.id });
}
},
author: {
type: AuthorType,
args: { id: { type: GraphQLID } },
resolve(parent, args){
//return _.find(authors, { id: args.id });
}
},
books: {
type: new GraphQLList(BookType),
resolve(parent, args){
//return books;
}
},
authors: {
type: new GraphQLList(AuthorType),
resolve(parent, args){
//return authors;
}
}
}
});
const Mutation = new GraphQLObjectType({
name: 'Mutation',
fields: {
addAuthor: {
type: AuthorType,
args: {
name: { type: GraphQLString },
age: { type: GraphQLInt }
},
resolve(parent, args){
let author = new Author({
name: args.name,
age: args.age
});
return (author.save())
}
}
}
});
module.exports = new GraphQLSchema({
query: RootQuery,
mutation: Mutation
})
;
error message
(node:31482) MongoError: (Unauthorized) not authorized on admin to execute command {
insert: "authors", documents: [[{name gyfdgyiszukjfheusdzyih} {age 88} {_id
ObjectID("60af9c682215ea7afad86f4c")} {__v 0}]], ordered: false, writeConcern: { w:
"majority" }
Found this issue, after trying practice by GraphQL tutorial on Youtube.
To solve it, you need to update your mongoose model to the last version.

Graphql execute where condition only if the argument is passed

I am using graphql in node js for my oracle database wherein I connect to the remote database and fetch some details. I am fairly new to these technologies so please pardon me. I have a customer table with below schema:
const Customer = new GraphQLObjectType({
description: 'Customer data schema',
name: 'Customer',
fields: () => ({
name: {
type: GraphQLString,
sqlColumn: 'NAME',
},
city: {
type: GraphQLString,
sqlColumn: 'CITY'
},
country: {
type: GraphQLString,
sqlColumn: 'COUNTRY'
},
gender: {
type: GraphQLString,
sqlColumn: 'GENDER'
},
emp_id: {
type: GraphQLString,
sqlColumn: 'EMP_ID'
}
})
});
Customer._typeConfig = {
sqlTable: 'CUSTOMER',
uniqueKey: ['NAME','EMP_ID']
}
Using join monster I create my Query root as:
const QueryRoot = new GraphQLObjectType({
description: 'global query object',
name: 'RootQuery',
fields: () => ({
customer: {
type: new GraphQLList(Customer),
args: {
emp_id: {
description: 'Emp Id',
type: GraphQLString
},
name: {
description: 'Customer Name',
type: GraphQLString
}
},
where: (customer, args, context) => {
return `${customer}."EMP_ID" = :emp_id AND ${customer}."NAME" = :name`;
},
resolve: (parent, args, context, resolveInfo) => {
return joinMonster(resolveInfo, context, sql => {
console.log('joinMaster', sql);
return database.simpleExecute(sql, args,{
outFormat: database.OBJECT
});
});
}
}
})
})
When I pass my query in graphql in browser with emp_id and name parameters I get data. But there are cases when I cannot pass any parameters and would want all the rows to be fetched.
When I do not send the parameters I get error as:
ORA-01008 : Not all variables bound
I want the arguments to be optional, and if I don't send them then it should return all rows.
Thank you.
Both the where and resolver functions are passed an args argument. This will have the parameter names and values if any. You can use that argument to build a dynamic where clause. Here's an untested example:
const QueryRoot = new GraphQLObjectType({
description: 'global query object',
name: 'RootQuery',
fields: () => ({
customer: {
type: new GraphQLList(Customer),
args: {
emp_id: {
description: 'Emp Id',
type: GraphQLString
},
name: {
description: 'Customer Name',
type: GraphQLString
}
},
where: (customer, args, context) => {
if (Object.keys(args).length === 0) {
return false;
}
let whereClause = '1 = 1';
if (args.emp_id != undefined) {
whereClause += `\n AND ${customer}."EMP_ID" = :emp_id`;
}
if (args.name != undefined) {
whereClause += `\n AND ${customer}."NAME" = :name`;
}
return whereClause;
},
resolve: (parent, args, context, resolveInfo) => {
return joinMonster(resolveInfo, context, sql => {
console.log('joinMaster', sql);
return database.simpleExecute(sql, args,{
outFormat: database.OBJECT
});
});
}
}
})
})
Since the where clause would then match the number of arguments, you shouldn't get the ORA-01008 error.

How to refactor GraphQL schema using "graphql-compose-elasticsearch" to tranform GraphQL query into Elasticsearch query?

I was hosting my data using json server on localhost:3000(REST api) and using GraphQL to fetch data, and now I'd like to move data into a Elastic Search server, and I still want to use GraphQL as API gateway. I tried this graphql-compose-elasticsearch library to use GraphQL as a proxy for ElasticSearch.
https://github.com/graphql-compose/graphql-compose-elasticsearch
In my original GraphQL schema, I defined types, root query, resolvers. The code is like this:
const graphql = require('graphql');
const axios = require('axios');
const {
GraphQLObjectType,
GraphQLList,
GraphQLID,
GraphQLInt,
GraphQLString,
GraphQLBoolean,
GraphQLSchema,
GraphQLNonNull
} = graphql;
const RoomType = new GraphQLObjectType({
name: 'RoomType',
fields: () => ({
id: { type: GraphQLID },
roomName: { type: GraphQLString },
roomNumber: { type: GraphQLString },
floorId: { type: GraphQLInt },
hasImages: { type: GraphQLBoolean },
hasGLTF: { type: GraphQLBoolean },
hasPhotogrammetry: { type: GraphQLBoolean },
hasPointClouds: { type: GraphQLBoolean },
roomDescription: { type: GraphQLString },
floor: {
type: FloorType,
resolve(parentValue, args) {
return axios
.get(`http://localhost:3000/floors/${parentValue.floorId}`)
.then((resp) => resp.data);
}
},
assets: {
type: new GraphQLList(AssetType),
resolve(parentValue, args) {
return axios
.get(`http://localhost:3000/rooms/${parentValue.id}/assets`)
.then((resp) => resp.data);
}
}
})
});
const FloorType = new GraphQLObjectType({
name: 'FloorType',
fields: () => ({
id: { type: GraphQLID },
floorName: { type: GraphQLString },
floorDescription: { type: GraphQLString },
rooms: {
type: new GraphQLList(RoomType),
resolve(parentValue, args) {
return axios
.get(`http://localhost:3000/floors/${parentValue.id}/rooms`)
.then((resp) => resp.data);
}
}
})
});
const AssetType = new GraphQLObjectType({
name: 'AssetType',
fields: () => ({
id: { type: GraphQLID },
category: { type: GraphQLString },
assetName: { type: GraphQLString },
assetNumber: { type: GraphQLString },
roomId: { type: GraphQLString },
location: { type: GraphQLString },
isHeritageAsset: { type: GraphQLBoolean },
hasImages: { type: GraphQLBoolean },
hasGLTF: { type: GraphQLBoolean },
hasPhotogrammetry: { type: GraphQLBoolean },
hasPointClouds: { type: GraphQLBoolean },
assetDescription: { type: GraphQLString },
room: {
type: RoomType,
resolve(parentValue, args) {
return axios
.get(`http://localhost:3000/rooms/${parentValue.roomId}`)
.then((resp) => resp.data);
}
}
})
});
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
getRoom: {
type: RoomType,
args: { id: { type: new GraphQLNonNull(GraphQLID) } },
resolve(parentValue, { id }) {
return axios.get(`http://localhost:3000/rooms/${id}`).then((resp) => resp.data);
}
},
getFloor: {
type: FloorType,
args: { id: { type: new GraphQLNonNull(GraphQLID) } },
resolve(parentValue, { id }) {
return axios.get(`http://localhost:3000/floors/${id}`).then((resp) => resp.data); //to make it compatible between axios and graphql, a workaround
}
},
getAsset: {
type: AssetType,
args: { id: { type: new GraphQLNonNull(GraphQLID) } },
resolve(parentValue, { id }) {
return axios.get(`http://localhost:3000/assets/${id}`).then((resp) => resp.data); //to make it compatible between axios and graphql, a workaround
}
},
getAllRooms: {
type: new GraphQLList(RoomType),
resolve() {
return axios.get(`http://localhost:3000/rooms`).then((resp) => resp.data);
}
},
getAllAssets: {
type: new GraphQLList(AssetType),
resolve() {
return axios.get(`http://localhost:3000/assets`).then((resp) => resp.data);
}
},
getAllFloors: {
type: new GraphQLList(FloorType),
resolve() {
return axios.get(`http://localhost:3000/floors`).then((resp) => resp.data);
}
}
}
});
//expose this to the rest of the application
module.exports = new GraphQLSchema({
query: RootQuery
});
For Elasticsearch, the version is 7.0. I'm running on localhost:9200, and I have 3 indices, rooms, floors, and assets, and each of the index have a mapping. I tried to code like this:
const graphql = require('graphql');
const graphql_compose_elasticsearch = require('graphql-compose-elasticsearch');
const { elasticApiFieldConfig, composeWithElastic } = graphql_compose_elasticsearch;
const { GraphQLSchema, GraphQLObjectType } = graphql;
const elasticsearch = require('elasticsearch');
// Mapping obtained from ElasticSearch server
const floor_mapping = {
properties: {
floorId: {
type: 'text',
fields: {
keyword: {
type: 'keyword',
ignore_above: 256
}
}
},
hasGLTF: {
type: 'boolean'
},
hasImages: {
type: 'boolean'
},
hasPhotogrammetry: {
type: 'boolean'
},
hasPointClouds: {
type: 'boolean'
},
roomDescription: {
type: 'text',
fields: {
keyword: {
type: 'keyword',
ignore_above: 256
}
}
},
roomName: {
type: 'text',
fields: {
keyword: {
type: 'keyword',
ignore_above: 256
}
}
},
roomNumber: {
type: 'text',
fields: {
keyword: {
type: 'keyword',
ignore_above: 256
}
}
}
}
};
const room_mapping = {
//similar
};
const asset_mapping = {
//similar
};
const Room = composeWithElastic({
graphqlTypeName: 'RoomType',
elasticIndex: 'rooms',
elasticType: '_doc',
elasticMapping: room_mapping,
elasticClient: new elasticsearch.Client({
host: 'http://localhost:9200',
apiVersion: '7.0'
})
});
const Floor = composeWithElastic({
graphqlTypeName: 'FloorType',
elasticIndex: 'floors',
elasticType: '_doc',
elasticMapping: floor_mapping,
elasticClient: new elasticsearch.Client({
host: 'http://localhost:9200',
apiVersion: '7.0'
})
});
const Asset = composeWithElastic({
graphqlTypeName: 'AssetType',
elasticIndex: 'assets',
elasticType: '_doc',
elasticMapping: asset_mapping,
elasticClient: new elasticsearch.Client({
host: 'http://localhost:9200',
apiVersion: '7.0'
})
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
roomSearch: Room.getResolver('search').getFieldConfig(),
roomSearchConnection: Room.getResolver('searchConnection').getFieldConfig(),
elastic70: elasticApiFieldConfig({
host: 'http://localhost:9200',
apiVersion: '7.0'
})
}
})
});
module.default = { schema };
server.js is like this:
const graphqlHTTP = require('express-graphql');
const { schema } = require('./schema/schema_es');
// const schema = require('./schema/schema');
var cors = require('cors');
const server = require('express')();
//allow cross-origin
server.use(
'/',
cors(),
graphqlHTTP({
schema: schema,
graphiql: true,
formatError: (error) => ({
message: error.message,
stack: error.stack.split('\n')
})
})
);
const PORT = process.env.PORT || 6060;
server.listen(PORT, () => console.log(`Server started on port ${PORT}`));
I restarted the servers, and here is the error I got
invariant.esm.js:31 [GraphQL error]: Message: GraphQL middleware options must contain a schema., Location: undefined, Path: undefined
I have no idea how to how to transfrom graphql schema, rootquery..etc into ES query using this graphql-compose-elasticsearch, any help is appreciated!

GraphQL Mongoose promise best practices

Using GraphiQL, I'm able to update a user with the following command (1).
I'm able to return the actual user with the service (3).
Is there simpler/more compact way of returning the user after an update? (seen how easy it is when creating a user).
(1) GraphiQL mutation command
mutation {
updateUser(id: "1", firstName: "Bob"){
firstName
}
}
(2) the mutation:
const mutation = new GraphQLObjectType({
updateUser: {
type: UserType,
args: {
id: { type: new GraphQLNonNull(GraphQLString) },
firstName: { type: GraphQLString },
age: { type: GraphQLInt },
companyId: { type: GraphQLString }
},
resolve(parentValue, args){
return UserService.updateUser(args);
}
},
})
(3) the service:
function updateUser(args) {
var {id} = args;
return User.findOne({id})
.then(user => {
if(!user){throw new Error('No user found')}
return User.update({id}, args)
.then(() => {
return User.findOne({id})
})
})
},
function addUser(args) {
const user = new User(args);
return user.save(args);
}
Assuming you're using mongoose:
function updateUser(args) {
var {id} = args;
return await User.findOneAndUpdate({ id }, args, { new: true });
},

GraphQL.js Node/Express: How to pass object as GraphQL query argument

My goal is to be able to pass an object as an argument in a GraphQL query.
Goal:
{
accounts (filter:
{"fieldName": "id",
"fieldValues":["123"],
"filterType":"in"}){
id
}
}
Error:
"message": "filterType fields must be an object with field names as keys or a function which returns such an object."
I've tried a few different approaches but this seems to be the closest to the potential solution.
Schema:
const filterType = new GraphQLObjectType ({
name: 'filterType',
fields: {
fieldName: { type: GraphQLString },
fieldValues: { type: GraphQLString },
filterType: { type: GraphQLString },
}
})
const QueryType = new GraphQLObjectType({
name: 'Query',
fields: () => ({
accounts: {
type: new GraphQLList(accountType),
args: {
filter: { type: new GraphQLInputObjectType(filterType) },
},
resolve: (root, args, { loaders }) => loaders.account.load(args),
},
}),
});
I have found the solution here.
https://github.com/mugli/learning-graphql/blob/master/7.%20Deep%20Dive%20into%20GraphQL%20Type%20System.md#graphqlinputobjecttype
Schema:
const filterType = new GraphQLInputObjectType({
name: 'filterType',
fields: {
fieldName: { type: GraphQLString },
fieldValues: { type: GraphQLString },
filterType: { type: GraphQLString },
}
})
const QueryType = new GraphQLObjectType({
name: 'Query',
fields: () => ({
accounts: {
type: new GraphQLList(accountType),
args: {
filter: { type: filterType },
},
resolve: (root, args, { loaders }) => {
return loaders.account.load(args)},
},
}),
});
Problem was in the query, I had the both the keys and values as strings in the object argument.
Correct Query:
{
accounts(filter: {fieldName: "id", fieldValues: "123", filterType: "in"}) {
id
}
}
You don't define filterType as an object type then wrap it in an input type, you literally create it as an input type:
const filterType = new GraphQLInputObjectType({
name: 'filterType',
fields: {
fieldName: { type: GraphQLString },
fieldValues: { type: GraphQLString },
filterType: { type: GraphQLString },
}
})
const QueryType = new GraphQLObjectType({
name: 'Query',
fields: () => ({
accounts: {
type: new GraphQLList(accountType),
args: {
filter: { type: filterType },
},
resolve: (root, args, { loaders }) => loaders.account.load(args),
},
}),
});
You'll also want to declare its type at query time, as illustrated in #piotrbienias's answer.

Resources