get value from firestore take time - node.js

The problem is that the value from the database is updated after the response to google action is finished , i have tried couple of option to make the function to wait for the value to update, but it doesn't work,
i need to execute twice to get the right valuelog
function operation(callback) {
dialogflowAgentDoc.doc(format).get()
.then(doc => {
console.log(doc.data().Url);
Url = doc.data().Url;
})
.catch((err) => {
console.log('Error getting documents', err);
});
callback(Url);
}
app.intent("Default Welcome Intent", conv => {
new GetFormat();
console.log("GetFormat Started");
new operation(function(Url) {
console.log("Ask Started");
conv.ask(
new SimpleResponse({
speech: "Playing Gallay Tzahal",
text: "Playing Gallay Tzahal"
}),
new MediaObject({
name: 'Gallay Tzahal News',
url: Url,
description: "text",
icon: new Image({
url: 'https://upload.wikimedia.org/wikipedia/he/thumb/3/30/GaltzLogo.svg/150px-GaltzLogo.svg.png',
alt: 'Media icon',
}),
})
);
});
conv.ask(suggestions3);
});

Loading data from the cloud takes take. To prevent blocking your app while waiting for that data, the Firestore (and most modern web APIs) load the data asynchronously and then call a function you pass into then() when the data is available.
This means that code that needs the data from Firestore must be inside the then() callback, which gets called when the data is available. So:
function operation(callback) {
dialogflowAgentDoc.doc(format).get()
.then(doc => {
console.log(doc.data().Url);
Url = doc.data().Url;
callback(Url);
})
.catch((err) => {
console.log('Error getting documents', err);
});
}

Related

Node GET by ID API

I have follow the instructions to create a NODE API here.
I'm trying to have a few endpoints with a NODE app to serve data to my React UI.
The database is mongodb where I have a collection for 'stores'.
I have 2 GET calls:
One to retrieve all stores
One to retrieve a store by ID
Node app.js:
app.get('/viewstores', (request, response) => {
storesCollection.find({}).toArray((error, result) => {
if (error) {
return response.status(500).send(error);
}
response.send(result);
});
});
app.get("/viewstores/:id", (request, response) => {
storesCollection.findOne({ "_id": new ObjectId(request.params.id) }, (error, result) => {
if(error) {
return response.status(500).send(error);
}
response.send(result);
});
});
I make my API calls from axios in React.
If I make a call to the first API to retrieve all stores, there no problem at all, but if I try to make the API call by ID, I still get all stores from the first API.
It seems that I am not able to target the GET by ID api.
React app
React.useEffect(() => {
axios.get('http://localhost:5000/viewstores', {
params: { _id: params.storesid}
})
.then(({data}) => {
console.log("DATA ==> ", data)
})
.catch(error => console.log("ERROR API GET ==> ", error))
}, [])
MongoDB store sample:
_id: ObjectId("12345")
businessname:"ABC"
businessaddress:"address abc 1"
Any idea why when I try to call the GET by ID I always get back the whole collection?
Thanks in advance.
Joe.
Assume params.storesid is 12345,
your current React code sends requests to http://localhost:5000/viewstores?_id=12345, and the route /viewstores is reached. To reach the /viewstores/:id route, the URL should be something likes http://localhost:5000/viewstores/12345 then Express will capture the 12345 part in the URL to request.params.id. You can try the code below:
React.useEffect(() => {
axios.get(`http://localhost:5000/viewstores/${params.storesid}`)
.then(({data}) => {
console.log("DATA ==> ", data)
})
.catch(error => console.log("ERROR API GET ==> ", error))
}, [])
You can read about Express route parameters in the official document.

Cannot access Firestore within a HTTP Trigger of Cloud Functions

I need to store my values from the request body to the cloud firestore and sent back the foruminsertdata.Name back in the response. But I am not able to do this.
const functions = require('firebase-functions');
const admin =require("firebase-admin");
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.helloWorld = functions.https.onRequest((req, res) => {
if(req.method === 'POST'){
foruminsertdata = req.body;
db.collection('forum').add({
Name: foruminsertdata.Name,
Description: foruminsertdata.Description,
Heading: foruminsertdata.Heading,
PostedOn: foruminsertdata.PostedOn,
Status: foruminsertdata.Status,
})
.then(ref => {
console.log('Added document with ID: ', ref.id);
return res.status(200).json(
{
message: foruminsertdata.Name
});
})
.catch(err => {
console.log('Error getting documents', err);
});
res.json({
message: foruminsertdata.Status,
});
}
})
I don't know what is happening...Whatever I do I always get the output as
{
message: foruminsertdata.Status,
}
in which "foruminsertdata.Status" has some value that I give
but what I expect the output as
{
message: foruminsertdata.Name
}
Your function is immediately returning foruminsertdata.Status to the client without waiting for the promise from the database operations to resolve. Any function that returns a promise is asynchronous and returns immediately. Execution will continue in the callbacks you attach to it.
I'm not sure why you have two calls to res.json() in your code, but if you want to send a response only after your query completes, you'll remove the second one and just send a response after the query is done. You will probably also want to send a response in the catch callback as well to indicate an error.

Cloud Function Not Returning Any Documents

