Problem: Create a Fluent Pizza Order Builder

easy
15 min
Build a customizable pizza order using a step-by-step fluent interface.

Problem statement

Your team is building a food ordering backend. Currently, pizza orders are assembled with plain objects and optional fields, such as size, toppings, and delivery instructions—resulting in messy and inconsistent code.

You’ve been asked to provide a clean and expressive builder for PizzaOrder. It should allow developers to fluently build a pizza object with optional toppings and notes, but always enforce a selected size.

Goal

Create a PizzaBuilder class with the following methods:

  • .withSize(size: string): Required, must be set before build.

  • .addTopping(topping: string): Can be called multiple times.

  • .withNote(note: string): Optional special instructions.

  • .build(): Returns the final object with shape: { size, toppings: string[], note? }.

If .withSize() was never called, .build() should throw an error.

Constraints

  • Do not prefill defaults for size.

  • Do not allow direct mutation of the returned object.

  • Support chaining regardless of method call order.

Sample output

The examples below illustrate what the output should look like:

// Valid pizza with toppings and note
const order1 = new PizzaBuilder()
.addTopping('pepperoni')
.addTopping('mushrooms')
.withSize('large')
.withNote('extra crispy')
.build();
console.log(order1);
/* Expected output:
{ size: 'large',
toppings: [ 'pepperoni', 'mushrooms' ],
note: 'extra crispy' } */
// Missing size
try {
const order2 = new PizzaBuilder()
.addTopping('cheese')
.withNote('no garlic')
.build();
} catch (err) {
console.log('Failed to build order2:', err.message);
}
/* Expected output:
Failed to build order2: Pizza size is required */
// Valid pizza with just size
const order3 = new PizzaBuilder()
.withSize('medium')
.build();
console.log(order3);
/* Expected output:
{ size: 'medium', toppings: [] } */
// Valid pizza with toppings only
const order4 = new PizzaBuilder()
.withSize('small')
.addTopping('olives')
.addTopping('onions')
.build();
console.log(order4);
/* Expected output:
{ size: 'small', toppings: [ 'olives', 'onions' ] } */

Good luck trying the problem! If you’re unsure how to proceed, check the “Solution” tab above.

Problem: Create a Fluent Pizza Order Builder

easy
15 min
Build a customizable pizza order using a step-by-step fluent interface.

Problem statement

Your team is building a food ordering backend. Currently, pizza orders are assembled with plain objects and optional fields, such as size, toppings, and delivery instructions—resulting in messy and inconsistent code.

You’ve been asked to provide a clean and expressive builder for PizzaOrder. It should allow developers to fluently build a pizza object with optional toppings and notes, but always enforce a selected size.

Goal

Create a PizzaBuilder class with the following methods:

  • .withSize(size: string): Required, must be set before build.

  • .addTopping(topping: string): Can be called multiple times.

  • .withNote(note: string): Optional special instructions.

  • .build(): Returns the final object with shape: { size, toppings: string[], note? }.

If .withSize() was never called, .build() should throw an error.

Constraints

  • Do not prefill defaults for size.

  • Do not allow direct mutation of the returned object.

  • Support chaining regardless of method call order.

Sample output

The examples below illustrate what the output should look like:

// Valid pizza with toppings and note
const order1 = new PizzaBuilder()
.addTopping('pepperoni')
.addTopping('mushrooms')
.withSize('large')
.withNote('extra crispy')
.build();
console.log(order1);
/* Expected output:
{ size: 'large',
toppings: [ 'pepperoni', 'mushrooms' ],
note: 'extra crispy' } */
// Missing size
try {
const order2 = new PizzaBuilder()
.addTopping('cheese')
.withNote('no garlic')
.build();
} catch (err) {
console.log('Failed to build order2:', err.message);
}
/* Expected output:
Failed to build order2: Pizza size is required */
// Valid pizza with just size
const order3 = new PizzaBuilder()
.withSize('medium')
.build();
console.log(order3);
/* Expected output:
{ size: 'medium', toppings: [] } */
// Valid pizza with toppings only
const order4 = new PizzaBuilder()
.withSize('small')
.addTopping('olives')
.addTopping('onions')
.build();
console.log(order4);
/* Expected output:
{ size: 'small', toppings: [ 'olives', 'onions' ] } */

Good luck trying the problem! If you’re unsure how to proceed, check the “Solution” tab above.

Node.js
class PizzaBuilder {
// implement methods here
}
// Example usage:
// Valid pizza with toppings and note
const order1 = new PizzaBuilder()
.addTopping('pepperoni')
.addTopping('mushrooms')
.withSize('large')
.withNote('extra crispy')
.build();
console.log(order1);
/* Expected output:
{ size: 'large',
toppings: [ 'pepperoni', 'mushrooms' ],
note: 'extra crispy' } */
// Missing size
try {
const order2 = new PizzaBuilder()
.addTopping('cheese')
.withNote('no garlic')
.build();
} catch (err) {
console.log('Failed to build order2:', err.message);
}
/* Expected output:
Failed to build order2: Pizza size is required */
// Valid pizza with just size
const order3 = new PizzaBuilder()
.withSize('medium')
.build();
console.log(order3);
/* Expected output:
{ size: 'medium', toppings: [] } */
// Valid pizza with toppings only
const order4 = new PizzaBuilder()
.withSize('small')
.addTopping('olives')
.addTopping('onions')
.build();
console.log(order4);
/* Expected output:
{ size: 'small', toppings: [ 'olives', 'onions' ] } */