How to Create Aurelia Services without Redundant Header Configuration? - aurelia

I'm currently working on a project using Aurelia as the front-end framework, and I'm wondering if there's a more eloquent and less redundant way to set the request header in my API services. The following is an example.
In this Post service, I have created a configureHeaders method that I'm calling prior to every API call because, otherwise, I run into the case where the web token has changed but the request header isn't updated. While creating this configureHeaders method is a functional workaround, I have to do it for each of my services, and it's feeling very redundant.
Is there a way to configure the request header application-wide so that I don't have to create a configureHeaders method for each service and call it for each request?
import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-http-client';
import environment from 'environment';
#inject(HttpClient)
export class Post {
constructor(http) {
this.http = http;
}
configureHeaders() {
this.token = window.localStorage.getItem('token') || null;
this.http = this.http
.configure(x => {
x.withBaseUrl(environment.serverBaseURL);
x.withHeader('Authorization', `Bearer ${this.token}`);
});
}
getPosts() {
this.configureHeaders();
return this.http.get('post')
.then(posts => {
return JSON.parse(posts.response);
});
}
}

As R.Richards commented, Aurelia's HttpClient Interceptor is what you're after.
Here's a class example - as opposed to object with anonymous functions
1.) Declare the interceptor
import {Interceptor, HttpResponseMessage, RequestMessage} from 'aurelia-http-client'
export class CustomInterceptor implements Interceptor {
request(request: RequestMessage): RequestMessage {
//Do request interceptor here
return request;
}
response(response: HttpResponseMessage): HttpResponseMessage{
//Do response interception here
return response;
}
}
2.) Register the interceptor as part of your default http client within your main.js
import {CustomInterceptor} from 'path/to/custom-interceptor'
...
...
http.configure(config => {
//config stuff here
).withInterceptor(new CustomInterceptor())
This should suit your eloquence!

Related

How can we access decorator from a service class in NestJS

I am new in NestJS and trying to do auth system. I was able to do. So here is what I am doing to get access to auth.
In my controller I have
#Get('/user')
async getUser(#AuthUser() token: string) : Promise<Object> {
return this.authService.getUser(token)
return token
}
Here I am passing a AuthUser decorator I want to avoid passing in controllers.
In the authService.getUser method I have something like this
async getUser(token: string): Promise<Object> {
try {
const user = await this.jwtService.verifyAsync(token)
return user
} catch (error) {
return false
}
}
and my decorator looks like this
import { createParamDecorator, ExecutionContext } from '#nestjs/common';
export const AuthUser = createParamDecorator(
(data = 'u_ses', ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return data ? request.cookies?.[data] : request.cookies;
},
);
I don't like code. If I need to know the user id from a service class or anywhere I would need to pass the token and to get token I need use #AuthUser() token: string)
So I want to do something like this
this.authService.getUser(), here I don't want to pass token or anything and should be able to access this getUser method from anywhere. Since it's a service class, I can inject and use it but I won't have the token.
I tried injecting the decorator inside the service class, but this doesn't work.
One best solution I would prefer is to use the JWT things inside the decorator, so I don't need the service class' method :)
I am looking for a nicer solutions from you :)
Thank you.
Nestjs has NestMiddleware. Here, you can authorize before access to controller like this:
import { Injectable, NestMiddleware, UnauthorizedException } from '#nestjs/common';
import { Request, Response, NextFunction } from 'express';
#Injectable()
export class AuthenticationMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const headerAuthentication = req.headers.authorization;
if(!headerAuthentication) throw new UnauthorizedException('Authorization failed!');
const token = req.headers.authorization.split(' ')[1];
if(token) {
next();
}else {
throw new UnauthorizedException('Authorization failed!');
}
}
}
and in AppModule implement it
configure(consumer: MiddlewareConsumer) {
consumer.apply(AuthenticationMiddleware).forRoutes('/');
}
}

How can I give an option to user to set baseurl?

