Angular: Http vs fetch api - javascript

Although I understand the way Angular makes HTTP requests, I prefer using the built-in Fetch API because I don't have to subscribe and unsubscribe just to make 1 simple request. I tried using it in my angular app and it didn't throw any errors, page didn't reload (still a SPA), everything worked fine. I suppose there is a time and place for everything.
This:
fetch('/api/get_post_by_id/1').then(r => r.json()).then(j => { console.log(j); });
Is more simple, than this:
const obs = this.http.get('/api');
obs.subscribe(() => { ... });
obs.unsubscribe();
Basically my question is, is it wrong to use the Fetch API when developing Angular apps?

Like any tool you encounter during development each tool will have advantages and disadvantages and it's good to think about why a tool is being used.
When we take a look at HttpClient it originally simplified the mess that was XMLHttpRequest. In the same way $.ajax originally did it, HttpClient was the 'angular solution'. It followed the ideology of everything being an observable which has advantages (you can mix and match it with other observables) and disadvantages (it adds a lot of bloat).
Advantages of HttpClient
It allows easy mixing and matching of two observables (e.g. let's say you have one observable which returns multiple times and an API request which returns once and you want to zip the two together it's trivially easy to do). Of course, turning a promise into an observable only requires importing from from rxjs
If you forget adding an abstraction - having all requests going through an apiService layer - you can still use an interceptor to achieve similar results magically.
It will increase the learning curve for new Angular developers, thus making your job more special.
HttpClient does some magic for you such as automatic retrying of requests.
It's already included in Angular, so if you need to support 7 year old browsers like IE11 you don't need to load a polyfill like with fetch.
Advantages of fetch
Important : All fetch related code assumes you do create a very simple abstraction (e.g. apiService in these examples). This is the same as setting up an interceptor in HttpClient-land.
It's the new industry standard. Once you know it, it can be used anywhere (most likely once Angular and React die - which at some point they will - fetch will still most likely be around).
It simplifies working with service workers as the Request and Response objects are the same you are using in your normal code.
HTTP requests will typically return once and only once (of course you might be loading a file chunk by chunk, but that's a very rare exception to the rule). fetch is built around the norm (single return values), not the exception (multiple return values), and thus returns a Promise rather than a stream-like-type. The advantage this results in is that it plays nicely with any and all relevant new language features such as async and await. Compare:
try {
const posts = await this.apiService.get('/posts');
// work with posts
} catch (error) {
// handle error
}
console.log('this happens **after** the request completes');
with
this.http.get('/posts')
.subscribe(posts => {
// work with posts
})
.catch(error => {
// work with error
});
console.log('this happens **before** the request completes');
(of course you can also toPromise each Observable that will complete (or add .pipe(take(1)), but that's frankly a bunch of superfluous code (which I still often end up using))
It simplifies onboarding of new people. When you see a request such as
this.apiService.get('/posts');
a developer from any framework can come and right-click on .get and check out the function definition where things such as a domain and an authentication header being added will be clearly defined.
On the other hand when a developer sees
this.http.get('/posts')
they have no way of easily discovering if and where the request might be changed unless they are aware of Angular specific magic. This is one of the reasons why Angular is considered to have a steep learning curve.
There is no risk of there being magic you aren't aware of such as automatic retrying of requests which can end up in the same request triggering 4 times on the server and you having no idea how that's possible.
It's already included in the browser - provided you don't need to support 7 year old browsers - so it can result in a slightly smaller bundle size.
Complete tie
I sincerely don't see how types are a difference, as typing a return value from any method can be done. <Model>this.apiService.get('/posts') works perfectly fine.
Conclusion
Personally, I would strongly recommend anybody to use fetch with an abstraction layer. It results in easier to read code (even a junior who hasn't ever seen async and await is able to read it) and even if you are in a rare situation where your apiService has to return multiple times you are still completely free to do so as you're fully in control. And in general, you should only not use the standard (fetch) if the alternative offers significant advantages. Even if it was a perfect tie in terms of advantages and disadvantages it probably isn't worth going for a 'framework specific' solution.
HttpClient just doesn't seem to offer any tangible advantages beyond saving a couple of minutes of time during the initial project setup where you don't need to set up an abstraction layer for API requests.

this.http.get('/api').subscribe(j => console.log(j));
You made it too complicated, above is all you need and it is similar to the code you have for window.fetch. You can use the generic version and it will be typed to the expected interface as well making it even easier.
this.http.get<IModel>('/api').subscribe(j => console.log(j));
There is no need for unsubscribe and you only need to pipe + map if you want to transform the result before hand. Calling json() was required for the "old" HttpModule which has been replaced with (technically extended by) HttpClient as of version 4.3
And if you still prefer Promise over an Observable you can always call toPromise().
this.http.get('/api').toPromise().then(j => console.log(j));
See also HttpClient
is it wrong to use the Fetch API when developing Angular apps?
It is not wrong but there are reasons not to do it. You can inject HttpClient and write comprehensive unit tests for various scenarios, URLs, Http verbs, etc. Doing the same if you have used window.fetch throughout your code becomes much more difficult. HttpClient is also richer in that you can use the type system for your results. Observables are also more feature rich than Promises and this can come in handy in more complex scenarios like sequencing calls when dealing with multiple micro services or canceling a call if additional user input is received.
So there are multiple reasons to use the HttpClient and I can't think of a single one not to besides that it is a small learning curve.

So, after 2 years of using Angular, here are some things i found out:
There is this library called RxJS - https://rxjs-dev.firebaseapp.com/guide/overview, which is written in TypeScript (superset of JavaScript). It is like one of thee best libraries for JavaScript.
Basically, it makes event-driven apps a breeze in JS by using the Observable paradigm. A Promise can only return a value once and is async only. Observables can be used to return a single value OR multiple values and can be sync or async (depending on how you use it), you can add pipes and operators to transform the results before it is used/consumed by the subscriber to it and etc; it gives a functional programming feel to using it, which is great. It is far more feature-rich than promises.
A subscriber listens or "subscribes" to and Observable (the .subscribe() call on an observable). An Observable emits 3 types of events: next, error, and complete. Next means an event with/without data was emitted, error means there was an error within the event stream, and complete means the event stream has ended and the observable will not emit anymore.
Now, just because there was an error emitted, does not mean the event stream stopped. An event stream is only stopped when the complete event happens. Also, a subscriber can unsuscribe from an observable, meaning it will stop listening to the event stream.
The HttpClient service from Angular is built using RxJS. They basically wrapped it over the old XmlHttpRequest way of making requests - https://github.com/angular/angular/blob/main/packages/common/http/src/xhr.ts#L193.
When you make a request using Angular's HttpClient service, it automatically completes the observable on ok response, so there is no needed to call unsuscribe on the observable since it's done anyway; it won't call complete on error response but http calls only response once.
Observables don't execute until they are subscribed to; a Promise executes immediately.
I would say Observables are far better to use over Promises; i don't see RxJS leaving any time soon or ever.

I wanted to leave this a comment but I'm too new here. For the abstraction you could use HttpBackend for the fetch call and just extend the HttpClient.
import { HttpBackend, HttpClient, HttpEvent, HttpHeaders, HttpRequest, HttpResponse } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable, Observer } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class FetchBackend implements HttpBackend {
handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
if (!('fetch' in window)) {
throw new Error(
`Fetch not supported with this browser`);
}
if (req.method === 'JSONP') {
throw new Error(
`Attempted to construct Jsonp request without HttpClientJsonpModule installed.`);
}
return new Observable((observer: Observer<HttpEvent<any>>) => {
const request = new Request(req.urlWithParams, { method: req.method});
fetch(request)
.then((r) => {
r.json().then((body) => {
const headers = new HttpHeaders();
r?.headers.forEach((value, name) => {
headers.append(name, value)
})
observer.next(new HttpResponse({
url: r.url,
status: r.status,
statusText: r.statusText,
body: body,
headers: headers
}))
});
})
});
}
}
#Injectable({
providedIn: 'root'
})
export class FetchClient extends HttpClient {
constructor(handler: FetchBackend) {
super(handler);
}
}
StackBlitz

