Skip to content

Response Builder

  • import: @azure-net/kit/infra
  • Preferred location (for base shared responses): src/app/core/responses
  • Preferred location (for context): src/app/contexts/{ContextName}/infrastructure/http/responses

Построитель ответов с поддержкой маппинга, извлечения данных и добавления метаданных, подстраивает всю систему ответов под единую, избавляя от постоянных распаковок data.data и извлечения данных, требуемых для ответа, из заголовков и других ключей.

Основное использование

Ниже представлено базовое использование этого класса без наследования и интеграции с источником данных. Для изучения методов рекомендуется интеграция с Datasource по примерам в следующем разделе.

typescript
import { ResponseBuilder } from '@azure-net/kit/infra';

// Создание из HTTP ответа
const response = await httpService.get<User[]>('/users');
const builder = new ResponseBuilder(response);

// Получение данных
const data = builder.getData(); // User[] - просто получает data из ответа
const full = builder.get(); // { data: User[], meta: {} } - получает data и метаданные
const flatten = builder.getFlatten(); // { data: User[], ...meta }
const raw = builder.getRaw(); // HttpServiceResponse - отдает голый ответ от http-сервиса

// Добавление метаданных (автотипизация)
const withMeta = builder.addMeta({ 
  totalCount: 100,
  currentPage: 1 
});

console.log(withMeta.get());
// { data: User[], meta: { totalCount: 100, currentPage: 1 } }

Расширенное использование

typescript
import { ResponseBuilder } from '$lib/core/infra/index.js';

// То, что нам присылает бэкенд, всегда в одном формате, например:
export interface IBackendApiDataSourceResponse<T = unknown> {
	data: T;
	success: boolean;
	message: string;
}

// TData и TMeta мы не трогаем, так как они нужны для типизации дженериками ответов с бэка.
// Третий дженерик — это случай, когда бэк посылает нам ответ в обёртке, которую мы хотим распаковать заранее
export class CustomApiResponse<TData = unknown, TMeta = unknown> extends ResponseBuilder<TData, TMeta, IBackendApiDataSourceResponse<TData>> {
	// override метода unwrapData позволяет нам распаковывать данные из обёртки, возвращает чистую data
    override unwrapData(data: IBackendApiDataSourceResponse<TData>): TData {
		return data.data;
	}

    // Кастомный метод, добавляющий во все ответы возможность добавить метаданные, например извлечь заранее пагинацию из ответа и поместить её в мету
    // Использование данного метода автоматически типизирует мету под то, что у нас возвращается
    // Таких методов может быть сколько угодно
	paginate() {
		return this.addMeta({ page: Number(this.response.data.data.meta.page), total: Number(this.response.data.data.meta.total) });
	}
}

// Базовый пример использования, рекомендуется интеграция через datasource по примерам в следующем разделе
const paginatedResponse = new CustomApiResponse(response).paginate().get(); //{data: typeof response, meta: {page: number, total: number;}}

Маппинг с ресурсами (пример с использованием CustomApiResponse из раздела Расширенное использование выше)

typescript
interface IUser {
    id: number;
    name: string;
    email: string;
}

interface IMappedUser {
    id: number;
    firstName: string;
    lastName: string;
    email: string;
    status: number;
    fullName: string;
}

// Создание класса-маппера
class UserDTO extends DTOMapper<IMappedUser> {
    id: number;
    name: string;
    lastName: string;
    email: string;
    status: number;
    fullName: string;

    constructor(data: IUser) {
        super();
        this.id = data.id;
        this.name = data.name;
        this.lastName = 'SomeTestLastname';
        this.email = data.email;
        this.status = 1;
        // все обработки вызываем в конструкторе
        this.fullName = this.getFullName();
    }

    getFullName() {
        return `${this.name} ${this.lastName}`;
    }
}

// Маппинг одиночного объекта
const userBuilder = new CustomApiResponse(response);
const mappedUser = userBuilder.mapUsing(UserDTO).getData(); // IMappedUser

// Маппинг коллекции
const usersResponse = await httpService.get<User[]>('/users');
const usersBuilder = new CustomApiResponse(usersResponse);
const mappedUsers = usersBuilder.paginate().mapCollectionUsing(UserDTO).get(); //{data: IMappedUser[], meta: {page: number, total: number;}}

Извлечение вложенных данных

typescript
interface ILoginResponse {
    token: string;
    type: 'Bearer';
    expires: {
        at: string;
    }
}

const response = await httpService.post<ILoginResponse>('/login', {json: someJson});
const builder = new ResponseBuilder(response);

// Извлечь только токен из ответа и вернуть в data
const users = builder.extract('token').getData(); // string;

// Извлечение вложенного значения
const total = builder.extract('expires.at'); // string  ILoginResponse['expires']['at']