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

Response builder with support for mapping, data extraction, and metadata addition. It adjusts the entire response system to a unified one, saving you from constant unpacking of data.data and extracting data required for responses from headers and other keys.

Basic usage

Below is the basic usage of this class without inheritance and integration with data source. For studying methods, it's recommended to integrate into Datasource following the examples in the next section.

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

// Creating from HTTP response
const response = await httpService.get<User[]>('/users');
const builder = new ResponseBuilder(response);

// Getting data
const data = builder.getData(); // User[] - simply gets data from response
const full = builder.get(); // { data: User[], meta: {} } - gets data and metadata
const flatten = builder.getFlatten(); // { data: User[], ...meta }
const raw = builder.getRaw(); // HttpServiceResponse - returns raw response from http-service

// Adding metadata (auto-typing)
const withMeta = builder.addMeta({ 
  totalCount: 100,
  currentPage: 1 
});

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

Advanced usage

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

// What backend always sends us in one format for example
export interface IBackendApiDataSourceResponse<T = unknown> {
	data: T;
	success: boolean;
	message: string;
}

// We don't touch TData and TMeta as they are needed for typing generics of backend responses.
// The third generic is for the case when the backend sends us a response in a wrapper that we want to unwrap in advance.
export class CustomApiResponse<TData = unknown, TMeta = unknown> extends ResponseBuilder<TData, TMeta, IBackendApiDataSourceResponse<TData>> {
	// Override of unwrapData method allows us to unwrap data from wrapper, returns clean data
    override unwrapData(data: IBackendApiDataSourceResponse<TData>): TData {
		return data.data;
	}

    // Custom method adding to all responses the ability to add metadata, for example, extract pagination from response in advance and put it in our meta
    // Using this method automatically types the meta for what we return
    // There can be any number of such methods
	paginate() {
		return this.addMeta({ page: Number(this.response.data.data.meta.page), total: Number(this.response.data.data.meta.total) });
	}
}

// Basic usage example, integration through datasource is recommended following the examples in the next section
const paginatedResponse = new CustomApiResponse(response).paginate().get(); //{data: typeof response, meta: {page: number, total: number;}}

Mapping with resources (example using CustomApiResponse from Advanced usage section above)

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

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

// Creating mapper class
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;
        // Call all processing in constructor
        this.fullName = this.getFullName();
    }

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

// Mapping single object
const userBuilder = new CustomApiResponse(response);
const mappedUser = userBuilder.mapUsing(UserDTO).getData(); // IMappedUser

// Mapping collection
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;}}

Extracting nested data

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

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

// Extract only the token from response and return it in data
const users = builder.extract('token').getData(); // string;

// Extracting nested value
const total = builder.extract('expires.at'); // string  ILoginResponse['expires']['at']