Related

Is subscribing inside RxJS tap, bad practice for an asynchronous logic?

I am having two http requests where the second one (Log request) should be subscribed after the first one (Order request) emits a value and I do some logic that should not be blocked by the Log request and the result of log request is ignored.
As I understand that tap is
Used to perform side-effects
https://rxjs.dev/api/operators/tap
and since I assume Log request and its response here is a side-effect to the Order request, is it bad practice to subscribe inside tap? Is there a more slick and RxJS way of handling this?
const order = of('2- created order').pipe(
delay(100),
tap(console.log)
);
const log = of('4- logged info to log server').pipe(
delay(500),
tap(console.log)
);
console.log('1- started creating order');
order
.pipe(tap(() => log.subscribe()))
.subscribe(() => console.log("3- didn't wait for log server"));
StackBlitz
Yes, it definitely is bad practice.
You're right that tap is there for side effects but those should not involve other streams, they should be simple side effects like assigning variables or console logging and stuff.
The issue is that you generally do not want to subscribe inside a subscribe on inside the pipe because doing so makes for very unpredictable and difficult to maintain code.
For example your subscribe inside the tab looks innocuous enough, but imagine if you were listening to a continuous stream of data, how many subscriptions would you have? would you care to track them all to unsubscribe, wouldn't that end up quite difficult to understand/debug, etc...
The problem with your code is that you are somewhat thinking in an imperative way (something like "do this and then that, and then...") instead of thinking in terms of streams.
So basically in my opinion, instead of thinking something like "how do I do this before that?" you should be thinking about how you handle the stream and the order of operations you can perform on it.
In your case, is there any reason why you'd want to print the third message inside the subscribe and not in the pipe?
Why not just doing the following?
order
.pipe(
tap(() => console.log("3- didn't wait for log server")),
switchMap(() => log)
)
.subscribe();
(like here: https://stackblitz.com/edit/rxjs-aace8i?file=index.ts)
If I may, I would like to analyse your question...
Starting with
I am having two http requests where the second one (Log request)
should be subscribed after the first one (Order request) emits a value
that just seems a simple case of having an initial observable (Order) and needing to use a mapping operator to move to a different one (Log), I assumed that you want to discard the first and move to the second so I chose switchMap (alternatively you could use concatMap or mergeMap)
Then we get to:
and I do some logic that should not be blocked by the Log request and
the result of log request is ignored.
since we already thought of how to handle the 2 observables, if we read your sentence it really spells out that we just want a side effect to occur between the first and second observable, and it ignores the streams' values anyways, so it clearly calls for a simple tap
I am sorry for the rather long message I hope it does not sound too pedantic :P
What I basically wanted to say is that you should always look at your streams and sort of think how to fit all together in accordance to rxjs programming style and what you need, and not whether some ways of doing something is acceptable or not, as that makes me realy thing that you already had suspicions on whether it not being the best solution.
As #martin mentions in the comment, it's generally bad to subscribe inside a pipe (not specifically tap, but any operator) because there's no way handle cleaning up subscriptions.
Generally, it's preferred to use one of the "Higher-Order Mapping Operators" because they handle subscribing, emitting, and unsubscribing from your observable.
Is there a more slick and RxJS way of handling this
Not sure if this would be considered "slick" or not :-), but I think it gives a nice separation of concerns if a Subject is used as a dedicated stream of log messages; then create a single subscription to have your server logging logic executed:
const logMessage$ = new Subject<string>();
logMessage$
.pipe(mergeMap(logToServer))
.subscribe();
Then in your other code, instead of subscribing, you can call logMessage$.next() to trigger the server logging logic without impeding the flow of your order stream:
order.pipe(tap(o => logMessage$.next(o)));
Here's an updated StackBlitz.

