Frontend content with a backend API - how to avoid hard-coding where the backend API is? - cross-domain

I have frontend content that needs backend REST APIs to function. The APIs allow for cross-origin resource sharing (CORS). Typically we run the complete stack locally, including a user-mode Nginx instance tailored for development use which serves the frontend content. The full stack however is a bit too much to expect part time contractors to wrangle. So I'd like an approach very basic they can use to be effective and get stuff done.
Their current solution is horrible:
var port = location.port;
// base url of backend API
var url = window.location['origin'];
if (port != '443') {
// assume we're running in "development" mode against a staging server
url = "https://staging-server.somewhere.com";
}
Apart from the fact that this is furthering frontend content that is a bit kludgey as it is - it precludes the static content from being hosted in a variety of other ways, including a suite of functional and integration tests.
I have some ideas, like having them run a small web server that proxies to the backend APIs, but what I would really like is something simpler that allows me to default url in a less kludgey way. Ideally, there would be some manner of configuring url from a file ignored by version control (e.g., .gitignore).

I was able to create a solution that works for all manner of local development as well as production releases.
I created some JavaScript, apiurl.js, that sits alongside all of our other JavaScript content. If the apiurl.jsfile is present, I read its responseText into eval(). The frontend can therefore change the URL based on the content of that file.
E.g., apiurl.js has:
var apiurl = "https://staging-server.somewhere.com";
And the JavaScript to handle the content:
eval(responseText);
if (typeof(apiurl) != undefined) {
url = apiurl;
}
The apiurl.js file is untracked by version control and not used in production.

Related

node Vue.js different code scenario if run in dev mode

I have Vue.JS front app with nodeJS backend based on expressJS. ExpressJS also used as web server for statically built Vue.JS app
Front app communicates with express backend via rest and websocket. It uses url host from window.location instance and easily communicates with backend
In production mode, when built application in static expressJS server area, everything work perfect
In dev mode, Vue use it's own web server, and backend urls based on window.location are incorrect because no expresJS on same host and port.
So my question is it possible change some code blocks if running in dev mode ?
Like something this :
if( devmode)
{
const url = "http://somebackendhost/rest"
}
else {
const url = location.host ....
}
}
I will assume you are developing your Vue app using Vue CLI
Changing app behavior depending on environment
In Vue CLI you can use Environment Variables
if(process.env.NODE_ENV === "development")
{
}
This works thanks to Webpack's Define plugin and big advantage is that process.env.NODE_ENV is replaced at build time by the real value. So in production build Webpack will see just if("production" === "development") {} and happily removes the code in optimization phase because it knows this can never be true
Better solution
But I would not use this approach for your problem. Using different API server (not same as the server used for serving Vue SPA) can easily lead to CORS problems
Exactly for this use case, Vue CLI (and Webpack Dev server used under the hood) supports proxying
vue.config.js
module.exports = {
devServer: {
proxy: {
'^/api': {
target: 'http://localhost:58300/',
ws: true, // websockets
changeOrigin: true,
}
}
},
},
This config makes Vue Dev server to proxy any request to /api to other server running at http://localhost:58300/ (your node/express app) and change the origin (so browser thinks response came from the dev server)
All of this can be done without Vue CLI but you will need to set it up by yourself in Webpack config...
The problem
You can't access this information from your browser.
But there are three solutions:
Solution #1
On compilation time create a variable in code which defines devmode (const devmode = true;)
Solution #2
Because your bundler can minify your variable names or changing the scope for security reasons, may be the situation where you can't access it.
So second solution is to define devmode in your localStorage.
Solution #3
Third solution is almost the best.
If you are developing, you are probably accessing your web app via localhost.
location.hostname will return the name of host, so you can make something like:
const devmode = location.hotname == 'localhost';
Best solution
Do not do this. Develop a fully working web app using local REST API and define the URL of REST API in some variable, so when you are preparing your production app, you or compiler just changes the URL adress variable in code of your REST API.
Why is this the best solution?
Because it do not impacts your end-user's performance and they will be loading less code, which is the best practise.
Post Scriptum
Don't forget to remove all devmode codepaths when compiling production version!

