I am building an app on an a marketplace in which I receive webhooks from the app and then make api calls to perform certain actions.
I am using the Hubspot API for most functions and the Stripe API to collect payment and verify payment status. The entire code is written in Node JS and I am running the express node js library to listen to endpoints and deal with requests.
This is the setup of my express
const express = require('express')
const app = express()
app.use(express.raw({type: "application/json"})
app.use(express.json())
I am using express.raw because of the stripe API which requires the raw request to be called into some kind of Buffer.
Now this is the call for an example endpoint for a non-Stripe call, such as getting account settings for Hubspot
app.post('/account-fetch', (req, res) => {
const portalId = req.body.portalId
console.log(portalId)
setup.retrieveKey(portalId, "hubname")
.then((acc) => {
console.log(acc)
const component = {
"response": {
"accounts": [{
"accountId": '"' + portalId + '"',
"accountName": acc
}]
}
}
console.log(component)
res.status(200).send(component)
})
.catch((err) => {
console.error(err)
res.status(400).send(err)
})
})
Now the issue is that while stripe needs it to have that express.raw call, it messes up everything else, because the Buffer is encoded and it just comes out as numbers.
I've also tried on the stripe endpoint:
app.post('/stripe/:type', express.raw({type: "application/json }),(req, res) => {
//rest of my code
})
or not calling it at all, still no cigar. All I get is a StripeSignatureVerificationError because it can't read the signature on the payload properly.
I've tried placing that line just before the stripe endpoint, but it doesn't work.
Is there a way to either to convert the Buffer back into what it would be if I didn't do the express.raw call or a way to just make that call isolated for one endpoint?
Ok I found the answer to this. It was to use Routers
Essentially I did this:
const stripeApp = express.Router()
stripeApp.use(express.raw({type:"*/*"}))
app.use('/stripe',stripeApp)
and then for the endPoint:
stripeApp.post('/:type', express.raw({type: "application/json"}),(req, res) => {
//my code here
})
Both Hubspot and Stripe APIs started working.
Related
I'm writing an Express + Typescript application that, among other things, has an API endpoint which allows the user to search for a track. The endpoint receives a track name as a URL parameter, requests an access token from Spotify, uses the access token to query the Spotify Web API for matching tracks, then returns the data in JSON format. My problem is that I'm using the spotify-web-api-node library to interact with the API and so far I've only been able to make it work by requesting a new access token each time the route is accessed. I've tried adding middleware to request the access token elsewhere so it doesn't need to be done in this specific route, but it seemingly executes the route before the middleware, thus executing the API call without first getting the access token. Here is my working solution so far
import { Router, Request, Response, NextFunction } from 'express';
import SpotifyWebApi from 'spotify-web-api-node';
const spotifyRouter = Router();
const spotifyApi: SpotifyWebApi = new SpotifyWebApi({
clientId: /* My client ID */,
clientSecret: /* My client secret */
})
spotifyRouter.get('/search/track/:trackName', (req: Request, res: Response) => {
spotifyApi.clientCredentialsGrant()
.then(data => spotifyApi.setAccessToken(data.body['access_token']))
.then(() => { return spotifyApi.searchTracks(req.params.trackName) })
.then(data => res.json(data.body))
.catch(error => res.json(error));
})
export default spotifyRouter;
So to reiterate, I'm wondering how I can improve the management of the access token so it doesn't have to get a new one each time the above route is used.
Thanks.
EDIT: I forgot to mention that this is using Spotify's Client Credentials Flow
On my express server I make queries to an external API using its own token. When I log in to my server I request a token to the external API based on the user who logged in, and I keep the token of the external API in the token of my express server.
Each user gets different data according to their token from the external api, for queries that require external API information, I read the received token and get the external API token to send it through headers with axios, for example:
const LoginUser = (request, response) {
axios.post('/ExternalApi/auth',request.body)
.then( data =>{
const payload = {
...
tokenExternalApi: data.token
}
const token = jwt.sign(payload, ...)
return response.status(200).json(token)
})
}
const getData = (req, response){
const tokenFromClient = req.headers.authorization
//Function extract tokenExternalApi from payload Token
const tokenExternalApi = getTokenExternl(tokenFromClient )
axios.get(`/urlExternalApi`, { headers:
{ Authorization: tokenExternalApi }}
).then(res => {
return response.status(200).json(res.data)
})
}
Is this the correct approach to managing external apis tokens or is there a cleaner way to do it?
Here is my sample code that I use for hit an external API within function in node js using axios
first time you should install axios npm install axois
const axios = require('axios');
async yourFunction(){
axios({
method: 'POST',
url: "http://yoururl.com",
data: {
name: '+62'+phoneNumber,
number: '+62'+phoneNumber,
message: 'success',
}
});
}
In my personal opinion, this seems to be a clean approach.
But keep in mind that tokens are visible to users, so the fact is your users can decode the token, view tokenExternalApi, know that you are using an external API in the backend and directly make calls to ExternalApi using that token, provided they have the know-how of it. If you understand this fact and are fine with it, then this works.
Otherwise, you can consider encoding the token before sending it to the user or store it on the server-side session.
I have a route in express app like this:
router.get('/search_documents_for_home_page', async (req, res) => {
var responses = [];
await Article.find({}).select('image title body').limit(4).sort({ _id:-1 }).then(articles=>{
responses.push([articles]);
});
await Image.find({}).limit(4).sort({ _id:-1 }).then(images=>{
responses.push([images]);
});
await Video.find({}).limit(4).sort({ _id:-1 }).then(videos=>{
responses.push([videos]);
});
await Project.find({}).limit(4).sort({ _id:-1 }).then(projects=>{
responses.push([projects]);
});
res.json(responses);
});
And when the user goes to the home page, a fetch request is sended:
await fetch('/api/search_documents_for_home_page').then(result=>{
return result.json();
}).then(articles=>{
// show the users all of the documents
});
But I want that only my server can fetch this url.
How do I do that?
Im also using pugjs
You can secure your api by requiring some type of authentication
You can add a check to make sure request is coming from your front end, depending on server this can be handled differently (i.e. window.location.origin)
Enable CORS, only prevents browser>browser calls
I'm hosting a Twilio app on Heroku. I want Twilio to start a separate outbound call and put the user into a conference, if the user selects a certain menu option.
Here is my handler.js:
const VoiceResponse = require('twilio').twiml.VoiceResponse;
const client=require('twilio')(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN
);
var request = require('request');
exports.menu = function menu(digit,sid) {
var responseTwiml;
switch(digit){
case '1':
console.log("menu: chose 1");
responseTwiml=guestCallsHost(sid);
break;
default:
doSomethingElse();
break;
}
return responseTwiml;
};
function guestCallsHost(sid){
baseUrl='/ivr/callHost';
console.log("guestCallsHost: baseUrl "+baseUrl);
conferenceName=sid;
url='/ivr/callHost?conferenceName='+sid;
var call=client.calls.create({
url:url,
to: process.env.CELL_PHONE_NUMBER,
from: process.env.TWILIO_PHONE_NUMBER,
method: 'GET'
});
const response = new VoiceResponse();
const dial = response.dial();
dial.conference(sid);
responseStr=response.toString();
return responseStr;
};
exports.callHost=function callHost(conferenceName){
const response=new VoiceResponse();
response.say("testing outbound call to host");
return response.toString();
};
And here is the router.js that defines the /ivr/ endpoints:
const Router = require('express').Router;
const {
menu,
callHost,
} = require('./handler');
const router = new Router();
// GET: /ivr/menu
router.get('/menu', (req, res) => {
const digit = req.query.Digits;
const sid=req.query.sid;
console.log("/ivr/menu: digit "+digit);
console.log("/ivr/menu: sid "+sid);
res.send(menu(digit,sid));
});
// GET: /ivr/callHost
router.get('/callHost', (req, res) => {
console.log("reached callHost endpoint");
const conferenceName=req.query.conferenceName;
res.send(callHost(conferenceName));
});
The problem is that the client.calls.create() call is not being made, but no error appears in either the Twilio debugger or the Heroku log. And the /ivr/callHost endpoint is not being visited, despite it being set as the url for client.calls.create(). I know that client has been successfully instantiated as a Twilio object (i think) because when I list all its methods using:
console.log(Object.getOwnPropertyNames(client));
then all the expected Twilio API methods are listed. Or maybe that just means that the object has been created but hasn't been successfully connected to Twilio using the Account SID and Auth Token?
I built this by modifying the app at https://github.com/TwilioDevEd/ivr-phone-tree-node, and I'm able to successfully use client.calls.create() to launch a phone call in another app where I call it in the same index.js file that I'm actually running to start the node app, so maybe in this case the handler.js file isn't actually able to successfully submit the Account SID and Auth Token from where it is in the app structure?
So I'm confused, why can't I make an outbound phone call from inside handler.js? I can transfer inbound calls, so clearly I can modify a live call.
EDIT: I checked whether client is able to make API calls, using:
client
.calls(sid)
.fetch()
.then(call => console.log("call.to "+call.to)).catch(function(error){
console.log("error: "+error.toString());
});
The current inbound call's TO number was successfully retrieved, so apparently client has been instantiated and can connect to Twilio's server.
I solved the problem. It turns out that client.calls.create() requires a full URL with hostname, not just a relative URL, per the documentation for making calls.
I changed:
baseUrl='/ivr/callHost';
to
baseUrl='https://appurl.com/ivr/callHost';
and now the outbound call is successfully generated.
I'm new to node.js, I have a salesforce web to lead form. I want to use express.Router() to post the form to salesforce. How do i format the
router.post( '/Contact', function(req,res,next) {
var body = req.body;
});
body? The form action = 'https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8'
and for example the OID = '1111111' ** I will use process.env.ORG_ID to get the real org ID.
Express isn't used for making a request but rather is a library for making Web APIs/Apps. If you need to make a request then you could use request which is very popular and wraps the Node Core API HTTP Library function, http.request().
Using request you can make your POST request to salesforce as such
let orgId = process.env.ORG_ID
request({
method: 'POST',
url: `https://webto.saleforce.com/servlet/servlet.WebToLead?encoding=UTF-8&OID=${orgid}`
}, (err, res, body) => {
if (err) {
return err
}
// Handle res/body per needs
})