Builder Pattern
In this page:
Basic Builder
A basic builder is a class that accumulates configuration across multiple method calls before a final .build() method assembles and returns the fully constructed object, instead of a single large constructor taking every option at once.
Example: Basic Builder
class PizzaBuilder {
private toppings: string[] = [];
addTopping(topping: string): this {
this.toppings.push(topping);
return this;
}
build(): string {
return `Pizza with ${this.toppings.join(", ")}`;
}
}
console.log(new PizzaBuilder().addTopping("cheese").build());
Fluent Methods
Fluent methods on a builder each return this, which is what allows chaining calls together — new Builder().setA(1).setB(2).build() — into one readable expression instead of separate statements.
Example: Fluent Methods
class RequestBuilder {
private url = "";
private method = "GET";
setUrl(url: string): this { this.url = url; return this; }
setMethod(method: string): this { this.method = method; return this; }
build() { return `${this.method} ${this.url}`; }
}
console.log(new RequestBuilder().setUrl("/api").setMethod("POST").build());
Optional Values
Marking builder fields optional in the internal state models values that don't have to be set before calling .build(), letting the builder supply sensible defaults for whatever the caller left unspecified.
Example: Optional Values
class UserBuilder {
private name = "";
private age?: number;
setName(name: string): this { this.name = name; return this; }
setAge(age: number): this { this.age = age; return this; }
build() { return { name: this.name, age: this.age ?? 0 }; }
}
console.log(new UserBuilder().setName("Ravi").build());
Validation
A builder's .build() method is a natural place to validate that all required fields were actually set, throwing a clear error before an invalid object gets constructed rather than after.
Example: Validation
class UserBuilder {
private name?: string;
setName(name: string): this { this.name = name; return this; }
build() {
if (!this.name) throw new Error("name is required");
return { name: this.name };
}
}
console.log(new UserBuilder().setName("Ravi").build());
When to Use Builder
Reach for the builder pattern when an object has many optional configuration fields — it avoids both an unwieldy multi-argument constructor and an easy-to-misuse partially-filled object passed around before it's ready.
Example: When to Use Builder
class QueryBuilder {
private parts: string[] = [];
select(cols: string): this { this.parts.push(`SELECT ${cols}`); return this; }
from(table: string): this { this.parts.push(`FROM ${table}`); return this; }
build() { return this.parts.join(" "); }
}
console.log(new QueryBuilder().select("*").from("users").build());
Chapter Quiz — Complete all 7 topics to unlock
0/7 topics done
Complete these topics first: