Hone logo
Hone
Problems

Building a Fluent API with TypeScript

Fluent APIs allow for a chainable syntax, making code more readable and expressive. This challenge asks you to implement a basic fluent API for building SQL queries in TypeScript, demonstrating how to leverage TypeScript's type system to enforce correctness and provide a smooth developer experience. The goal is to create a type-safe and chainable query builder.

Problem Description

You need to create a QueryBuilder class in TypeScript that allows users to construct SQL queries using a fluent API. The QueryBuilder should support the following operations, each returning a modified QueryBuilder instance to enable chaining:

  • select(fields: string[]): QueryBuilder: Sets the fields to select.
  • where(condition: string): QueryBuilder: Adds a WHERE clause to the query.
  • orderBy(field: string, direction: 'ASC' | 'DESC'): QueryBuilder: Adds an ORDER BY clause.
  • limit(count: number): QueryBuilder: Adds a LIMIT clause.
  • build(): string: Generates the final SQL query string.

The QueryBuilder class should maintain the state of the query being built internally. The build() method should return a valid SQL query string based on the operations performed. The API should be type-safe, meaning the TypeScript compiler should catch errors related to incorrect method calls or argument types.

Key Requirements:

  • Implement the QueryBuilder class with the specified methods.
  • Ensure that each method returns a QueryBuilder instance to allow for chaining.
  • The build() method should return a valid SQL query string.
  • The API should be type-safe, leveraging TypeScript's type system.
  • Handle potential errors gracefully (e.g., invalid direction in orderBy).

Expected Behavior:

A user should be able to chain the methods together to build a complex query. The build() method should then return the complete SQL query string.

Edge Cases to Consider:

  • Empty select array.
  • No where clause.
  • Invalid direction in orderBy (should default to 'ASC' or throw an error - your choice, but document it).
  • limit with a negative number (should default to 0 or throw an error - your choice, but document it).
  • Multiple where clauses (should be concatenated with 'AND').
  • Multiple orderBy clauses (should be concatenated with ',').

Examples

Example 1:

Input:
const query = new QueryBuilder()
  .select(['name', 'email'])
  .where('age > 25')
  .orderBy('name', 'ASC')
  .limit(10)
  .build();

Output:
SELECT name, email FROM table WHERE age > 25 ORDER BY name ASC LIMIT 10

Explanation: The query builder chains the select, where, orderBy, and limit methods to construct a SQL query.

Example 2:

Input:
const query = new QueryBuilder()
  .select(['id', 'created_at'])
  .where('status = \'active\'')
  .orderBy('created_at', 'DESC')
  .build();

Output:
SELECT id, created_at FROM table WHERE status = 'active' ORDER BY created_at DESC

Explanation: Another example demonstrating a different query construction.

Example 3: (Edge Case - Multiple WHERE clauses)

Input:
const query = new QueryBuilder()
  .select(['name'])
  .where('age > 25')
  .where('city = \'New York\'')
  .build();

Output:
SELECT name FROM table WHERE age > 25 AND city = 'New York'

Explanation: Demonstrates how multiple where clauses are handled.

Constraints

  • The build() method should return a string representing the SQL query.
  • The SQL query should be valid and well-formed.
  • The QueryBuilder class should be implemented in TypeScript.
  • The select method should accept an array of strings.
  • The orderBy method's direction parameter must be either 'ASC' or 'DESC'.
  • The limit method should accept a number.
  • Assume a default table name of "table" for simplicity. Do not allow the user to specify the table name.

Notes

  • Focus on creating a type-safe and chainable API.
  • Consider using private fields to store the query state.
  • You don't need to implement a full-fledged SQL parser or executor. The goal is to build the query string.
  • Error handling can be simple (e.g., defaulting to a safe value or throwing an error). Document your choices.
  • Think about how to make the API extensible for future features (e.g., adding support for JOIN clauses).
Loading editor...
typescript