This isn’t a specific issue question, but I’m trying to wrap my head around the concept of REST APIs and calling an API within your own API.
For example, if I develop an App called “BeesBees”, where users can buy bees, and I have a database of logins and passwords and obviously the bees, and how much each cost, I understand that I can used my own apps BeesBees API to get the list of bees (and if I make it open for other people, they can also use my GET /bees point to get, well, the bees)
But if I want to allow customers to buy the bees, and I don’t want to build a whole platform for doing so, so I integrate Stripe - could I have a POST /:users/charges/:priceOfBee API call that, in turn, called the Stripe API function somewhere somehow? For example, pointing to the URL of a Node.js project that will make the charge using Stripe’s Node.js SDK.
Or, in a case like this, would it be better to just implement the Stripe SDK for my device’s platform and make the charge using the device?
I have no code base so there’s nothing to pick apart, but I couldn’t think of anywhere else to ask, sorry y’all
You can certainly wrap APIs with other APIs, it's really just a form of composition, delegate to other services where it makes sense to do so.
Stripe integration might well be a good example of where it could make sense to follow this pattern. You certainly don't want to roll your own payment service.
Here's a trivial example of a local Express server making an API request:
const express = require("express");
const port = 3000;
const app = express();
const request = require('request');
app.get("/api/example", (req, res) => {
// Make external REST request here...
let options = {
url: 'https://httpbin.org/get',
qs: { parameter1: 42 },
json: true
}
request(options, (err, response, body) => {
if (err) {
res.status(500).send(err.message);
} else {
res.status(200).send(body);
}
});
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('running at http://' + host + ':' + port)
});
console.log(`Serving at http://localhost:${port}`);
Related
I built an app with Vuejs which is hosted on firebase, I recently added dynamic rendering with rendertron to improve SEO, I'm hosting the rendertron on Heroku. The rendertron client work well.
In order to send requests coming from bots like googlebot to rendertron and recieve a compiled HTML file, I used firebase function, it checks for the user agent, if it's a bot then it sends it to the rendertron link, if it's not, it fetches the app and resend result.
Here's the function code:
const functions = require('firebase-functions');
const express = require('express');
const fetch = require('node-fetch');
const url = require('url');
const app = express();
const appUrl = 'khbich.com';
const renderUrl = 'https://khbich-render.herokuapp.com/render';
function generateUrl(request){
return url.format({
protocol:request.protocol,
host:appUrl,
pathname:request.originalUrl
});
}
function detectBot(userAgent){
let bots = [
"googlebot",
"bingbot",
"facebookexternalhit",
"twitterbot",
"linkedinbot",
"facebot"
]
const agent = userAgent.toLowerCase()
for(let bot of bots){
if(agent.indexOf(bot)>-1){
console.log('bot-detected',bot,agent)
}
}
}
app.get('*', (req,res)=>{
let isBot = detectBot(req.headers['user-agent']);
if(isBot){
let botUrl= generateUrl(req);
fetch(`${renderUrl}/${botUrl}`)
.then(res => res.text())
.then(body=>{
res.set('Cache-Control','public','max-age=300','s-maxage=600')
res.set('Vary','User-Agent');
res.send(body.toString())
})
}
else{
fetch(`https://${appUrl}`)
.then(res=>res.text())
.then(body=>{
res.send(body.toString())
})
}
});
I used the function as an entry point for firebase hosting, so it's invoked whenever someone enters the app.
I checked on the firebase dashboard to see if it's working, and I noticed that it crashed for exceeding the number of requests per 100 second quota, I don't have much users when I checked, and the function invocations reached 370 calls in one minute.
I don't see why I had a large number of calls all at once, I'm thinking that maybe since I'm fetching the website if the user agent is not a bot, then the function is re-invoked causing an infinite loop of invocations, but I don't know if that's really why ?
If it's an infinite loop, how can I redirect users to their entered url without reinvoking the function ? will a redirect work ?
I am using actionssdk and I build my Action fulfilments using Javascript and node.js + Express.
I am looking for a way to get the url (protocol + host name + port) of the server where the fulfilment is hosted.
Is there a simple way to do this? E.g. in the MAIN intent? Is there some conv-property I can use? Can I get hold of a req-parameter in the MAIN-intent, from which I can deduct hostname etc?
const express = require('express');
const expressApp = express();
const { actionssdk, ... } = require('actions-on-google');
const app = actionssdk({
ordersv3: true,
clientId: ...
});
expressApp.post('/fulfilment', app);
app.intent('actions.intent.MAIN', (conv) => {
let myUrl: string = ... // ???????
...
});
(background: obviously I know myself to where I deployed my fulfilment code. But I have a reusable template for fulfilment code in which I want to refer to the host url, and I do not want to type that in manually each time I develop a new Action).
You can get access to the request object in a middleware via Framework Metadata which is by default of type BuiltinFrameworkMetadata which contains objects used by Express
For example, you can use it like this, which will be ran before each request:
app.middleware((conv, framework) => {
console.log(framework.express.request.headers.host)
})
I have made a web application with a simple API. The code for the front-end and and the API are both served from the same host. The front end consumes the API by making basic http requests. While developing, I have been making these requests within the front-end using port 3000 from the locally run server.
What is the best way to do this on a production server (An AWS EC2 instance)?
How do I easily generalize this in the development code so I don't have to change it from
axios.get("localhost:3000" + otherParams)
.then(response => {
//use the response to do things
});
})
to
axios.get("http://99.999.999.999:80" + otherParams)
.then(response => {
//use the response to do things
});
})
every time I push an update to the live server? Is this just something that web developers have to put up with? Sorry if this is a dumb question..
We definitely don't have to put up with changing our code like that every time! (Thank the coding gods)
So I think what you are after is environment variables
For example: You could setup an environment variable called SERVER_URL
Then when you are running locally that variable is localhost:3000 but when you deploy to amazon it can be set to http://99.999.999.999:80
in node you consume the variable like this
process.env.WHATEVER_YOUR_VARIABLE_NAME_IS
So in your case it would be
axios.get(process.env.SERVER_URL + otherParams)
a popular module to help create these variables is dotenv, which is worth looking at.
As a little bonus answer to help (and hopefully not confuse you too much), axios lets you create your own instance of axios so that you don't have to repeat yourself. Their example is
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
So you could do something like
const api = axios.create({
baseURL: process.env.SERVER_URL
});
then you can replace your axios calls with your new instance of axios (api)
Something like this.
api.get(otherParams)
Hope that makes some sense and gets you back on track!
You can create a config.js file:
var configs = {};
configs.appPort = 3000;
configs.host = '192.168.99.100';
module.exports = configs;
Importing the configure file:
var configs = require('./config');
Axios:
axios.get(configs.host + ":" + configs.appPort + "/" + otherParams)
.then(response => {
//use the response to do things
});
})
You can also create environment variables like this:
configs.isProduction = false;
configs.localHost = "localhost";
configs.productionHost = "192.168.99.100";
And then you can check in your app if it is production, use productionHost, otherwise, use localHost.
I have successfully created demo project where i can control an infrared transmitter using the Amazon Echo Alexa.
Moving forward with my project I'm not sure what the best practices would be in relation to performance and mostly important security. I will explain the project below and elaborate on the issues:
Installed nodejs server on a Raspberry pi running on port 1234
Installed web_lirc to be able to have an nodejs api interface to LIRC
Created an AWS-lambda skill based on the HelloWorld nodejs template with my own simple "hack" working but not pretty :) See code snippet below:
var http = require('http');
var host = '12.34.56.78'; // (no http/https !)
var port = 3000;
var cmd = '';
function performMacroRequest(endpoint, data)
{
cmd = '/macros/' + endpoint;
//console.log('cmd: ' + cmd);
var options = {
host : host,
port : port,
path : cmd, // the rest of the url with parameters if needed
method : 'POST'
};
http.request(options, function(res)
{
console.log('performMacroRequest - STATUS: ' + res.statusCode);
res.on('data', function (chunk)
{
console.log(cmd + ': '+ chunk);
});
}).end();
}
//
var APP_ID = "protected by me"; // Amazon Alexa hardware ID
HelloWorld.prototype.intentHandlers = {
// register custom intent handlers
"HelloWorldIntent": function (intent, session, response)
{
response.tellWithCard("Hello World!", "Hello World", "Hello World!");
},
"IRIntent": function (intent, session, response)
{
performMacroRequest('TESTTV','');
},
"AMAZON.HelpIntent": function (intent, session, response) {
response.ask("You can say hello or cake to me!", "You can say hello or cake to me!");
}
};
The issues as i see them, but are not sure how to address:
The best and most secure way to control my Raspberry web service from AWS. What would be the best option to control external hardware, is that using a webservice and what about protection?
Currently i need to have the port open in my router, so basically everyone with access to my IP could control my raspberry using JSON POST/GET commands. What could be a potential solution, is that to add an overlaying web interface with password protection?
Is it possible to have Alexa talking directly with my hardware on LAN without going trough AWS Lambda?
Overall i think I'm asking for the best practices(technically/security) on having Alexa to access local nodejs server.
Please let me know if anything has to be elaborated or explained in more details.
/Thomas
I'm trying to get a Skype bot up and running based off of the echo example but I'm struggling to make a successful POST to my app. When I send a post to /v1/chat I get back a status of 201 (successful creation), and nothing in the body. My console.log does not print anything either, which leads me to believe that the botService.on('personalMessage', ...) function is not being run. Does anyone have any insight into how these POST requests should be formatted? I cannot seem to find anything in the documentation.
My code:
const fs = require('fs');
const restify = require('restify');
const skype = require('skype-sdk');
const botService = new skype.BotService({
messaging: {
botId: '28:<bot’s id="ID176db9ab-e313-4d76-a60c-bc2a280e9825">',
serverUrl : "https://apis.skype.com",
requestTimeout : 15000,
appId: process.env.APP_ID,
appSecret: process.env.APP_SECRET
}
});
botService.on('contactAdded', (bot, data) => {
console.log('contact added');
bot.reply('Hello ${data.fromDisplayName}!', true);
});
botService.on('personalMessage', (bot, data) => {
console.log('message incoming');
console.log(data);
bot.reply('Hey ${data.from}. Thank you for your message: "${data.content}".', true);
});
const server = restify.createServer();
server.post('/v1/chat', skype.messagingHandler(botService));
const port = process.env.PORT || 8080;
server.listen(port);
console.log('Listening for incoming requests on port ' + port);
Final Edit & Solution: I think the problem caused by Heroku somehow(it could be something with their free tier ,1 dyno). After some effort, I uploaded the program to Azure, and it is now working perfectly.
Potential Solution: You need to change the server address in the server.post command. If you run your program in "https:\www.yourwebsite.com/v1/chat" , you need to modify this;
server.post('/v1/chat', skype.messagingHandler(botService));
to this;
server.post('https:\\www.yourwebsite.com/v1/chat', skype.messagingHandler(botService));
Of course, don't forget to specify your app id, bot id, and app secret. If you don't have one, you need to generate a password in your Skype application page.
I have the exact problem with the OP. I followed the tutorial, and it doesn't specify how to modify our code to comply with our server. So, after running the program it only returns this;
{"code":"ResourceNotFound","message":"/ does not exist"}
In the Echo example in the Skype Bot Webpage; it says;
"We'll assume the bot's URL for messaging was set to https://echobot.azurewebsites.net/v1/chat during registration."
Make sure that Procfile and worker processes are setup.
My bot is working fine on heroku itself