Over the last couple of days my Cloud Functions have been failing for reasons I can't figure out. A basic query to fetch a document is not returning anything.
I've been through the documentation, and since this is such a simple query, I'm not sure where it could be failing. I've hardcoded the document reference number and I'm sure the document exists.
I'm only facing this issue from Cloud Functions. I'm able to query the table and return the document from a Flutter app I'm building.
exports.sendEventInvites = functions.https.onCall((data, context) => {
return sendEventInvites.send(data, context).then((result) => {
return result;
}).catch((error) => {
console.log(error);
return {
status: 500,
message: 'Failed to create event document',
}
});
});
function send(event, context) {
let db = admin.firestore();
return new Promise(resolve => {
db.collection(`users`).doc('hardcoded-value').get().then(doc => {
if (!doc.exists) {
console.log("Failed to fetch organizer document. Invites not sent");
return resolve({
status: 501,
message: "Failed to send invites"
});
}
// a bunch of code here that never gets executed because doc.exists is false
}).catch(error => {
console.log(error);
return resolve({
status: 503,
message: "Failed to send invites"
});
});
});
}
I'm not getting any errors, just no document. I've tried initializing admin both ways from the documentation:
admin.initializeApp({credential: admin.credential.cert(require('./secret-doc')), databaseURL: "db-url"});
and
admin.initializeApp(functions.config().firebase);
I'm using the following relevant npm modules:
"#google-cloud/firestore": "^2.2.4"
"firebase-admin": "^8.2.0"
"firebase-functions": "^3.1.0"
"firebase-tools": "^7.1.0"

Send response to client after Promise resolves - Adonis, Node.js

I have some promise
getSomeInfo(data) {
return new Promise((resolve, reject) => {
/* ...some code... */
someObject.getData((err, info) => {
if (info) {
resolve(info)
}
else {
reject("Error")
}
})
})
}
I use this promise and want to send response to client from Controller (AdonisJS):
async create ({ request, response }) {
this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
})
}
Why response is not work?
Simply do this.
async create ({ request, response }) {
const info = await this.getSomeInfo(data)
console.log(info)
response.status(201).json({
code: 201,
message: "Data received!",
data: info
})
}
When marking a function as async the function must return a Promise, this can be done explicitly.
async create({ request, response }) {
return this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
})
}
Or implicitly using the await keyword.
async create({ request, response }) {
const info = await this.getSomeInfo(data)
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
}
If your console.log(info) inside of create() works and shows the data you want, but the response.status(201).json(...) does not send a response, then I can see the following possibilities:
You've already sent a response to this request (and thus cannot send another one)
The .json() method is having trouble converting info to JSON (perhaps because of circular references) and throwing an exception.
You aren't passing the arguments request and response properly and thus response isn't what it is supposed to be.
You can test for the second case like this:
create ({ request, response }) {
this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
});
}).catch(e => {
console.log("Error in create()", e);
response.sendStatus(500);
});
}
Also, there is no reason for this method to be declared async as you don't show that you're using await or any of the features of an async function.
In the comments, you say that this function is called directly by a router (I assume an Express router). If that's the case, then the function arguments are not declared properly as they come as two separate arguments, not as properties of an object. Change the function declaration to this:
create (request, response) { ... }

How to properly render server-side errors in the client for Node/Express?

I've setup a simple Node/Express service that creates new bookmarks to a database.
In my Bookmark schema, one of the fields, url, has validations. When you try to create a new bookmark without the correct validations, it throws an error and a message.
E.g. if you don't provide a url to the request, then it will throw a message Please provide a url.
The route for the POST request to create a new bookmark:
app.post(
'/api/bookmarks',
(req, res) => {
const { url, tags } = req.body;
const bookmark = new Bookmark({
url,
tags
});
bookmark.save()
.then(res => {
res.send(result)
})
.catch(error => {
console.log("err:", error);
res.status(400).send(error)
});
}
);
What my Bookmark schema looks like:
const bookmarkSchema = new Schema({
url: {
type: String,
required: [true, "Please provide a URL."],
match: [urlRegex, "Your URL is invalid. Please make sure to add www."]
},
tags: [String]
});
When I make an API call on the front end client (e.g. via React), I am able to catch the error and subsequent message (Please provide a URL.), but as you can see, I have to call err.response.data.errors.url.message which seems very verbose and long winded.
axios.post('http://localhost:5000/api/bookmarks', { tags: ["banana", "orange"] })
.then(result => console.log('Result:', result.data))
.catch(err => console.log('Error:', err.response.data.errors.url.message));
What has me thinking - is there a better way to render errors here? Would it be better handle errors in the client side instead?
I'm quite new to error handling so would like to know what is considered best practice.
Thanks!
Something like this. So you don't have to check it in schema.
app.post(
'/api/bookmarks',
(req, res) => {
const { url, tags } = req.body;
if (!url) {
res.status(400).send({error: "URL is missing"})
return;
}
if (!url.match(urlRegex) {
res.status(400).send({error: "URL is not valid"})
return;
}
const bookmark = new Bookmark({
url,
tags
});
bookmark.save()
.then(res => {
res.send(result)
})
.catch(error => {
console.log("err:", error);
res.status(500)
});
}
);
It's a bad practice to send server side errors to frontend. It's a easy way to leak information about your database/backend. You should send your own string message instead of catched error.

Resources