Problem to call an API inside a Webhook - Javascript - javascript

I'm creating a chatbot using a Webhook in Javascript and in a moment I need to connect to my API(using a POST) to get some data and send the information received inside a message to the user. The problem here is the API is not been called and the flow just bypass my connection(I don't even see the connection in the API server).
I've run some testes with the API out of the Webhook and it worked.
I've tried to use async function and await but the application continues to "ignore" my API call.
Below is my code with my tests. I've made some changes with the URL just fo security purpose, but the logic is the same.
var price = 0
var payload = {'product_id': 'n/a'} **//Creating this global variable just for testing**
//CREATING THE FUNCTION TO CONNECT TO MY API USING AXIOS
function my_api(payload){
app.get("/", async(request, response) => {
const {data} = await axios.post('https://www.test.com/my_api', payload)
price = data.price
});
}
//FILLING MY TEST PAYLOAD
payload = {'product_id':10}
//CALLING THE API OUT OF THE WEBHOOK AND PRINTING THE RETURN ON MY WEBSITE CONSOLE
my_api(payload) **//If I call the API here, the application is accessed by the function. I confirmed that writing a response.send(price) command in the API function and check my API Server log**
//STARTING MY WEBHOOK
app.post("/webhook", async function (request, response) {
const agent = new WebhookClient({ request: request, response: response });
//THE FUNCTION WHERE THE API IS CALLED AFTER THE USER'S INPUT IN THE LAST INTENT AND I NEED TO WAIT IT RETURN WITH THE RESULT TO SEND THE MESSAGE
async function product_price(agent){
user_input = agent.query
var payload = {'product_id': user_input}
await my_api(payload) **//When I call the API function here it don't connect to my application**
agent.add('The price for this product is '+price)
}
}
Do you have any idea how can I connect to the API in the Webhook?
I've tried to create a function outside the Webhook and call the my_api(payload) function inside it but it didn't work.
Thank you for your attention.

You have a rest endpoint /webhook and in it's handler you create a function product_price but never execute it. To fix it you just have to call it like product_price() but in your code there is no need to create this function at all
//STARTING MY WEBHOOK
app.post("/webhook", async function (request, response) {
const agent = new WebhookClient({ request: request, response: response });
const user_input = agent.query
const payload = {'product_id': user_input}
await my_api(payload)
agent.add('The price for this product is '+price)
}
Your code has some weird stuff, that should also be improved: in my_api you create a rest endpoint for / but why? I guess you just want to make a request to get the price?
async function my_api(payload){
const {data} = await axios.post('https://www.test.com/my_api', payload)
price = data.price
}

Related

GET/POST methods on Express

I am configuring a server using express.
My question has nothing to do with the project itself because it is running great.
I just have a minor doubt about why I have to use GET when for me it makes more sense to use POST.
So, for short I am configuring an API key on the server side and fetching it on the client side so I can use it.
This is the snippet on the server side:
const apiKey = process.env.API_KEY;
console.log(`Your API key is ${apiKey}`);
const dataObject ={};
app.get('/api', (req,res) => {
res.send({key: apiKey})
})
app.get('/all', sendData = (req,res) => {
res.send(dataObject)
})
app.post('/addText', (req,res) => {
let newEntry = {
agreement = req.body.agreement,
subjectivity = req.body.subjectivity
}
dataObject = newEntry;
res.send(dataObject);
} )
And then on the client side I fetch on the '/api' path:
const getApiKey = async () => {
// Getting API key from server
const request = await fetch('/api');
try {
const data = await request.json();
console.log(data);
return data;
}catch(error) {
console.log('ERROR', error);
}
}
Ok, that's working and everything, but my question is:
On the first GET on the server side, I understand that I am sending the API key to the '/api' path so that I can retrieve this key with fetch on the client side. But if I am sending the api key to this path, why am I using GET and not POST?
Sorry if it seems a stupid question but I am having a hard time understanding the GET method.
Thanks!
You are not sending any API key to the server. The server is sending the API key to the client as a response. The client uses a GET request to get the API key from /api. The names of the methods (GET, POST, PUT, DELETE, ...) are from the perspective of the client.
"And then on the client side I fetch on the '/api' path:" No. First the client sends the request with
const request = await fetch('/api');
try {
const data = await request.json();
console.log(data);
return data;
}catch(error) {
console.log('ERROR', error);
}
This triggers the callback in
app.get('/api', (req,res) => {
res.send({key: apiKey})
})
and the server sends the response.
This code returns the API key from the server. It does not send it from the client to the server.
app.get('/api', (req,res) => {
res.send({key: apiKey})
}
The
res.send()
Function is constructing the response returned by the server to the client.
Usually, you use the GET method when the client has to read some data from the server, in this case, you want to read the API_KEY defined in the server. GET has no body, but every request may be parametric by passing parameters in the query string.
On the other hand, when you have to update or create an entity on the server, that's when POST (or PUT) enters into action. Here you pass your data in the body of the request.

Send 1 request, Receive 2 Responses GraphQL

I want to trigger a request from an Apollo Server during (and dependent on the data returned from) a request that originated from the client. I want to return the data that was originally requested by the client, and later return the data from the request originating from the server to the client.
SERVER:
async function getPlaylistItems(playlistId) {
const url = '... external api endpoint with query parameter playlistId=playlistId'
const response = await axios(url, { method: 'GET' });
// response.data = ['title1','title2',...]'
const secondUrl = '...another external endpoint with response.data as query parameters'
// create a new request (but don't wait for it to resolve) and return response.data to client
const secondRequest = axios(secondUrl, { method: 'GET' });
return response.data
}
CLIENT:
const playlist = getPlaylistItems('playlistId');
const secondRequestData = ...?
I want to receive the response from getPlaylistItems first, and send the response from secondRequest (on the server) back to the client once it resolves.
Here is a diagram of the desired data flow:
The features you are looking for is #defer directive. But right now it is under development and not possible to use it using apollo-server.
As far I know you there is no support available for returning partial response right now.
Or else you can use GraphQL Subscriptions if you really want to return the partial response.
More info about #defer

How to read a value from Realtime Databse using Cloud Functions?

I have a Cloud Functions, which triggers if a certain value in my Realtime Database changes. After that I need to read an other value from the Database.
I searched the web and found one solution. It worked: The function triggered as soon as the value at /ID/temp_id changed, but it took additional 5 seconds to read the value at /ID/I.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.getID = functions.database.ref("/ID/temp_id").onUpdate((change, context)=>{ //triggers the function when value "temp_id" changes
const tempID = change.after.val();
const i_snap = admin.database().ref("/ID/i").once("value", function(snapshot){ //read the value at "/ID/i" from the databse
const i = snapshot.val();
})
})
Is there any way to read the value at /ID/I faster?
In general, you cannot simply speed up a simple database write like that. Be aware that Cloud Functions have an unavoidable cold start time for the first invocation of your function on a new server instance.
I would actually not expect your function to work at all because you're not returning a promise that resolves when all the asynchronous work is complete in your function. You're obliged to do that so that your function terminates normally.
You have to trigger a function through an HTTP request by using functions.https. This allows invoke a synchronous function.
Use functions.https to create a function that handles HTTP events. The event handler for an HTTP function listens for the onRequest() event.
Used as arguments for onRequest(), the Request object gives you access to the properties of the HTTP request sent by the client, and the Response object gives you a way to send a response back to the client.
exports.date = functions.https.onRequest((req, res) => {
// ...
});
More details documentation: https://firebase.google.com/docs/functions/http-events
Take a look at the example below:
var functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
//Call function via HTTP requests. This allows invoke a synchronous function
exports.showValue = functions.https.onRequest((req, res) => {
const params = req.url.split("/");
const tempId = params[2];
return admin.database().ref('ID/' + tempId).once('value', (snapshot) => {
var value = snapshot.val();
res.send(`
<!doctype html>
<html>
<head>
<title>${value.name}</title>
</head>
<body>
<h1>Title ${value. name}, id ${value.id}</h1>
</body>
</html>`
);
});
});

Firebase callable functions response not working

I have a simple firebase functions script setup (running firebase-admin version 8.0 and firebase-functions version 2.3.1):
const functions = require('firebase-functions');
const cors = require('cors')({
origin: true,
});
//Gets and returns a user's ip address
exports.getIPAddress = functions.https.onRequest((req, res) => {
let ipAddress = req.headers['fastly-client-ip'] || req.connection.remoteAddress;
ipAddress = ipAddress.toString();
console.log('Fetched IP Address: ' + ipAddress);
return cors(req, res, () => {
res.status(200).send(ipAddress);
});
});
The function's goal is simply to return to user's IP address. It logs fine in the functions console, no errors.
Here is the client code:
var getIPAddress = mainFirebase.functions().httpsCallable('getIPAddress');
function testIP() {
getIPAddress().then(function(result) {
console.log(result.data.text)
});
}
However, the console says that 'result' is not a valid JSON object.
I've tried using https.onCall which somebody else on the internet recommended, however, the console says that function doesn't exist.
Any help getting the response to work properly would be greatly appreciated!
Your function is a regular HTTP type function. However, your client code is attempting to call it as if it were a callable type function. That's not going to work. If you want to invoke a callable type function, you'll have to implement the function according to the documentation.
If you need to keep the function as an HTTP type function, you can't use the Firebase client SDK to invoke it. Just invoke it as if it were any other type of HTTP endpoint.
For Callable functions. You need to create a function like:
exports.addMessage = functions.https.onCall(
async (data, context) => {
// context contains the user info.
}
);
And on your front-end you can call them like:
firebase.functions().httpsCallable('addMessage');
addMessage({text: messageText}).then(function(result) {
// Read result of the Cloud Function.
var sanitizedMessage = result.data.text;
}).catch(function(error) {
// Getting the Error details.
var code = error.code;
var message = error.message;
var details = error.details;
// ...
});
As you are calling an https message. You can also use the SDK to call https methods. But make sure you are handling CORS on your server.
In your client. Just use the http client.
this.http.post method with the function url.

Firebase: Calling Cloud Function From Cloud Function

I am running in to an issue with Firebase cloud functions. I have an onWrite cloud function that triggers a sequence of events. I have a path for requests that the onWrite cloud function is tied to. When that cloud function executes, it deletes the new request for the requests path and pushes the request in to a render path/que that will be used client side for rendering UI elements/data. Once the data has been written to the render path, I call a vanilla javascript function that is not tied to any cloud events. The vanilla javascript function is supposed to reach out to an external API and fetch some data to later be updated on the render object that was pushed in to the render path.
The problem is that the vanilla javascript function never executes. I have been looking all over the web to figure out why this happening but can't seem to figure out why. I am on the Flame plan so outbound api requests should be allowed to my knowledge. Here an example of my code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const request = require('request');
admin.initializeApp();
exports.requestModule = functions.database.ref('/requests').onWrite((change, context) => {
// Create reference to database
let db = admin.database();
if (context && context.auth && context.auth.uid) {
const afterData = change.after.val();
let uid = context.auth.uid;
let cleanData = afterData[uid];
cleanData.status = "loading";
// Remove the requested module from the requests path
let cleansePath = db.ref('/requests/' + uid);
cleansePath.remove().then((snapshot) => {
return true;
}).catch((error) => {
console.log(error);
return false;
});
// Add requested module to the render path
let renderPath = db.ref('/render/' + uid);
renderPath.push(cleanData).then((snapshot) => {
let val = snapshot.val();
let key = snapshot.key;
// Trigger the get weather api call
getWeather(uid, key, val);
return true;
}).catch((error) => {
console.log(error);
return false;
});
}
});
// Fetches data from external api
function getWeather (uid, key, obj) {
console.log('Fetching weather!');
let db = admin.database();
request('https://api.someweathersite.net/forecast/', (error, response, body) => {
if (!error && Number(response.statusCode) === 200) {
console.log('error:', error);
console.log('statusCode:', response && response.statusCode);
console.log('body:', body);
obj.data = body;
obj.status = 'loaded';
// Set data from api response in render object to be shown client side
let render = db.ref('/render/' + uid + '/' + key );
render.set(obj).then(() => {
return true;
}).catch((error) => {
console.log(error)
return false;
});
}
});
}
The console.log message at the top of the "getWeather" function never executes. I don't think that the "getWeather" function is ever executing.
If I put the api call directly in the onWrite "requestModule" function, the api call will work. However, when it calls an external function it never gets called/works. I basically want to have the "requestModule" function handle all requests and plan to have a module dispatcher that handles which module function/api data should be fetched from. That's why I don't want to keep the api call in the "requestModule" function. Any idea of why this happening or how I can get this working?
getWeather is performing asynchronous work to fetch some data, but it's not returning a promise to indicate when that work is complete. In fact, none of the async work you're performing here is correctly using the promises returned by the various API calls. It's not sufficient to simply use then() on each promise.
You need to keep track of all of the async work, and return a single promise that resolves only after all the work is complete. Otherwise, Cloud Functions may terminate and clean up your function before the work is complete. (Note that it's not deterministic which work may or may not actually complete before forced termination, but the only way to ensure that all work completes is through that single promise you return.)
You may want to watch my tutorials on using promises in Cloud Functions to get a better handle on what you're required to do make your functions work correctly: https://firebase.google.com/docs/functions/video-series/

Categories

Resources