Builder Design Pattern
The Builder design pattern is a creational pattern that allows you to construct complex objects step by step. It’s particularly useful when you need to create an object that requires multiple steps or configurations. In TypeScript, the Builder pattern can be implemented in a way that maintains type safety and clarity.
Let's consider a practical software scenario: Building an API Request for an E-commerce Platform.
In an e-commerce platform, you might need to build API requests to interact with the backend, such as fetching products, searching for items, filtering by categories, or sorting by price. Instead of manually constructing these requests, you can use the Builder Pattern to create a flexible and reusable solution.
Problem
We need to construct a GET request to fetch products from the backend API. The request might include various parameters such as search terms, filters for categories, price range, sorting options, and pagination details.
Solution: ApiRequestBuilder
Let’s create an ApiRequestBuilder class to handle this.
export class APIRequest {
private url: string;
private params: URLSearchParams;
constructor(baseURL: string, params: URLSearchParams) {
this.url = `${baseURL}?${params.toString()}`;
this.params = params;
}
getURL(): string {
return this.url;
}
getParams(): URLSearchParams {
return this.params;
}
}
export class ApiRequestBuilder {
private baseURL: string = "";
private params: URLSearchParams = new URLSearchParams();
setBaseURL(url: string): this {
this.baseURL = url;
return this;
}
addSearchTerm(term: string): this {
this.params.append("search", term);
return this;
}
addCategoryFilter(category: string): this {
this.params.append("category", category);
return this;
}
addPriceRange(min: number, max: number): this {
this.params.append("minPrice", min.toString());
this.params.append("maxPrice", max.toString());
return this;
}
addSortBy(field: string, direction: "ASC" | "DESC" = "ASC"): this {
this.params.append("sortBy", field);
this.params.append("direction", direction);
return this;
}
addPagination(page: number, limit: number): ApiRequestBuilder {
this.params.append("page", page.toString());
this.params.append("limit", limit.toString());
return this;
}
build(): APIRequest {
return new APIRequest(this.baseURL, this.params);
}
}
We discuss three practical use-cases in the code sample below.
import { ApiRequestBuilder } from "./ApiRequestBuilder";
function main() {
const builder = new ApiRequestBuilder();
const searchRequest = builder
.setBaseURL("https://api.demo.com/products")
.addSearchTerm("mobile")
.addCategoryFilter("5G")
.build();
console.log(searchRequest.getURL());
const sortedRequest = new APIRequestBuilder()
.setBaseURL('https://api.demo.com/products')
.addSortBy('price', 'DESC')
.addPagination(2, 10)
.build();
console.log(sortedRequest.getURL());
const complexRequest = new APIRequestBuilder()
.setBaseURL('https://api.example.com/products')
.addSearchTerm('smartphone')
.addCategoryFilter('mobile')
.addPriceRange(200, 800)
.addSortBy('rating', 'ASC')
.build();
console.log(complexRequest.getURL());
}
main();
Practical Use Case: E-commerce API Requests
1. Fetch Products by Search Term and Category
Imagine you want to fetch products that match the search term "mobile" and belong to the "5G" category.
The generated URL
https://api.demo.com/products?search=mobile&category=5G
2. Fetch Products Sorted by Price with Pagination
Now, let’s fetch products, sorted by price in descending order, and retrieve the second page with 10 items per page.
The generated URL
https://api.demo.com/products?sortBy=price&direction=DESC&page=2&limit=10
3. Fetch Products with Complex Filtering
Suppose you want to search for "smartphone" in the "mobile" category, with a price range between Rs.24999 and Rs.33999, sorted by rating in ascending order.
The generated URL
https://api.demo.com/products?search=smartphone&category=mobile&minPrice=24999&maxPrice=33999&sortBy=rating&direction=ASC
Summary of Builder Pattern in Use
baseURL: Stores the base URL for the API request.
params: Collects all the query parameters like search terms, filters, sorting, and pagination.
build(): Combines the base URL and parameters into a final API request URL.
Conclusion
In this e-commerce scenario, the APiRequestBuilder class provides a clear and structured way to build complex API requests. It allows you to add or modify query parameters dynamically and ensures that your code remains clean and maintainable.
This is a practical example of how the Builder Pattern can be used in real-world software development, providing flexibility and reusability in constructing API requests.
The GITHUB LINK for the code samples.