Aureliajs Waiting For Data on App Constructor

I am developing an app in aureliajs. The development process is started for many months and now, the back-end developers want to make their services versioned. So I have a web service to call to get the version of each server side (web api) app and then, for the further requests, call the right api address including its version.
So, in the app.js I am requesting the system meta and storing it somewhere. But some components get initialized before this request gets done. So they won't find the version initialized and requesting the wrong server data.
I want to make the app.js constructor wait until this data is retrieved. For example something like this:
export class App {
async constructor(...) {
...
await this.initializeHttp();
...
}
initializeHttp(){
// get the system meta from server
}
}
but this solution is not applicable. Because the constructor can't be async. So how should I block the job until the system meta is retrieved?
UPDATE
The question is not a duplicate of this question. In that question, there is a place in outer class to await for the initialization job; although in my question, the main problem is, where to put this await-tion. So the question is not just about async function in constructor, but is about blocking all aurelia jobs until async job resolves.
Aurelia provides many ways to handle asynchronous flow. If your custom element is a routed component, then you can leverage activate lifecycle to return a promise and initialize the http service asynchronously.
Otherwise, you can use CompositionTransaction to halt the process further, before you are done with initialization. You can see a preliminary example at https://tungphamblog.wordpress.com/2016/08/15/aurelia-customelement-async/
You can also leverage async nature of configure function in bootstrapping an Aurelia application to do initialization there:
export function configure(aurelia) {
...
await aurelia.container.get(HttpServiceInitializer).initialize();
}

