@angular-ru/cdk/http

DataHttpClientModule

Custom http client, with the ability to customize requests, auto unsubscribe and additional request interceptors.

Table of contents:

First step

Example, if your API base url placed here https://my-server.com/api/*** and have swagger documentation:

import { HttpClientModule } from '@angular/common/http';
import { DataHttpClientModule } from '@angular-ru/cdk/http';

@NgModule({
    imports: [
        // ...
        HttpClientModule,
        DataHttpClientModule.forRoot([ApiUsersClient], {
            hostUrl: 'https://my-server.com/api/'
        })
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})
export class AppModule {}

Create your http client for your api controller

  • user.interface.ts

export interface User {
    id: number;
    name: string;
}
  • api-users.client.ts

import { Delete, Get, Patch, PathVariable, RequestBody, Put, RestClient } from '@angular-ru/cdk/http/decorators';
import { DataHttpClient } from '@angular-ru/cdk/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable()
@RestClient('/users')
export class ApiUsersClient extends DataHttpClient {
    @Get()
    public findAllUsers(): Observable<User[]> {
        return this.restTemplate();
    }

    @Post()
    public createUser(@RequestBody() _body: User): Observable<void> {
        return this.restTemplate();
    }

    @Get('/{id}')
    public findByIdUser(@PathVariable('id') _id: number): Observable<User> {
        return this.restTemplate();
    }

    @Put('/{id}')
    public updateUser(@PathVariable('id') _id: number, @RequestBody() _body: User): Observable<void> {
        return this.restTemplate();
    }

    @Delete('/{id}')
    public deleteByIdUser(@PathVariable('id') _id: number): Observable<void> {
        return this.restTemplate();
    }

    @Patch('/{id}')
    public mutateUser(@PathVariable('id') _id: number, @RequestBody() _body: Partial<User>): Observable<void> {
        return this.restTemplate();
    }
}
  • app.component.ts

@Component({
    //...
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class UsersComponent {
    private readonly users: User[] = [];

    constructor(private readonly users: ApiUsersClient, private readonly cd: ChangeDetectorRef) {}

    public ngOnInit(): void {
        this.users.findAllUsers().subscribe((users) => {
            this.users = users;
            this.cd.detectChanges();
        });
    }
}

Different use cases

each of these examples works the same

@Injectable()
@RestClient('/cities')
class MyCitiesClient extends DataHttpClient {
    @Put()
    public updateCity(@RequestBody() _body: CityRecordDto, @RequestParam('id') _id: number): Observable<void> {
        return this.restTemplate({ emitSuccess: true });
    }
}
@Injectable()
@RestClient('/cities')
class MyCitiesClient extends DataHttpClient {
    @Put()
    public updateCity(body: CityRecordDto, id: number): Observable<void> {
        return this.restTemplate({ emitSuccess: true, body, queryParams: { id } });
    }
}
@Injectable()
@RestClient('/cities')
class MyCitiesClient extends DataHttpClient {
    public updateCity(body: CityRecordDto, id: number): Observable<void> {
        return this.put({ emitSuccess: true, body, queryParams: { id } });
    }
}

Limiting the number of concurrent requests (optional)

there is almost no limit on the number of requests that can be sent in parallel Note: various browsers have various limits for maximum connections per host name (Chrome: 6)

but if necessary, you can change it for example, limitConcurrency: 5 This mean that maximum of 5 requests can be executed in parallel. Next one immediately start only if one of the previous requests is completed

  • app.module.ts

import { DataHttpClientModule } from '@angular-ru/cdk/http';

@NgModule({
    imports: [
        // ...
        DataHttpClientModule.forRoot([ApiUsersClient], {
            // ...
            limitConcurrency: 5
        })
    ]
    // ...
})
export class AppModule {}

@angular-ru/cdk/http/decorators

  • @RestClient(url)

@Injectable()
@RestClient('my-controller-api-path')
export class ApiEtcClient extends DataHttpClient {}
  • @BaseUrl(url)

@Injectable()
@BaseUrl('nginx-path-controller')
@RestClient('my-controller-api-path')
export class ApiEtcClient extends DataHttpClient {}
  • @HostUrl(url)

@Injectable()
@HostUrl('//no-cors.my-api.com')
@BaseUrl('nginx-path-controller')
@RestClient('my-controller-api-path')
export class ApiEtcClient extends DataHttpClient {}
  • @Get(url), @Post(url), @Put(url), @Delete(url), @Patch(url)

@Injectable()
@RestClient('users')
export class ApiUsersClient extends DataHttpClient {
    @Get()
    public getUsersAll(): Observable<User[]> {
        return this.restTemplate();
    }
}
  • @RequestParam(key)

@Injectable()
@RestClient('users')
export class ApiUsersClient extends DataHttpClient {
    @Get()
    public getUsersAllByPagination(
        @RequestParam('size') _pageSize: number,
        @RequestParam('index') _pageIndex: number
    ): Observable<User[]> {
        return this.restTemplate();
    }

    // ...
}
  • @PathVariable(key)

@Injectable()
@RestClient('users')
export class ApiUsersClient extends DataHttpClient {
    @Get('/{id}')
    public getUserById(@PathVariable('id') _userId: number): Observable<User> {
        return this.restTemplate();
    }
}
  • @RequestBody()

@Injectable()
@RestClient('users')
export class ApiUsersClient extends DataHttpClient {
    @Post()
    public createUser(@RequestBody() _body: User): Observable<User> {
        return this.restTemplate();
    }
}

@angular-ru/cdk/http/utils

  • isLocalhost

expect(isLocalhost('https://127.0.0.1:4200')).toEqual(true);
expect(isLocalhost('https://google.com')).toEqual(false);
  • getPathWithoutQueryParams

expect(getPathWithoutQueryParams('http://hello/world/todo/1/all?pageSize=10&pageIndex=0')).toEqual(
    'http://hello/world/todo/1/all'
);
  • getUrlSegments

expect(getUrlSegments({})).toEqual({ hostUrl: 'http://localhost/', baseUrl: '' });
expect(getUrlSegments({ hostUrl: 'http://hello_world', baseUrl: 'api' })).toEqual({
    hostUrl: 'http://hello_world/',
    baseUrl: '/api/'
});
  • isAbsolutePath

expect(isAbsolutePath('/api')).toEqual(false);
expect(isAbsolutePath('//hello_world')).toEqual(false);
expect(isAbsolutePath('http://hello_world')).toEqual(true);
  • replaceDoubleSlash

expect(replaceDoubleSlash('https://a///b//c/d/')).toEqual('https://a/b/c/d/');
expect(replaceDoubleSlash('////a///b//c/d/')).toEqual('/a/b/c/d/');
  • replaceLeadingAndTrailingSlashes

expect(replaceLeadingAndTrailingSlashes('/')).toEqual('');
expect(replaceLeadingAndTrailingSlashes('//')).toEqual('');
expect(replaceLeadingAndTrailingSlashes('//a///b//c/d/')).toEqual('a/b/c/d');
  • urlParse

expect(urlParse('////a///b//c/d?quick', getUrlSegments({ hostUrl: 'https://127.0.0.0:8030' }))).toEqual(
    'https://127.0.0.0:8030/a/b/c/d'
);
  • getHttpHeader

const headers: HttpHeaders = getHttpHeader({ a: '1', b: '2' });
expect(headers.keys()).toEqual(['a', 'b']);
expect(headers.get('a')).toEqual('1');
expect(headers.get('b')).toEqual('2');
  • parseQueryParams

const queryParams: PlainObject = parseQueryParams('/todos/get?pageSize=5&value=2');
expect(queryParams).toEqual({ pageSize: '5', value: '2' });
  • getHttpParams

const params: HttpParams = getHttpParams('/todos/get?pageSize=5&value=2', { pageIndex: 0 });
expect(params.keys()).toEqual(['pageSize', 'value', 'pageIndex']);
expect(params.get('pageSize')).toEqual('5');
expect(params.get('value')).toEqual('2');
expect(params.get('pageIndex')).toEqual(0);

Last updated