What does localhost:1337 become in production?

I'm sure this question is very basic but it's confusing me.
Currently in development mode my backend runs in localhost:1337 (using Strapi cms to be precise).
But what will it turn into when the website is deployed to a web server?
Specifically, the website will reside in a sub-directory and not in the root, will it affect this?
Currently this is a url I'm using inside of my project as images src:
localhost:1337/uploads/image-example.jpg
I'm trying to understnad what will be the new url once the project has been deployed, and what's more it's deployed in a subdirectory and not in the root
Thanks in advance
You shouldn't have to do anything.
Most, if not all, web hosts use the PORT environment variable to configure the application server. I just did a quick code search through Strapi and found this:
"port": "${process.env.PORT || 1337}",
to read the environment variable or use a default value (instead of asking for a random port with 0, for predictability while developing). If you used their starter script, I would assume your application has the same.
In many cases, this is sufficient, with two exceptions.
If your application server doesn't know its URL, it can only generate relative URLs. For example, it won't be able to compose an email containing a link to itself.
HTTP redirects required an absolute URL once upon a time, but that has since been amended.
Setting just the port assumes that the application will be mounted on /. If, for example, you were configuring your own web server (instead of Heroku), and you wished to mount your application on /blog, you would need some way to tell your application so that it can put the right prefix on paths.
It might be possible to get around this by restricting the URLs to relative paths only, but that sounds tedious and I don't think it's usually done. And an incoming request doesn't actually have enough information to discern the URL that the user is looking at. Instead, you show the URL to your app out of band.
For example, Grafana has a root_url variable in its config file where you put the full URL prefix, Prometheus takes a -web.external-url argument, etc.
However, I don't think Heroku can mount your application anywhere other than /, so the port should be sufficient.
Here's a complete server that runs on Heroku and shows its environment to you:
const app = require("express")();
app.set("json spaces", 2);
app.get("/", (req, res) => {
res.send(process.env);
});
const server = app.listen(process.env.PORT, () => {
console.log('server is up on', server.address());
});
The PORT variable is all the configuration it needs; the environment does not include the app name.
Hardcoding the production URL of your app can become a maintenance burden, so make sure it remains configurable with a command line argument or environment variable if you do end up teaching your app what its URL is.
So to answer your question specifically, your image src should begin with /uploads.

Cannot find the file URL using xhr.open('post',url,true) - Status: 404 node js

Trying to Upload an image by XMLHttpRequest(), have issue understanding what is the correct URL to access file via xhr.open(...).
Following this example for server side code and everything..
var xhr = new XMLHttpRequest();
xhr.open('post','../../../../server/routes/saveImage.js',true);
xhr.onload = function() {
if(this.status ==200) {
resolve(this.response);
} else {
reject (this.statusText);
}
};
The Project directory is something like this
Project
client
app
component
product
addproduct.js <-- xhr.open is called from here
server
routes
saveImage.js <-- File being called
Also regarding paths let me know if there is a more convenient way to check the access path or absolute path to use in url.
I think there is a conceptual problem in many levels.
First, When you are XHRing(ajax) an url that means you are accessing the url from CLIENT SIDE. So, Let's say you have an app and HTTP posting or getting an url. Do you have that file from client side? The answer is obviously NO.
let's say you are hosting the app in:
http://localhost/myapps/app
So, When you access ./someFile.txt, ../someFile.txt and ../../someFile.txt you are actually requesting
./someFile.txt-> http://localhost/myapps/app/someFile.txt
../someFile.txt-> http://localhost/myapps/someFile.txt
../../someFile.txt-> http://localhost/someFile.txt
Now, For your problem. You need to host the Server Side upload code somewhere. The example assumes the Server Side code is hosted in, for example, http://localhost/upload and use xhr.open('post','/upload',true);
You need to understand requireing or importing or fs.readFile a file is accessing the path internally. But when you host the app, any client side code like Ajax(XHR) is accessing the url from outside.
The problem indeed was with setting up a server side route, this was mostly clarified, thanks to the answer from #Arkita and comment from #Kasper. But I went ahead to dig for a solution, which may not be very useful to others as this was a dumb question in the first place, but here it goes..
On client side
xhr.open('post','/saveImage/save',true);
on server side if you are using Express.js or other connect based frameworks
app.post('/saveImage/save',(req,res,next)=> {....})
Also the example I linked above may be outdated, this seems more helpful.

