# Configuraited http service ###### tags: `Typescript` > Пример конфигурироваемого Http service для фронтенд проекта. Сначала надо определить конфиг для апишки ```typescript= interface IApiUrls { baseApiUrl: string; [key: string]: string; } interface IConfig { [key: string]: { host: string; [key: string]: string; }; } export interface IApiConfig { api: IConfig; } function getConfig(urls: IApiUrls, cachingLifeTime?: number): IApiConfig { return { api: Object.assign({ caching: { lifetime: cachingLifeTime !== undefined ? cachingLifeTime : 5 * 1000, // 5 sec }, }, getApiConfig(urls)), }; } function getApiConfig({ baseApiUrl }: IApiUrls): IConfig { return { signing: { host: `${baseApiUrl}/signing/v2/`, statusByAccessToken: 'sessions/status/:accessToken', updateSubjectInfo: 'sessions/:sessionId/tasks/updateSubjectInfo', sessionTask: 'sessions/:sessionId/tasks/:taskId', uploadSignature: 'signature', signingTaskExit: 'sessions/status/:accessToken/exit', }, }; } export default { getConfig, }; ``` Потом определяем файл с конфигом для всего проекта и импортим туда конфиг апишки ```typescript= import _merge from 'lodash/merge'; import defaultConfig from './default'; import defaultApi from './default-api'; module.exports = _merge( {}, defaultConfig, defaultApi.getConfig({ baseApiUrl: defaultConfig.baseApiUrl }), { isSsr: true, }, ); ``` Далее делаем api service который будет работать с этим конифгом. Вам понадобиться HttpService это [классический сервис описанный в этой статейке](https://hackmd.io/@devhouse/classic-http-service) ```typescript= import { HttpService } from './http.service'; import { AxiosError, AxiosRequestConfig, AxiosResponse, Method } from 'axios'; import * as Sentry from '@sentry/browser'; import _forOwn from 'lodash/forOwn'; import _merge from 'lodash/merge'; import _get from 'lodash/get'; import _isUndefined from 'lodash/isUndefined'; import { ENVS } from '../constants/common'; class ApiServiceClass { private readonly _http = HttpService; public defaultOptions: AxiosRequestConfig; constructor(private _appConfig: any) { if (!this._appConfig) { throw new Error('Api config is not defined'); } else { this.defaultOptions = this._appConfig.api.options; } } public get<T = Object>(apiKey: string, pathKey: string, urlParams?: {}, options?: AxiosRequestConfig): Promise<T> { const url = this.buildUrl(apiKey, pathKey, urlParams); if (!url) { throw new Error(`url is not found please check keys, ${apiKey}, ${pathKey}`); } return this.handleResponse<T>(this._http.get<T>(url, this.getOptions(options))); } public post<T = Object>(apiKey: string, pathKey: string, urlParams?: {}, body?: {}, options?: AxiosRequestConfig): Promise<T> { return this._payloadRequest.apply(this, ['post', apiKey, pathKey, urlParams, body, options]) as Promise<T>; } public patch<T = Object>(apiKey: string, pathKey: string, urlParams?: {}, body?: {}, options?: AxiosRequestConfig): Promise<T> { return this._payloadRequest.apply(this, ['patch', apiKey, pathKey, urlParams, body, options]) as Promise<T>; } public put<T = Object>(apiKey: string, pathKey: string, urlParams?: {}, body?: {}, options?: AxiosRequestConfig): Promise<T> { return this._payloadRequest.apply(this, ['put', apiKey, pathKey, urlParams, body, options]) as Promise<T>; } public remove<T = Object>(apiKey: string, pathKey: string, urlParams?: {}, options?: AxiosRequestConfig): Promise<T> { const url = this.buildUrl(apiKey, pathKey, urlParams); if (!url) { throw new Error(`url is not found please check keys, ${apiKey}, ${pathKey}`); } return this.handleResponse(this._http.delete(url, this.getOptions(options))); } public getOptions(options: AxiosRequestConfig = {}): AxiosRequestConfig { let requestOptions = this.defaultOptions; if (options) { requestOptions = _merge({}, this.defaultOptions, options); } return requestOptions; } public handleResponse<T>(promise: Promise<AxiosResponse<T>>): Promise<T> { return promise .then((res: AxiosResponse<T>) => { return res.data; }) .catch((err: AxiosError) => { Sentry.captureException(err); throw err; }); } public buildUrl(apiKey: string, pathKey: string, urlParams: {} = {}): string { if (apiKey.indexOf('api.') !== 0) { apiKey = 'api.' + apiKey; } const api = _get(this._appConfig, apiKey); const path = _get(api, pathKey); if (_isUndefined(api) || _isUndefined(path)) { console .error(`apiKey: '${apiKey}' or pathKey '${pathKey}' were not found in config, ${JSON.stringify(this._appConfig)}`); Sentry .captureException(`apiKey: '${apiKey}' or pathKey '${pathKey}' were not found in config, ${JSON.stringify(this._appConfig)}`); return ''; } return api.host + this._fillUrl(path, urlParams); } private _payloadRequest<T>(method: Method, apiKey: string, pathKey: string, urlParams?: {}, body?: any, options?: AxiosRequestConfig): Promise<T> { const url = this.buildUrl(apiKey, pathKey, urlParams); if (!url) { throw new Error(`url is not found please check keys, ${apiKey}, ${pathKey}`); } return this.handleResponse( this._http.request( this.getOptions({ method, url, data: body, ...options, }), ), ); } private _fillUrl(url: string, params: {}): string { _forOwn(params, (value, key) => { url = url.replace(new RegExp(':' + key), value); }); return url; } } /*** * TODO * Improve get configs by env */ let environment_path = 'environment'; if (process.env.ENV === ENVS.DEV) { environment_path += '.dev'; } if (process.env.ENV === ENVS.STAGE) { environment_path += '.stage'; } if (process.env.ENV === ENVS.PROD) { environment_path += '.prod'; } const environment = require(`../../environments/${environment_path}`); export const getSigningApiAuthToken = (): string => { return process.env.SIGNING_API_AUTH_TOKEN || 'XXXXX'; }; export const ApiService = new ApiServiceClass(environment); ``` И потом это можно вот так использовать ```typescript= import { ApiService, getSigningApiAuthToken } from '../services/api.service'; import { ISession } from './interfaces/i-session'; import { IUpdateSignerInfoPayload } from './interfaces/i-update-signer-info-payload'; import { IUploadSignaturePayload } from './interfaces/i-upload-signature-payload'; export class SigningApiService { public static getSessionByAccessToken(accessToken: string, remoteIpAddress?: string): Promise<ISession> { return ApiService.get('signing', 'statusByAccessToken', { accessToken }, { headers: { Authorization: 'Bearer ' + getSigningApiAuthToken(), 'X-Forwarded-For': remoteIpAddress } }); } public static updateSignerInfo(sessionId: string, payload: IUpdateSignerInfoPayload): Promise<ISession> { return ApiService.put('signing', 'updateSubjectInfo', { sessionId }, payload, { headers: { Authorization: 'Bearer ' + getSigningApiAuthToken(), } }); } public static uploadSignature(payload: IUploadSignaturePayload): Promise<{redirectUrl: string}> { const formData = new FormData(); formData.append('signatureFile', payload.signatureFile, payload.signatureFile.name); formData.append('signatureInput', JSON.stringify({ accessToken: payload.accessToken, signerName: payload.signerName, personalNumber: payload.personalNumber, })); return ApiService.post('signing', 'uploadSignature', {}, formData, { headers: { 'Content-Type': 'multipart/form-data', Accept: 'application/json', Authorization: 'Bearer ' + getSigningApiAuthToken(), } }); } public static taskExit(accessToken: string): Promise<{ redirectUrl: string }> { return ApiService.post('signing', 'signingTaskExit', { accessToken }, {}, { headers: { Authorization: 'Bearer ' + getSigningApiAuthToken(), } }); } } ```