Converting promise-based React app to redux-saga: how do I set React component variables?

I'm a beginner with redux-saga and as a task I thought I'd try to convert a large existing React app from Promises to redux-saga. I'm making some progress, but one issue I came on is that when I used Promises I was able to set component-local variables depending on whether the promise fulfilled or rejected. Now it seems I just "hand over" the request to redux-saga and never know (in the call site) whether it fulfilled or not, and now I don't know (within my component) whether it succeeded or not.
For example, let us say I have this promise-based call which fetches my user's games
getCurrentGamesForUser = (nickname, token) => {
return logic.getGamesForUser(nickname, token)
.then(currentGames => {
this.needToUpdateGamesFlagFromSocketIO = false
this.setState({currentGames})
sessionStorage.setItem('currentGames', JSON.stringify(currentGames))
})
.catch(({message}) => this.onError(message))
}
Here I'm setting the flag this.needToUpdateGamesFlagFromSocketIO -- as well as a sessionStorage variable -- if my promise succeeds.
Now, as I understand it, I would convert this to
getCurrentGamesForUser = (nickname, token) => {
this.props.dispatch(getCurrentGames(nickname,token))
}
and this works fine. If there's an error, that will be sent to the store from the saga and my component will reflect that.
But what do I do about the local flags and the session storage? Do I need to add these to the store as well? That seems a messy way to go, unless I had a separate store for each component, a local store for each component, which also seems messy. I saw some discussion of a similar topic here, https://github.com/redux-saga/redux-saga/issues/907, but there doesn't seem to be an answer. I'm sure I'm missing the "redux way" of handling all this, and so would welcome any guidance.
Redux-Saga is meant to offload all action based triggers outside component to a separate saga scope. So unfortunately there is no straightforward way to implement what you have requested. Although there are some suggested workarounds in the issue tracker you have mentioned.
The primary aim of redux-saga was ease of management of side affects by offloading it to a separate execution context. One of the tradeoff, is communication can only happen through component -> action -> saga -> store -> component. Hence for the above requirement, I believe using redux-saga would be counterproductive.
That said, you can always use redux-saga in combination with other promise / callback based methods such as redux-thunk.
Usecases such as above would be better suited for callback based implementations while complete isolation of side effects to redux would be handled well with redux-saga.

Function synchronization and observable