I have built an app using ionic but my clients will be using different servers for accessing API. How can I give an option to set the base url by the user to call the desired server API?
There are 2 ways:
The temporary way:
This way, when the app is closed, it reset to the default api:
create a service ionic generate service
in this service, make a variable that will have the url you need
make some getter and setter
import this service where you need it (were you change your api, and in your api service)
The permanent way:
Use the file plugin to make, for example, a JSON that you will read/write with the api url in it.
set your base url in environment.ts file and use in any of service
export const environment = {
production: false,
baseUrl: 'http://localhost:3000/'
};
auth.service.ts
import { Injectable } from '#angular/core';
import { Observable} from 'rxjs';
import { HttpClient } from '#angular/common/http';
import { environment } from '../../../environments/environment';
#Injectable({
providedIn: 'root'
})
export class AuthService {
baseUrl = environment.baseUrl;
constructor(
private http: HttpClient
) { }
Userlogin(data: any): Observable<any> {
return this.http.post(this.baseUrl + 'user/login', data);
}
}

Http post and get request in angular 6

In angular 5.2.x for http get and post I had this code:
post(url: string, model: any): Observable<boolean> {
return this.http.post(url, model)
.map(response => response)
.do(data => console.log(url + ': ' + JSON.stringify(data)))
.catch(err => this.handleError(err));
}
get(url: string): Observable<any> {
return this.http.get(url)
.map(response => response)
.do(data =>
console.log(url + ': ' + JSON.stringify(data))
)
.catch((error: any) => Observable.throw(this.handleError(error)));
}
In angular 6 it doesn't work.
How can we make an HTTP post or get request?
Update :
In angular 7, they are the same as 6
In angular 6
the complete answer found in live example
/** POST: add a new hero to the database */
addHero (hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions)
.pipe(
catchError(this.handleError('addHero', hero))
);
}
/** GET heroes from the server */
getHeroes (): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl)
.pipe(
catchError(this.handleError('getHeroes', []))
);
}
it's because of pipeable/lettable operators which now angular is able to use tree-shakable and remove unused imports and optimize the app
some rxjs functions are changed
do -> tap
catch -> catchError
switch -> switchAll
finally -> finalize
more in MIGRATION
and Import paths
For JavaScript developers, the general rule is as follows:
rxjs: Creation methods, types, schedulers and utilities
import { Observable, Subject, asapScheduler, pipe, of, from, interval, merge, fromEvent } from 'rxjs';
rxjs/operators: All pipeable operators:
import { map, filter, scan } from 'rxjs/operators';
rxjs/webSocket: The web socket subject implementation
import { webSocket } from 'rxjs/webSocket';
rxjs/ajax: The Rx ajax implementation
import { ajax } from 'rxjs/ajax';
rxjs/testing: The testing utilities
import { TestScheduler } from 'rxjs/testing';
and for backward compatability you can use rxjs-compat
You can do a post/get using a library which allows you to use HttpClient with strongly-typed callbacks.
The data and the error are available directly via these callbacks.
The library is called angular-extended-http-client.
angular-extended-http-client library on GitHub
angular-extended-http-client library on NPM
Very easy to use.
Traditional approach
In the traditional approach you return Observable<HttpResponse<T>> from Service API. This is tied to HttpResponse.
With this approach you have to use .subscribe(x => ...) in the rest of your code.
This creates a tight coupling between the http layer and the rest of your code.
Strongly-typed callback approach
You only deal with your Models in these strongly-typed callbacks.
Hence, The rest of your code only knows about your Models.
Sample usage
The strongly-typed callbacks are
Success:
IObservable<T>
IObservableHttpResponse
IObservableHttpCustomResponse<T>
Failure:
IObservableError<TError>
IObservableHttpError
IObservableHttpCustomError<TError>
Add package to your project and in your app module
import { HttpClientExtModule } from 'angular-extended-http-client';
and in the #NgModule imports
imports: [
.
.
.
HttpClientExtModule
],
Your Models
export class SearchModel {
code: string;
}
//Normal response returned by the API.
export class RacingResponse {
result: RacingItem[];
}
//Custom exception thrown by the API.
export class APIException {
className: string;
}
Your Service
In your Service, you just create params with these callback types.
Then, pass them on to the HttpClientExt's get method.
import { Injectable, Inject } from '#angular/core'
import { SearchModel, RacingResponse, APIException } from '../models/models'
import { HttpClientExt, IObservable, IObservableError, ResponseType, ErrorType } from 'angular-extended-http-client';
.
.
#Injectable()
export class RacingService {
//Inject HttpClientExt component.
constructor(private client: HttpClientExt, #Inject(APP_CONFIG) private config: AppConfig) {
}
//Declare params of type IObservable<T> and IObservableError<TError>.
//These are the success and failure callbacks.
//The success callback will return the response objects returned by the underlying HttpClient call.
//The failure callback will return the error objects returned by the underlying HttpClient call.
searchRaceInfo(model: SearchModel, success: IObservable<RacingResponse>, failure?: IObservableError<APIException>) {
let url = this.config.apiEndpoint;
this.client.post<SearchModel, RacingResponse>(url, model,
ResponseType.IObservable, success,
ErrorType.IObservableError, failure);
}
}
Your Component
In your Component, your Service is injected and the searchRaceInfo API called as shown below.
search() {
this.service.searchRaceInfo(this.searchModel, response => this.result = response.result,
error => this.errorMsg = error.className);
}
Both, response and error returned in the callbacks are strongly typed. Eg. response is type RacingResponse and error is APIException.
For reading full response in Angular you should add the observe option:
{ observe: 'response' }
return this.http.get(`${environment.serverUrl}/api/posts/${postId}/comments/?page=${page}&size=${size}`, { observe: 'response' });

