GraphQL with RESTful returning empty response - node.js

I am connecting GraphQL with REST endpoints, I confirmed that whenever I am calling http://localhost:3001/graphql it is hitting REST endpoint and it is returning JSON response to GraphQL server, but I am getting an empty response from GraphQL server to GUI as follows:
{
"data": {
"merchant": {
"id": null
}
}
}
Query (decoded manually):
http://localhost:3001/graphql?query={
merchant(id: 1) {
id
}
}
Below is how my GraphQLObjectType looks like:
const MerchantType = new GraphQLObjectType({
name: 'Merchant',
description: 'Merchant details',
fields : () => ({
id : {
type: GraphQLString // ,
// resolve: merchant => merchant.id
},
email: {type: GraphQLString}, // same name as field in REST response, so resolver is not requested
mobile: {type: GraphQLString}
})
});
const QueryType = new GraphQLObjectType({
name: 'Query',
description: 'The root of all... queries',
fields: () => ({
merchant: {
type: merchant.MerchantType,
args: {
id: {type: new GraphQLNonNull(GraphQLID)},
},
resolve: (root, args) => rest.fetchResponseByURL(`merchant/${args.id}/`)
},
}),
});
Response from REST endpoint (I also tried with single object in JSON instead of JSON array):
[
{
"merchant": {
"id": "1",
"email": "a#b.com",
"mobile": "1234567890"
}
}
]
REST call using node-fetch
function fetchResponseByURL(relativeURL) {
return fetch(`${config.BASE_URL}${relativeURL}`, {
method: 'GET',
headers: {
Accept: 'application/json',
}
})
.then(response => {
if (response.ok) {
return response.json();
}
})
.catch(error => { console.log('request failed', error); });
}
const rest = {
fetchResponseByURL
}
export default rest
GitHub: https://github.com/vishrantgupta/graphql
JSON endpoint (dummy): https://api.myjson.com/bins/8lwqk
Edit: Adding node.js tag, may be issue with promise object.

Your fetchResponseByURL function get empty string.
I think the main problem is that you are using wrong function to get the your JSON string, please try to install request-promise and use it to get your JSON string.
https://github.com/request/request-promise#readme
something like
var rp = require('request-promise');
function fetchResponseByURL(relativeURL) {
return rp('https://api.myjson.com/bins/8lwqk')
.then((html) => {
const data = JSON.parse(html)
return data.merchant
})
.catch((err) => console.error(err));
// .catch(error => { console.log('request failed', error); });
}

In this case using data.merchant solved my problem. But the above suggested solution i.e., use of JSON.parse(...) might not be the best practice because if there are no object in JSON, then expected response might be as follows:
{
"data": {
"merchant": null
}
}
Instead of fields to be null.
{
"data": {
"merchant": {
"id": null // even though merchant is null in JSON,
// I am getting a merchant object in response from GraphQL
}
}
}
I have updated my GitHub: https://github.com/vishrantgupta/graphql with working code.

Related

How to update Elasticsearch dynamic data multiple fields using UpdateByQuery in NodeJS

