I am using graphql with node js on oracle db to fetch some data. I have two tables customer and sub_customer with the below schemas:
const Customer = new GraphQLObjectType({
description: 'Customer data schema',
name: 'Customer',
fields: () => ({
name: {
type: GraphQLString,
sqlColumn: 'NAME',
},
city: {
type: GraphQLString,
sqlColumn: 'CITY'
},
region: {
type: GraphQLString,
sqlColumn: 'REGION'
},
country: {
type: GraphQLString,
sqlColumn: 'COUNTRY'
},
gender: {
type: GraphQLString,
sqlColumn: 'GENDER'
},
emp_id: {
type: GraphQLString,
sqlColumn: 'EMP_ID'
},
sub_id: {
type: new GraphQLList(Subcustomer),
sqlJoin: (Subcustomer, Customer, args) => `${Subcustomer}.REGION_CODE = ${Customer}.REGION`
}
})
});
Customer._typeConfig = {
sqlTable: 'CUSTOMER',
uniqueKey: ['NAME','EMP_ID']
}
const Subcustomer = new GraphQLObjectType({
description: 'Subcustomer data schema',
name: 'Subcustomer',
fields: () => ({
sub_customer_id: {
type: GraphQLString,
sqlColumn: 'SUB_CUSTOMER_ID',
},
region_code: {
type: GraphQLString,
sqlColumn: 'REGION_CODE'
},
customer: {
type: new GraphQLList(Customer),
args: {
emp_id: {
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" = '${args.emp_id}'`;
}
return whereClause;
},
sqlJoin: (Subcustomer, Customer, args) => `${Subcustomer}.REGION_CODE = ${Subcustomer}.REGION`
}
})
});
Subcustomer._typeConfig = {
sqlTable: 'SUB_CUSTOMER',
uniqueKey: 'REGION_CODE'
}
And this is my Query:
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
});
});
}
},
sub_customer: {
type: new GraphQLList(Subcustomer),
args: {
user_id: {
description: 'User Id',
type: GraphQLString
},
},
where: (user, args, context) => {
if (Object.keys(args).length === 0) {
return false;
}
let whereClause = '1 = 1';
if (args.user_id != undefined) {
whereClause += `\n AND ${user}."SUB_CUSTOMER_ID" = :user_id`;
}
return whereClause;
},
resolve: (parent, args, context, resolveInfo) => {
return joinMonster(resolveInfo, context, sql => {
console.log('joinMaster', sql);
return database.simpleExecute(sql, args,{
outFormat: database.OBJECT
});
});
}
}
})
})
I get correct results when I execute:
{
sub_customer(user_id: "123") {
sub_customer_id
customer(emp_id: "456") {
name
}
}
}
For getting count of records with static where clause I do:
sqlExpr: Customer => `(SELECT count(*) FROM CUSTOMER WHERE EMP_ID = '456')`
This gives me correct count. However I want the count of all the results returned when where clause is applied by the user and it could be dynamic.
I am using join monster as the library to fetch records. I could not find any way to execute this aggregation with dynamic where clauses.
Thank you.
The sqlExpr function is passed more than a single parameter. You have access to: tableAlias, args (for this field), context, and sqlASTNode:
https://join-monster.readthedocs.io/en/latest/API/#sqlExpr
The sqlASTNode has a parent property (not enumerable) that allows you to get the args of the parent field (Customer in your case). So you could make this field more dynamic too. Here's an example that I've not tested.
sqlExpr: (table, args, context, sqlASTNode) => {
let expr;
if (sqlASTNode.parent && sqlASTNode.parent.args && sqlASTNode.parent.args.emp_id != undefined) {
expr = '(SELECT count(*) FROM CUSTOMER WHERE EMP_ID = :emp_id)';
} else {
expr = 'null';
}
return expr;
}
Related
I've been going back and forth on this code for sometime now and I'm trying to have a totalQty value in the cart object that returns total number of items in the cart and I want to use that value in the views of course right next to the cart icon in the navigation. Here is my code for the user model and routes:
User model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
role: {
type: String,
default: 'BASIC'
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
address: {
type: String
},
apartment: {
type: String
},
country: {
type: String
},
state: {
type: String
},
city: {
type: String
},
zip: {
type: String
},
phone: {
type: String
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
resetToken: String,
resetTokenExpiration: Date,
cart: {
items: [
{
productId: {
type: Schema.Types.ObjectId,
ref: 'Product',
required: true
},
quantity: { type: Number, required: true }
},
],
totalQty: {
type: Number,
default: 0
}
}
}, { timestamps: true });
userSchema.methods.addToCart = function (product) {
const cartProductIndex = this.cart.items.findIndex(cp => {
return cp.productId.toString() === product._id.toString();
});
let newQuantity = 1;
// let newTotalQty = 1;
const updatedCartItems = [...this.cart.items];
if (cartProductIndex >= 0) {
newQuantity = this.cart.items[cartProductIndex].quantity + 1;
updatedCartItems[cartProductIndex].quantity = newQuantity;
newTotalQty = this.cart.totalQty + 1;
updatedTotalQty = newTotalQty;
} else {
updatedCartItems.push({
productId: product._id,
quantity: newQuantity
});
}
const updatedCart = {
items: updatedCartItems,
totalQty: updatedTotalQty
};
this.cart = updatedCart;
return this.save();
};
userSchema.methods.removeFromCart = function (productId) {
const updatedCartItems = this.cart.items.filter(item => {
return item.productId.toString() !== productId.toString();
});
this.cart.items = updatedCartItems;
return this.save();
};
userSchema.methods.clearCart = function () {
this.cart = { items: [] };
return this.save();
};
module.exports = mongoose.model('User', userSchema);
User routes:
exports.getCart = (req, res, next) => {
// populate req user
req.user
.populate('cart.items.productId')
.execPopulate()
.then(user => {
const products = user.cart.items;
// render cart view
res.render('shop/cart', {
path: '/cart',
pageTitle: 'Cart - Hashing365.com',
products: products
});
})
.catch(err => {
const error = new Error(err);
error.httpStatusCode = 500;
return next(error);
});
};
exports.postCart = (req, res, next) => {
// extract prod ID
const prodId = req.body.productId;
// run DB find with prod ID
Product.findById(prodId)
.then(product => {
// return true && add to cart
return req.user.addToCart(product);
})
.then(result => {
// re-render same page
res.redirect('back');
})
.catch(err => {
const error = new Error(err);
error.httpStatusCode = 500;
return next(error);
});
};
Would really appreciate if someone could help me with a way to do that. Thanks!
You can look into Array reducer function. It should look like this
cart.totalQty = cart.items.reduce((sum, item)=>{
return sum + item.quantity;
},0);
I've tried to isolate this example and I hope it's ok. I know, this isn't great code, but I hope you get the drift.
For the time being the resolvers return a static result object.
Here's my problem:
The result of the company resolve function should be passed on the user's resolve function. But that ain't happenin' and I wonder what I am missing.
const GraphQL = require('graphql');
const UserType = new GraphQL.GraphQLObjectType({
name: 'User',
fields: {
givenName: { type: GraphQL.GraphQLString },
familyName: { type: GraphQL.GraphQLString },
city: { type: GraphQL.GraphQLString },
},
});
const CompanyType = new GraphQL.GraphQLObjectType({
name: 'Company',
fields: {
legalName: { type: GraphQL.GraphQLString },
city: { type: GraphQL.GraphQLString },
employees: { type: new GraphQL.GraphQLList(UserType) },
},
});
const queryDef = new GraphQL.GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: UserType,
args: {
id: { type: GraphQL.GraphQLID },
givenName: { type: GraphQL.GraphQLString },
familyName: { type: GraphQL.GraphQLString },
city: { type: GraphQL.GraphQLString },
},
resolve: (parent, args, context, info) => {
console.log('parent should provide company object', parent);
// currentyl parent is undefined
return {
id: 10,
givenName: 'test',
};
},
},
company: {
type: CompanyType,
args: {
id: { type: GraphQL.GraphQLID },
},
resolve: (parent, args, context, info) => {
return {
id: 3,
legalName: 'legal test name',
city: 'company location',
};
},
},
},
});
const schema = new GraphQL.GraphQLSchema({ query: queryDef });
const companyQuery = `
{
company(id: 1) {
city
employees {
familyName
}
}
}`;
GraphQL.graphql(schema, companyQuery).then( (companyResult) => {
console.log(companyResult);
} ).catch( (err) => {
console.error(err);
});
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
});
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.
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 });
},