Problem: Construct a Form Schema Builder with Validation Rules

Hard
40 min
Build a fluent, nested form schema builder that supports field-level validation rules and serializes to JSON.

Problem statement

Your team is building a dynamic form engine. Forms are described using JSON schemas, where each field has a name, type, and optional validation rules, such as required, minLength, maxLength, etc.

Currently, developers construct these schemas manually using raw objects. You’ve been asked to build a fluent FormSchemaBuilder that cleanly assembles field definitions, including embedded validation logic.

Goal

Implement a FormSchemaBuilder with:

  • .addField(name: string): Creates a new field, returns a FieldBuilder.

  • FieldBuilder.withType(type: string): Sets the field type (e.g., 'string', 'number').

  • FieldBuilder.required(): Marks the field as required.

  • FieldBuilder.minLength(n: number): String only.

  • FieldBuilder.maxLength(n: number): String only.

  • FieldBuilder.done(): Finalizes the field and returns to FormSchemaBuilder.

  • .build(): Returns a JSON object with the following structure.

{
fields: {
username: {
type: 'string',
required: true,
minLength: 3,
maxLength: 20
},
age: {
type: 'number'
}
}
}

Constraints

  • Do not allow .build() until all added fields have called .done().

  • Do not allow duplicate field names.

  • Only include validation keys that were explicitly set.

Sample output

The examples below illustrate what the output should look like:

// Valid schema with rules
const schema1 = new FormSchemaBuilder()
.addField('username')
.withType('string')
.required()
.minLength(3)
.maxLength(20)
.done()
.addField('age')
.withType('number')
.done()
.build();
console.log(schema1);
/* Expected output:
{ fields:
{ username:
{ type: 'string', required: true, minLength: 3, maxLength: 20 },
age: { type: 'number' } } } */
// Invalid: build called before .done()
try {
const builder = new FormSchemaBuilder();
builder.addField('email').withType('string');
builder.build(); // should throw
} catch (err) {
console.log('Failed to build schema2:', err.message);
}
/* Expected output:
Failed to build schema2: Cannot build schema before calling .done() on the last field */
Invalid: duplicate field
try {
new FormSchemaBuilder()
.addField('email')
.withType('string')
.done()
.addField('email')
.withType('string')
.done();
} catch (err) {
console.log('Failed to build schema3:', err.message);
}
/* Expected output:
Failed to build schema3: Field "email" already defined */

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

Problem: Construct a Form Schema Builder with Validation Rules

Hard
40 min
Build a fluent, nested form schema builder that supports field-level validation rules and serializes to JSON.

Problem statement

Your team is building a dynamic form engine. Forms are described using JSON schemas, where each field has a name, type, and optional validation rules, such as required, minLength, maxLength, etc.

Currently, developers construct these schemas manually using raw objects. You’ve been asked to build a fluent FormSchemaBuilder that cleanly assembles field definitions, including embedded validation logic.

Goal

Implement a FormSchemaBuilder with:

  • .addField(name: string): Creates a new field, returns a FieldBuilder.

  • FieldBuilder.withType(type: string): Sets the field type (e.g., 'string', 'number').

  • FieldBuilder.required(): Marks the field as required.

  • FieldBuilder.minLength(n: number): String only.

  • FieldBuilder.maxLength(n: number): String only.

  • FieldBuilder.done(): Finalizes the field and returns to FormSchemaBuilder.

  • .build(): Returns a JSON object with the following structure.

{
fields: {
username: {
type: 'string',
required: true,
minLength: 3,
maxLength: 20
},
age: {
type: 'number'
}
}
}

Constraints

  • Do not allow .build() until all added fields have called .done().

  • Do not allow duplicate field names.

  • Only include validation keys that were explicitly set.

Sample output

The examples below illustrate what the output should look like:

// Valid schema with rules
const schema1 = new FormSchemaBuilder()
.addField('username')
.withType('string')
.required()
.minLength(3)
.maxLength(20)
.done()
.addField('age')
.withType('number')
.done()
.build();
console.log(schema1);
/* Expected output:
{ fields:
{ username:
{ type: 'string', required: true, minLength: 3, maxLength: 20 },
age: { type: 'number' } } } */
// Invalid: build called before .done()
try {
const builder = new FormSchemaBuilder();
builder.addField('email').withType('string');
builder.build(); // should throw
} catch (err) {
console.log('Failed to build schema2:', err.message);
}
/* Expected output:
Failed to build schema2: Cannot build schema before calling .done() on the last field */
Invalid: duplicate field
try {
new FormSchemaBuilder()
.addField('email')
.withType('string')
.done()
.addField('email')
.withType('string')
.done();
} catch (err) {
console.log('Failed to build schema3:', err.message);
}
/* Expected output:
Failed to build schema3: Field "email" already defined */

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

Node.js
class FormSchemaBuilder {
// implement builder logic here
}
class FieldBuilder {
// implement field-level chaining logic here
}
// Example usage:
// Valid schema with rules
const schema1 = new FormSchemaBuilder()
.addField('username')
.withType('string')
.required()
.minLength(3)
.maxLength(20)
.done()
.addField('age')
.withType('number')
.done()
.build();
console.log(schema1);
/* Expected output:
{ fields:
{ username:
{ type: 'string', required: true, minLength: 3, maxLength: 20 },
age: { type: 'number' } } } */
// Invalid: build called before .done()
try {
const builder = new FormSchemaBuilder();
builder.addField('email').withType('string');
builder.build(); // should throw
} catch (err) {
console.log('Failed to build schema2:', err.message);
}
/* Expected output:
Failed to build schema2: Cannot build schema before calling .done() on the last field */
Invalid: duplicate field
try {
new FormSchemaBuilder()
.addField('email')
.withType('string')
.done()
.addField('email')
.withType('string')
.done();
} catch (err) {
console.log('Failed to build schema3:', err.message);
}
/* Expected output:
Failed to build schema3: Field "email" already defined */