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
QueryBuilderclass with the specified methods. - Ensure that each method returns a
QueryBuilderinstance 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
selectarray. - No
whereclause. - Invalid
directioninorderBy(should default to 'ASC' or throw an error - your choice, but document it). limitwith a negative number (should default to 0 or throw an error - your choice, but document it).- Multiple
whereclauses (should be concatenated with 'AND'). - Multiple
orderByclauses (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
QueryBuilderclass should be implemented in TypeScript. - The
selectmethod should accept an array of strings. - The
orderBymethod'sdirectionparameter must be either 'ASC' or 'DESC'. - The
limitmethod 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).