I have a Typescript function which converts a list of elements into a Map: Map.
Along this process of conversion, I need to modify something in the Map before returning the Map.
To obtain the info, I have to request a server so I have a http.GET request/subscription to get the value.
I am using the value right after the GET, and the server has not yet answer, so I am returning the Map with the wrong value.
(It comes later but too late).
Then I use this map in which I don't have the correct value
I need a mechanism to synchronize my function with the result of my GET request, before processing the Map later in my code (after returning the function).
How can I do this, I have been told that the Observable may be the solution, but I don't know how to do it.
I could use some help ;-).
Best regards,
Charles.
How can I do this, I have been told that the Observable may be the solution, but I don't know how to do it.
For async things you need some form of continuation mechanism. The popular choices are:
Callbacks
Supported natively e.g. setTimeout uses it
// Some code
setTimeout(() => {
// Some more code that executes after 1 second
}, 1000)
Promises
Supported natively (now). Some docs on TypeScript and how they help https://basarat.gitbooks.io/typescript/docs/promise.html
Observables
If your frameworks http returns observables you would need to use them.
Summary
You cannot halt execution of entire JavaScript as JavaScript's JS context is single threaded. You need to work with continuations. Check the docs of the library (e.g. angular / axios) or native api (fetch / XHR) you are using.

How do you 'intercept' all HTTP requests in an EmberJs app?

I would like to be able to capture all HTTP requests and responses, and modify them, before they get to the rest of an EmberJs app. I would like to do this globally - across the entire app. I have not been able to find this digging through the API. How can this be done?
(Modification is to perform some conditional logic based in certain headers, or to add or modify certain headers).
In AngularJS, you can accomplish this using something like this:
App.factory('AppHttpInterceptor', function($q) {
return {
request: function(req) {
//modify request
return req;
},
response: function(res) {
// modify response
return res || $q.when(res);
}
};
});
App.config(function ($httpProvider) {
$httpProvider.interceptors.push('AppHttpInterceptor');
});
First, it's important to note that Angular and Ember are not built to serve the same purpose. They are both javascript frameworks, and that's about where the similarities end. Another big factor in the difference of async requests in the two framework is Angular's integration of promises into it's async services. Ember async services are not promise based, and therefore response interceptors are not possible (see my note below).
AngularJS provides $httpProvider as a configurable singleton that returns a configured instance of $http as a promise object. Angular focuses on services rather than methods (although it does have a few methods it gives you), and that's what sets Angular apart from other frameworks like Ember, which give you a structure to build services, but doesn't provide services in it's core.
Instead, with Ember, you'd have to build a service and service provider concept yourself. You could take something like cerebris/ember-rest, and extend it in such a way as to use properties you describe. This library provides an Ember.resource method, which you could use prototype to extend from there:
Ember.ResourceAdapter.extend({
_prepareResourceRequest: function(params) {
params.beforeSend = function (xhr, settings) {
//set your xhr interceptors here
}
}
});
Edit: Clarification on use of $ajax in ember and $http in Angular (promises vs callbacks)
The biggest difference on how angular makes response interceptors possible, is that async requests in angular are promises, whereas $ajax calls are not. Without getting too much into the weeds, you can assign variables to each step of a promise, making it available for mutation/handling along every step of the way, whereas with $ajax, you can only perform operations when the data has completely returned. With Promises, assign variables to represent states at any point during the lifecycle of the promise, and using that reference, you can mutate the promise as needed, at any point in time prior to the event of the promise fully resolving.
Thus why it's possible to do request interceptors with $ajaxPrefilter but there is no good way to do response interceptors, using the abstract configuration approach with $ajax. To truly do in Ember what AngularJS does with $http, you need to create a promise based async request/response service, as opposed to utilizing non-promise based xhr requests such as $ajax.
jQuery does provide an $ajaxSetup() method, which you might be able to set a dataFilter property to and define a handler function, however this is not recommended. With angular, the $httpProvider can be configured by module, and through de-coupling and Separation of Concerns, this can become truly powerful, allowing you to encapsulate and cascade http interceptor configurations with a lot of control. Making the same changes to your ajax settings will register themselves on the global jquery namespace, and can cause conflicts if you need to grow your app.
One video I found particularly englightening on this subject was from ng-conf 2014: Christion Lilley: Going Postal with Angular Promises
Edit 2: addressing Ember.RSVP
While Ember.RSVP is indeed a promise class useable in the framework, it does not have any methods available to perform resource requests. This means you have to manually assign an http request instance to an instance of RSVP.deferred such that it resolves your http request before returning the promise.
This allows you to do interceptors on both sides of each individual request, but does not provide a solution for configuring interceptors for all requests. You'd have to create another function or service for that, and extend RSVP with this function.

Categories