How to make API abstraction layer code cleaner - javascript

Introduction
I'm developing a react application that has to communicate with a rest api. Currently the full api isn't fully implemented yet, so I'm making mock ups and to avoid wasted code I'm adding an adding an abstraction layer between mock up/api and application.
Current situation
Currently I have classes representing the components (like 'a user') in the api. A get request of an url obj1/obj2/ob3/ is translated into javascript as server.get("obj1").get("obj2").get("obj3").fetch(...args).then(onsucces,onerror).
fetch would return a promise. the other methods
The question
My question has 2 parts.
First is there a way to clean up this part, .get("obj1").get("obj2").get("obj3"). (I don't think react supports Proxies).
Secondly, if you have recursive requests
server.get("user").get(<id>).fetch(
(user)=>{
update_ui(user);
user.books.fetch(
(books)=>{
update_ui(books);
},(error)=>{}
)
},(error)=>{}
)
they can get ugly quickly, is there a way similar to .then(...).then(...) (for promises) to flatten them or something complete differently that would result in better code.
What get().get()...fetch() does
the gets would construct a path from which the fetch,... operations will be executed for an actual api these would be urls for a mock-up this could be a hard-coded dictionary.
for example, get("users").get(<userid>), would correspond to an object of the form
{
path:"users.<userid>" //(or any other seperator)
fetch: function(...args) //GET specified in the api
push: null // api doesn't specify a POST request for this url
...
}
the translation of HTTP request to javascript is as follows:
GET to fetch
POST to push
PUT to set
PATCH to update
DELETE to pop
the implementation of these methods (fetch) would then use the path and the specified arguments to get,post,... the data.

Related

The relationship between front end and middleware

I have a front end application, which I would like to return results with from an Express backend. Let's just call those results country and city for this reference.
I have done a bunch of searching, but I cannot find any solid resources on the relationship between the front end and middleware. Yes, I know what these things are, and the order in which they should flow, but the confusion sits with :
Do I need to connect my front end and middleware? How so?
If I am already connected to my backend from the front end, would I also have to connect to middleware?
How would I return the country and city from the middleware and/or express backend?
Any other info you think would be helpful for the greater dev community who is still learning would be beneficial.
While you could return data from a middleware, it's probably not what you are trying to do. A middleware is a piece of code that is executed between the time the request is receive by your backend, and the resource is fetch. In a middleware you could do things such as check if a user has access to a certain resource or authenticate a user by some sort of credential passed with the request.
Either way, the way you would, typically, do request from your front-end to your backend is via an XmlHttpRequest. Those request are usually Asynchronous, so they usage will not block the whole page while being executed. There are many ways you could create XmlHttpRequest. The native Javascript way is kinda ugly so I would suggest using the fetch api instead. You could also go with third party library if you need to do more complex stuff. I personnally like axios but this is up to you.
To give you a better understanding of what Express is doing, it's basically an infinite loop that waits for http request. You need to defined routes, that execute function that returns data.
Here is a basic example. Note that this script is executed via NodeJS :
// myserver.js
const express = require('express')
const app = express()
app.get('/cities', (req, res) => {
const cities = /** somehow get all the cities **/
res.json(cities);
})
/** the rest of the server... **/
/** For example, the route for Countries **/
In the previous example, we've built a basic server that listen to the url localhost:3000/cities and execute a function when this url is fetched. The said function will fetch all the cities and return them as JSON.
In your frontend, You would need to do a XmlHttpRequest that would call this url, to get the server to execute the function, which will return the data. Phew... I hope I did not lost you there.
A typical example would be a simple call using the fetch api.
Please note that this script is executed in the browser.
// myclient.js
async fetchAllCities() {
const cities = await fetch('http://localhost:3000/cities');
console.log(cities);
}
// just for fun, we add a click listener on a button and call the function defined above.
document.getElementById('myButton').addEventListener('click', async function() {
// we fetch the cities when we click on the button !
await fetchAllCities();
});
In the previous example, I am using the fetch function to call the url we declared in our Express server.
I'm also using Async / Await, which can be a little tricky, but it just mean Wait for the data to be there before going forward.
I highly suggest reading on the subject. Here are some references.
How do I return the response from an asynchronous call?
Understanding async/await on NodeJS.
Await from MDN
I hope this brief overview of XmlHttpRequest helped you get the base of how an API works.
Middleware is used to help the back-end do its job in processing incoming requests. It does not exist separate from the back-end. It's part of the back-end. For example, you might have middleware that checks to see if an incoming request is properly authorized/authenticated before the route can be handled by it's regular route handler.
Do I need to connect my front end and middleware? How so?
No. Your front-end sends requests to the back-end. The back-end may or may not use middleware to service the request. That's entirely up to the implementation in the back-end and what it needs to do for any given request.
If I am already connected to my backend from the front end, would I also have to connect to middleware?
No. You don't separately connect to middleware. You connect to your back-end and the back-end may or may not use middleware to do its job (something the front-end will have no knowledge of).
How would I return the country and city from the middleware and/or express backend?
You would have to show more details about what you're actually trying to return back from a request, but a common data format is JSON so you could construct a Javascript object with your desired response and then send it back to the client as the response from the incoming request using either res.json(someObj) or res.send(someObj) (both do the same thing if someObj is a Javascript object).
For example:
app.get("/getsomething", (req res) => {
// do some processing here to get cityResult and countryResult
// construct object to send back to client
const obj = { city: cityResult, country: countryResult};
// send this object as JSON back the the client as the response to this
// incoming request
res.json(obj);
});

What is the difference between esri print task and export web map

While taking screenshot of the current map using arcgis JavaScript, I could see that we have two ways to take the screenshots.
I could see same question asked here
using /export of url
using print task api
var printTask = new esriLoader.PrintTask(PrintTaskServiceUrl);
esriLoader.Config.defaults.io.proxyUrl = proxyUrl;
esriLoader.Config.defaults.io.alwaysUseProxy = true;
var template = new esriLoader.PrintTemplate();
template.exportOptions = {
width: 1015,
height: 633,
dpi: 96 // if 200 ,map image will get good quality
};
template.format = "JPG";
template.layout = "MAP_ONLY",
template.preserveScale = false;
template.showLabels = true;
template.showAttribution = false;
template.layoutOptions = {
"legendLayers": [], // empty array means no legend
"scalebarUnit": "Miles",
"copyrightText": "<div>xxx</div>",
}
var params = new esriLoader.PrintParameters();
params.map = map;
params.template = template;
printTask.execute(params, success, error);
So, what is the difference between these 2 methods?
Is the REST API provided by ArcGIS Server to generated map images.
Is the Javascript object with in ArcGIS JavaScript SDK.
The PrintTask will use the Export Web Map Task from REST api to generate the map image. PrintTask provides a simple way of creating images. On the other hand, if you want to use the REST API directly you can do so, by using esri\request object, but you would have to generate all the required parameters as described by the API.
Disclaimer: I just joined and don't have reputation to comment; hence I have to post it as an answer to original question, which following is not.
Is there any advantage when using REST API directly or vice versa?
As T Kambi stated, using PrintTask, if available, is easier. Accessing Export Web Map Task REST end point directly may be beneficial in some cases, for example:
Web_Map_as_JSON parameter needs to be modified before request is
sent to the Export Web Map Task Geoprocessing service. For example, resources not present in the map need to be added, layers' visibility needs to be changed, tokens for secured services need to be supplied, etc.
The request for map image is sent from outside of ArcGIS API for JavaScript environment. There may be no actual map and one only wants to use ArcGIS Server capabilities to fuse services' images and utilize a Layout Template.
The Export Web Map task may be accessed either from client or server side. In the latter case, a kind of "Print Task proxy" may help with printing secured services; for example, eliminate need for custom printing service in applications utilizing Long-lived Tokens.
Answering the original question now.
The Export Map REST end point is specific to a MapServer service and allows for producing a service image for given extent, specified layers, spatial reference, etc.
Ultimately, PrintTask sends request to Export Web Map Task REST end point. Export Web Map Task is an interface for producing a map image, possibly with multiple services present and graphics, per provided Web_Map_as_JSON parameter. It also provides an option to surround the map image with a layout, predefined as an ArcGIS template (stored on the server). For example:
https://sampleserver6.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%20Web%20Map%20Task/execute
After executed, the value of url in provided results is reference to the map image.
Export Web Map Task interface results
Note: Web Map as JSON parameter can be observed in browser's developer tools after PrintTask request is sent by the API. In this case, it is taken from ESRI's Print Dijit example:
(cannot post another link)

Having a fixed response structure with node.js and Express

We have recently started using Node.js for our API server instead of Java. Apart from all the good things which Node.js provides, one thing I miss the most is having a proper response object for an API.
Since Javascript being dynamically typed languages, objects can be created on the fly while returning the response. This is in contrast to Java, where I can have a class , an instance of which will be serialized in the response. I can anytime lookup this class to determine what the response of the API will be.
Is there such a design pattern in Node.Js / Javascript. We would like our API's to have strict conformance to such templated object.
You can make them yourself.
If you're using ES6 for example, you can have various error and response modules, and perform your own validation in the class that creates those responses.
For example,
// sample-response.js
class SampleResponse {
constructor(message) {
// validate `message` somehow
this.data = message
}
}
module.exports = {
SampleResponse
}
Then however you're structuring your HTTP interface, you can send back whichever response you'd like (for example, with Express):
res.send(new SampleResponse(message))
Same goes with errors, etc. You're not necessarily limited by a lack of types with JavaScript, you just have to enforce things differently.
If you're not using ES6, you can do something like this:
module.exports = {
SampleResponse: function(message) {
// do some validation
return { data: message }; // or whatever you want
}
};
You can use Flow or TypeScript for you code.
You can use contract testing tools. Depending on the contract for your REST API:
abao for RAML
dredd for api blueprint
SoapUI for Swagger
My choice is to use TypeScript and Abao+RAML.

Is google apps script synchronous?

I'm a Java developer learning JavaScript and Google Apps Script simultaneously. Being the newbie I learned the syntax of JavaScript, not how it actually worked and I happily hacked away in Google Apps Script and wrote code sequentially and synchronous, just like Java. All my code resembles this: (grossly simplified to show what I mean)
function doStuff() {
var url = 'https://myCompany/api/query?term<term&search';
var json = getJsonFromAPI(url);
Logger.log(json);
}
function getJsonFromAPI(url) {
var response = UrlFetchApp.fetch(url);
var json = JSON.parse(response);
return json;
}
And it works! It works just fine! If I didn't keep on studying JavaScript, I'd say it works like a clockwork. But JavaScript isn't a clockwork, it's gloriously asynchronous and from what I understand, this should not work at all, it would "compile", but logging the json variable should log undefined, but it logs the JSON with no problem.
NOTE:
The code is written and executed in the Google Sheet's script editor.
Why is this?
While Google Apps Script implements a subset of ECMAScript 5, there's nothing forcing it to be asynchronous.
While it is true that JavaScript's major power is its asynchronous nature, the Google developers appear to have given that up in favor of a simpler, more straightforward API.
UrlFetchApp methods are synchronous. They return an HttpResponse object, and they do not take a callback. That, apparently, is an API decision.
Please note that this hasn't really changed since the introduction of V8 runtime for google app scripts.
While we are on the latest and greatest version of ECMAScript, running a Promise.all(func1, func2) I can see that the code in the second function is not executed until the first one is completed.
Also, there is still no setTimeout() global function to use in order to branch the order of execution. Nor do any of the APIs provide callback functions or promise-like results. Seems like the going philosophy in GAS is to make everything synchronous.
I'm guessing from Google's point of view, that parallel processing two tasks (for example, that simply had Utilities.sleep(3000)) would require multiple threads to run in the server cpu, which may not be manageable and may be easy to abuse.
Whereas parallel processing on the client or other companies server (e.g., Node.js) is up to that developer or user. (If they don't scale well it's not Google's problem)
However there are some things that use parallelism
UrlFetchApp.fetchAll
UrlFetchApp.fetchAll() will asynchronously fetch many urls. Although this is not what you're truly looking for, fetching urls is a major reason to seek parallel processing.
I'm guessing Google is reasoning this is ok since fetchall is using a web client and its own resources are already protected by quota.
FirebaseApp getAllData
Firebase I have found is very fast compared to using a spreadsheet for data storage. You can get many things from the database at once using FirebaseApp's getAllData:
function myFunction() {
var baseUrl = "https://samplechat.firebaseio-demo.com/";
var secret = "rl42VVo4jRX8dND7G2xoI";
var database = FirebaseApp.getDatabaseByUrl(baseUrl, secret);
// paths of 3 different user profiles
var path1 = "users/jack";
var path2 = "users/bob";
var path3 = "users/jeane";
Logger.log(database.getAllData([path1, path2, path3]));
}
HtmlService - IFrame mode
HtmlService - IFrame mode allows full multi-tasking by going out to client script where promises are truly supported and making parallel calls back into the server. You can initiate this process from the server, but since all the parallel tasks' results are returned in the client, it's unclear how to get them back to the server. You could make another server call and send the results, but I'm thinking the goal would be to get them back to the script that called HtmlService in the first place, unless you go with a beginRequest and endRequest type architecture.
tanaikech/RunAll
This is a library for running the concurrent processing using only native Google Apps Script (GAS). This library claims full support via a RunAll.Do(workers) method.
I'll update my answer if I find any other tricks.

UI unresponsive during AJAX calls

I have a dashboard screen that needs to make about 20 AJAX requests on load, each returning a different statistics. In total, it takes about 10 seconds for all the requests to come back. However during that 10 seconds, the UI is pretty much locked.
I recall reading a JS book by Nick Zakas that described techniques for maintaining UI responsiveness during intensive operations (using timers). I'm wondering if there is a similar technique for dealing with my situation?
*I'm trying to avoid combining the AJAX calls for a number of reasons
$(".report").each(function(){
var container = $(this)
var stat = $(this).attr('id')
var cache = db.getItem(stat)
if(cache != null && cacheOn)
{
container.find(".value").html(cache)
}
else
{
$.ajax({
url: "/admin/" + stat,
cache: false,
success: function(value){
container.find(".value").html(value.stat)
db.setItem(stat, value.stat);
db.setItem("lastUpdate", new Date().getTime())
}
});
}
})
If you have access to jQuery, you can utilize the $.Deferred object to make multiple async calls simultaneously and perform a callback when they all resolve.
http://api.jquery.com/category/deferred-object/
http://api.jquery.com/deferred.promise/
If each of these callbacks are making modifications to the DOM, you should store the changes in some temporary location (such as in-memory DOM objects) and then append them all at once. DOM manipulation calls are very time consuming.
I've had similar problems working heavily with SharePoint web services - you often need to pull data from multiple sources to generate input for a single process.
To solve it I embedded this kind of functionality into my AJAX abstraction library. You can easily define a request which will trigger a set of handlers when complete. However each request can be defined with multiple http calls. Here's the component (and detailed documentation):
DPAJAX at DepressedPress.com
This simple example creates one request with three calls and then passes that information, in the call order, to a single handler:
// The handler function
function AddUp(Nums) { alert(Nums[1] + Nums[2] + Nums[3]) };
// Create the pool
myPool = DP_AJAX.createPool();
// Create the request
myRequest = DP_AJAX.createRequest(AddUp);
// Add the calls to the request
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [5,10]);
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [4,6]);
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [7,13]);
// Add the request to the pool
myPool.addRequest(myRequest);
Note that unlike many of the other solutions provided this method does not force single threading of the calls being made - each will still run as quickly (or as slowly) as the environment allows but the single handler will only be called when all are complete. It also supports the setting of timeout values and retry attempts if your service is a little flakey.
In your case you could make a single request (or group related requests - for example a quick "most needed" request and a longer-running "nice to have" request) to call all your data and display it all at the same time (or in chunks if multiple requests) when complete. You can also specifically set the number of background objects/threads to utilize which might help with your performance issues.
I've found it insanely useful (and incredibly simple to understand from a code perspective). No more chaining, no more counting calls and saving output. Just "set it and forget it".
Oh - concerning your lockups - are you, by any chance, testing this on a local development platform (running the requests against a server on the same machine as the browser)? If so it may simply be that the machine itself is working on your requests and not at all indicative of an actual browser issue.

Categories

Resources