How do I configure FetchClient to use a non-default api when using Aurelia Auth

I'm setting up aurelia-auth and configured endpoints for my authorization server and a protected api:
aurelia.use.plugin('aurelia-api', configure => {
configure
.registerEndpoint('auth', 'http://localhost:5000/')
.registerEndpoint('api', 'http://localhost:5006')}
When I want to fetch data I inject the AuthService into my module and then call
this.authService.config.client.client.fetch('StaticData/offices')
but this calls against the auth endpoint not the api one, how do I tell the fetch client to use the non-default endpoint?
I was heading down the wrong path, you use the configuration object off aurelia-api to get an endpoint you can then call:
import { inject } from 'aurelia-framework';
import { Config } from 'aurelia-api'
#inject (Config)
export class Locations {
constructor (private apiEndpointConfig: Config)
{}
dataItems;
hasItems: boolean;
created(){
var api = this.apiEndpointConfig.getEndpoint('api');
api.client.fetch('StaticData/offices')
.then(response=>response.json())
.then(response=>
{
this.dataItems=response;
this.hasItems=true;
});
}
}

With Aurelia, is there a way to store data clientside for reuse between view classes?

I have some data that is accessed between multiple views. If there any want to share that? In Angular, I was able to store stuff on a rootscope or parent controller, then it became available to all sub-views/controllers.
I see no reason to continually fetch them.
Services/classes are created as singletons (unless you tell the DI otherwise, I believe) so use a service class/module and inject it into your view controllers.
Then the service class can use internal, cached data or it can depend on, say, the http module and fetch data as needed.
EDIT: Added a bit of a sample:
I doubt this will work directly but it should give the basic idea.
Global service "someGlobalStuff.js":
import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-http-client';
#inject(HttpClient)
export class SomeGlobalStuff {
constructor(http) {
this.http = http;
}
getSomethingVital() {
if (this.somethingVital) {
return Promise.resolve(this.somethingVital)
} else {
// Do something with the HTTP client that will get the
// required stuff and return a promise
return this.http.get(blah blah blah)
.then(r => {
this.somethingVital = r;
return r; //
});
}
}
}
And something that uses it:
import {inject} from 'aurelia-framework';
import {SomeGlobalStuff} from 'someGlobalStuff';
#inject(SomeGlobalStuff)
export class DataManager {
constructor(someGlobalStuff) {
this.globalStuff = someGlobalStuff;
}
doSomething() {
this.globalStuff.getSomethingVital()
.then(v => {
// Do something with it
})
}
}