How to serve static files in AppEngine Standard and nodejs

The documentation says that you simply must update your app.yaml - like you would for any language within AppEngine. It also states later on that for local development, you probably want your server to respond to static requests as well. However, when I update my very simple app.yaml to be as such:
runtime: nodejs8
handlers:
- url: /apiEndPoint
script: auto
- url: /.*
static_dir: public
It seems all requests still end up coming in to my script - which will return a 404 in the prod instance, since those files won't be uploaded. I can force them to be uploaded, and then my nodejs server responds to the static requests - but I thought the idea of this app.yaml was to configure it so static files are served outside of my app logic?
So to be clear - you can host static files in production Nodejs Standard AppEngine without needing to use a JS server. However, for local development, you must find a way to serve those files locally when running on your machine. For this reason, you put the handler in Express, for static files, which should never be touched in production - since the app.yaml handler is the first-pass.
If you want to be positive Express.js is not serving up static files in production, you can do so by doing something like this:
// Production instances automatically have this environment variable.
const isLocal = (process.env.NODE_ENV !== "production");
if(isLocal) {
app.use(express.static('public'));
}
The static files are uploaded during deployment, but not in the same place as the app's code. They're uploaded in Google's infra dedicated for directly serving static content. This could be confirmed by increasing the log verbosity of the deployment command.
When a request URL matches one of the static handlers it should be directed to this dedicated infra, it shouldn't reach your app code. It should be relatively easy to confirm with an actual deployment.
As for the local development, I'm not exactly sure how the Node.Js server behaves (indeed, the docs appear to suggest Express may be needed to handle static files), but the Python one serves itself the static files based solely on the app.yaml static handler configs, without hitting any of the app code. Could be because of the still very new Node.JS standard environment support.
The static files that you want to serve need to be deployed along your application code with gcloud app deploy.
Your app.yaml file says:
Any request that matches /apiEndPoint will be routed to your Node.js app
Any other request URL will serve a static file from your public folder and not arrive to your application (once deployed).
For example: /index.html will serve public/index.html if this file has not been deployed, then it will return a 404 page.

nodejs mobile development: how control navigation flow

I am a nodejs newbie and would like to understand the navigation flow when using nodejs to serve mobile applications.
Moible app
index.html
Show all users
Nodejs server snippit
var myData = {
"employees": [
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName":"Jones" }
]
};
res.send(myData);
Question: how do I display this data on another page (users.html)? I've worked with nodejs where I can just render to a specific path and it picks the appropriate Jade file but not sure how to do it since the html / js files are on the phone and not the server.
If you know of an example application I can just look through that code and figure it out.
Thanks for your help.
First of all you need to understand that your node.js is executed on server side, and all it can do - response on requests and do some logic, that stays on the server.
Then there is .html and .js that is sent to your clients (browser), and it is rendered and executed on client-side. This execution and logic is very different, and is focused to provide user interactions and render all sorts of data.
So all you need is be able to 'ask' server for data (request) and then get response, validate it in browser, if it is valid, you can render it using JS.
In order to make your life easier, consider using jQuery.
AJAX - to make requests to server and get response with data.
express.js - web framework for node, helps with routes.
And just generally - go and try things, experiment and it is better to understand whole picture or specific details frist, before you making any decisions.

Resources