How to update Elasticsearch data multiple fields using UpdateByQuery in NodeJS ?
Note - My data is coming dynamically. I can't pass static value. I have to pass like - data.name, data.id
Code -
function updateInELK(data) { // Update by Id
const updateScript = {
inline: {
"ctx._source.name = "+data.name,
"ctx._source.role = "+data.role,
};
return new Promise((resolve, reject) => {
elasticsearch.updateByQuery({
index: indexName,
body: {
query: { match: { id: data.id } },
script: updateScript,
lang: 'painless',
}
}).then((response) => {
resolve(response);
}).catch((err) => {
console.log(err);
reject("Elasticsearch ERROR - data not updated")
})
});
}
Error -
TypeError: "ctx._source.name = " is not a function
Please let me know, if any other options are there. I can't update using id, because I don't know the id. I wanted to use updateByQuery, with conditions in the query parameters.
Here are the solutions -
await esClient.updateByQuery({
index: "data",
type: "doc",
refresh: true,
body:{
query:{
match: {
dataId: "70897e86-9d69-4700-b70e-881a7f74e9f9"
}
},
script:{
lang:"painless",
source:`ctx._source.data='This is updated test data';ctx._source.updatedAt=params.date;ctx._source.segmentId=params.segmentId`,
params:{
date: Date.now(),
segmentId: null
}
}
}
});

passing object to graphql resolver shows object as undefined

I'm new to graphql and am working on this issue for a while. My frontend uses React.js and Apollo Client, and backend is Node.js with Apollo GraphQL and Prisma. I am trying to create a simple mutation that will add a recipe.
Here is my react component code:
const [addRecipe] = useMutation(CREATE_RECIPE)
const addRecipeClick = () => {
const recipeData = {
title: 'Crinkle Cookies',
author: 'Adina', ingredients: [{ quantity: '1 c', name: 'cocoa' }], link: '', imageURL: ''
}
addRecipe({ variables: { recipe: recipeData } })
.then(() => {
enqueueSnackbar('Recipe Added!', { variant: 'success' })
})
.catch(() => enqueueSnackbar(`Unable to add recipe`, { variant: 'error' }))
}
apollo client query:
export const CREATE_RECIPE = gql`
mutation($recipe: RecipeInput!) {
createRecipe(recipe: $recipe){
recipes {
title
author
ingredients {
quantity
name
}
}
}
}`
backend schema:
type Mutation {
createRecipe(recipe: RecipeInput!): RecipeResults
}
input RecipeInput {
title: String!,
author: String!,
ingredients: [Ingredient]!,
link: String,
imageURL: String
}
backend resolver:
const createRecipe = async (recipe: RecipeInput) => {
console.log('createRecipe resolver: recipe ', recipe) // undefined
try {
const result = await prisma.recipe.create({
data: recipe
})
const recipes = await prisma.recipe.findMany()
return { recipes }
}
catch (e) {
console.log('error: ', e)
return {}
}
}
When I log out 'recipeData' on the frontend, console shows its value is set.
However, when I log out 'recipe' in createRecipe() resolver, it's undefined. Can't figure out why.

How to make a request to post an array to the server?

I need to execute a request to send an array. But when I do this I get null as the result.
Result Request Payload:
[
{nom_name: "Test", cost: 500, quantity: 1, parentOrder_id: 19},
{nom_name: "35634", cost: 100, quantity: 1, parentOrder_id: 19}
]
Result Preview:
{
address: null
apartment: null
code: null
cost: null
date_delivery: null
floor: null
hierarchyLevel: 1
is_new: true
name: null
nom_name: null
order_id: 40
parentOrder_id: null
payment: null
quantity: null
telephone: null
}
How do I fix to prevent null values ​​from being sent to the server for the values ​​I am sending? And so that the post request sends exactly an array, and not a single object.
service:
create(order: Order[]): Observable<Order> {
return this.http.post<Order>(`${env.url}/api/orders/create`, order);
}
ts:
onSave() {
const order: Order[] =
this.order.list.map(e => {
const { order_id, ...rest } = e
return ({ ...rest, parentOrder_id: 19 })
})
this._order.create(order)
.subscribe(
() => {
console.log('Data Saved.')
},
error => {
console.log(error)
}
);
}
On the server I am using Nodejs and ORM Sequelize.
node.js controller:
module.exports.create = function (req, res) {
const order = db.orders.bulkCreate([
{
nom_name: req.body.nom_name,
quantity: req.body.quantity,
cost: req.body.cost,
parentOrder_id: req.body.parentOrder_id
}
])
res.status(201).json(order);
};
I haven't personally worked a lot with sequelize, but I'm pretty sure that you need to pass the the request-body object directly since req.body contains the array of data and thus req.body.nom_name for example will be undefined. Also bulkCreate is defined as an async-function that you should await before sending the response:
module.exports.create = async (req, res) => {
const orders = await db.orders.bulkCreate(req.body);
res.status(201).json(orders);
};

NestJS HttpService All Method API Call is not working when Interceptor is used

NestJS API App use HttpService to call Another API and It works when no custom interceptor is used.
The HttpService API call is executed but it is not reached to another API and could not see the response.
This is get call code
get(path: string, body: any = {}): Observable<AxiosResponse<any>> {
console.log('DomainAPI Response Begining');
const url = this.baseURL + path;
this.updateLastUtilizedTimestamp();
return this.httpService.get(url, { validateStatus: null }).pipe(
tap(data => {
console.log('DomainAPI Response Tap', data);
}),
retryWhen(
this.rxJSUtilsService.genericRetryStrategy({
numberOfAttempts: 3,
delayTime: 200,
ignoredErrorCodes: [500],
}),
),
catchError(this.formatErrors),
);
}
if any custom interceptor is used, I found the following when debug.
arguments:TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
The Postman shows the following response.
{
"statusCode": 500,
"message": {
"Title": "TypeError",
"Type": "Error",
"Detail": "Converting circular structure to JSON\n --> starting at object with constructor 'Observable'\n | property 'operator' -> object with constructor 'CatchOperator'\n --- property 'caught' closes the circle",
"Status": "Status",
"Extension": ""
},
"timestamp": "Exception - AllExceptionsFilter 2019-12-26T17:29:42.447Z"
}
I changed the above as below but still it is not working
get(path: string, body: any = {}) {
console.log('DomainAPI Response Begining');
const url = this.baseURL + path;
this.updateLastUtilizedTimestamp();
return this.httpService.get(url, { validateStatus: null }).pipe(
map(response => {
console.log('DomainAPI Response Tap', response.data);
return response.data;
}),
);
}
It gives the following response in the postman
{
"data": {
"_isScalar": false,
"source": {
"_isScalar": false,
"source": {
"_isScalar": false
},
"operator": {}
},
"operator": {}
}
}
Please advise
async get(path: string, body: any = {}): Promise<any> {
...
const res = await this.httpService.get(url, { validateStatus: null }).toPromise();
return res.data;
}
get(path: string, body: any = {}) {
...
return this.httpService.get(url, { validateStatus: null })
.toPromise()
.then(res => res.data)
.catch(err => this.logger.error(err));
}
Converting to a promise using the .toPromise() method has been deprecated because we are working with observables and a promise returns only one value while observables could return no value or more that one value (see more #: Conversion to Promises). So as an example using the question above the solution would be:
import { lastValueFrom } from 'rxjs';
get(path: string, body: any = {}): Observable<AxiosResponse<any>> {
console.log('DomainAPI Response Begining');
const url = this.baseURL + path;
this.updateLastUtilizedTimestamp();
return await lastValueFrom(
this.httpService.get(url, { validateStatus: null })
.pipe(
tap(data => {
console.log('DomainAPI Response Tap', data);
}),
retryWhen(
this.rxJSUtilsService.genericRetryStrategy({
numberOfAttempts: 3,
delayTime: 200,
ignoredErrorCodes: [500],
}),
),
catchError(this.formatErrors),
));
}
Basically all you have to do is wrap the whole http request with firstValueFrom or lastValueFrom, in this case lastValueFrom is what would be appropriate. Visit the link to the documentation on Conversion to promises above by rxjs to know more about this.
NOTE: I added just the lastValueFrom import only as a clear indicator of what is needed.
This is another example using my own code:
async send_otp(
phone: string,
channel: string,
): Promise<Observable<AxiosResponse<any>>> {
return await lastValueFrom(
this.httpService
.post(`/messaging/otp/`, {
sender: 'Oyinkayy',
destination: '234' + String(phone).slice(1),
length: 6,
channel: channel,
reference: '',
})
.pipe(
tap((resp) => console.log(resp)),
map((resp) => {
return resp.data;
}),
tap((data) => console.log(data)),
catchError(async (err) => {
return err;
}),
),
);
}

How to update a model? .updateAttributes is not a function

I'm building a Node Express app, with Postgres as DB and Sequelize as ORM.
I have a router.js file:
router.route('/publish')
.put((...args) => controller.publish(...args));
controller.js which looks like this:
publish(req, res, next) {
helper.publish(req)
.then((published) => {
res.send({ success: true, published });
});
}
And a helper.js
publish(req) {
return new Promise((resolve, reject) => {
Article.findAll({
where: { id: req.query.article_id },
attributes: ['id', 'state']
})
.then((updateState) => {
updateState.updateAttributes({
state: 2
});
})
.then((updateState) => {
resolve(updateState);
});
});
}
So for example when I hit PUT http://localhost:8080/api/publish?article_id=3555 I should get:
{
"success": true,
"published": [
{
"id": 3555,
"state": 2
}
]
}
The current state of the article is 1.
However, I get the following error Unhandled rejection TypeError: updateState.updateAttributes is not a function. When I remove the updateState.updateAttributes part from my helper.js I get the response with the current state.
How do I update the state of the article correctly?
You should just change findAll with findOne , as you are just trying to find specific article by id :
Article.fineOne({ //<--------- Change here
where: { id: req.query.article_id },
attributes: ['id', 'state']
})
.then((updateState) => {
updateState.updateAttributes({state: 2}); //<------- And this will work
})
But if you still want to go with findAll and to know how to use that , Please try this and read the comments , that will clear all your doubts :
Article.findAll({
where: { id: req.query.article_id },
attributes: ['id', 'state']
})
.then((updateState) => {
// updateState will be the array of articles objects
updateState.forEach((article) => {
article.updateAttributes({ state: 2 });
});
//-------------- OR -----------------
updateState.forEach((article) => {
article.update({ state: 2 });
});
})

Resources