Introduction to Consumption APIs
Viewing typescript
switch to python
Consumption APIs make it easy to build type-safe endpoints for surfacing data from your OLAP database. As a developer, you'll write simple, strongly-typed functions that automatically handle:
- URL parameter parsing and type conversion
- SQL query construction and execution
- Response formatting
- Automatically generated OpenAPI documentation
No need to write boilerplate for parameter validation, type conversion, or error handling - the framework handles these for you.
Getting Started
File and Folder Structure
Create your API endpoints by adding .ts
.py
files to the /apis
folder. Each file automatically becomes an API endpoint:
- getFoo.ts
- getBar.ts
- getFoo.py
- getBar.py
Your files are automatically mapped to /consumption/
endpoints:
getFoo.ts
get_foo.py
→/consumption/getFoo
getBar.ts
get_bar.py
→/consumption/getBar
Creating a New API Endpoint
The fastest way to create a new Consumption API endpoint is using the Moose CLI:
npx moose-cli consumption init getBar
moose-cli consumption init get_bar
This command will:
- Create a new file
getBar.ts
get_bar.py
in your/apis
directory - Scaffold the basic API structure with type definitions
- Add example query parameters and SQL query
Basic API Template
The generated template is a basic API endpoint that you can customize to your needs:
import { createConsumptionApi } from "@514labs/moose-lib";
// This file is where you can define your API templates for consuming your data
interface QueryParams {}
// createConsumptionApi uses compile time code generation to generate a parser for QueryParams
export default createConsumptionApi<QueryParams>(
async (params, { client, sql }) => {
return client.query(sql`SELECT 1`);
}
);
# This file is where you can define your API templates for consuming your data
# All query_params are passed in as strings, and are used within the sql tag to parameterize you queries
from moose_lib import MooseClient
@dataclass
class QueryParams:
## Define your query parameters here
def run(client: MooseClient, params: QueryParams):
return client.query("SELECT 1", { })
You can then customize this template by:
- Defining your query parameters
- Writing your SQL query
- Adding any necessary data transformations
Implementing the API Logic
Each API endpoint is defined by a single function that handles incoming requests. This function receives typed query parameters and returns database query results.
import {
createConsumptionApi,
ConsumptionHelpers as CH,
} from "@514labs/moose-lib";
interface QueryParams {
n_days?: number;
sort_by?: "total_value" | "unique_ids";
}
export default createConsumptionApi<QueryParams>(
async (
{ n_days = 10, sort_by = "total_value" }: QueryParams,
{ client, sql }
) => {
const query = sql`
SELECT
date,
sumMerge(total_value) as total_value,
uniqMerge(unique_ids) as unique_ids
FROM BarAggregated
GROUP BY date
ORDER BY
${CH.column(sort_by)} DESC
LIMIT ${n_days}
`;
return client.query(query);
}
);
Key components:
QueryParams
interface defines the expected URL parameters and their typescreateConsumptionApi
helper provides type safety and automatic parameter parsing- Built-in
client
andsql
utilities for safe query construction ConsumptionHelpers
(CH) for secure parameter injection of SQL identifiers
from dataclasses import dataclass
from moose_lib import MooseClient
@dataclass
class QueryParams:
n_days: int = 10 # Number of top days to return
sort_by: str = "total_value" # Either "total_value" or "unique_ids"
def run(client: MooseClient, params: QueryParams):
# Define the base query to get aggregated metrics
base_query = """
SELECT
date,
sumMerge(total_value) as total_value,
uniqMerge(unique_ids) as unique_ids
FROM BarAggregated
GROUP BY date
"""
# Define sort order based on params
sort_orders = {
"total_value": "ORDER BY total_value DESC, unique_ids DESC",
"unique_ids": "ORDER BY unique_ids DESC, total_value DESC"
}
# Construct final query with sort order and limit
query = f"""
{base_query}
{sort_orders[params.sort_by]}
LIMIT {params.n_days}
"""
return client.query.execute(query, {"n_days": params.n_days})
Key components:
QueryParams
dataclass defines the expected URL parameters and their types- Type hints and default values provide automatic parameter parsing
- Built-in
MooseClient
for database interactions - Safe query construction using parameterized queries
Query Parameters
Query parameters allow your APIs to accept dynamic inputs through URL parameters. These parameters are automatically parsed and type-converted before reaching your handler function.
Use an interface to define the expected parameters and their types. You can use Union types to allow multiple valid values or define more complex types.
// Define expected parameters and their types
interface QueryParams {
n_days?: number; // Optional number parameter
sort_by?: "total_value" | "unique_ids"; // String union type
}
// URL: /consumption/getBar?n_days=5&sort_by=unique_ids
// Automatically provides:
{
n_days: 5, // Converted to number
sort_by: "unique_ids" // Type-checked
}
Benefits:
- Automatic type conversion from URL strings
- Runtime type validation
- IDE autocompletion
- Type safety throughout your codebase
Python Consumption APIs use dataclasses to define and validate query parameters:
@dataclass
class QueryParams:
n_days: int = 10 # Optional with default value
sort_by: str = "total_value" # Optional with default value
# URL: /consumption/getBar?n_days=5&sort_by=unique_ids
# Automatically provides:
params = QueryParams(
n_days=5, # Converted to int
sort_by="unique_ids" # Validated as str
)
Benefits:
- Automatic type conversion from URL strings
- Runtime type validation
- Type hints for IDE support
- Default values for optional parameters
- Clean, declarative parameter definition
Advanced: Query Parameter Validation with Typia Tags
Moose leverages Typia (opens in a new tab) to enforce runtime validation on query parameters using plain TypeScript interfaces. This enables you to use all the advanced Typia tags (opens in a new tab) in your query parameter interfaces to enhance your type definitions and enforce runtime validations.
Note: This section applies only to TypeScript projects. Python support for advanced validation using Pydantic will be available soon.
How It Works
-
Auto-Generated Validators: By annotating your TypeScript interface with Typia tags (e.g.,
Type
,Format
,ExclusiveMinimum
), Moose automatically generates the code needed to validate query parameters at runtime. -
Type-Safe Constraints: For instance, specifying:
userId: number & typia.tags.Type<"uint32">
ensures that theuserId
query parameter must be a 32-bit unsigned integer.
Example
Below is a practical example demonstrating how to use Typia tags within your consumption API endpoint to enforce validations on query parameters:
import { createConsumptionApi } from "@514labs/moose-lib";
import typia from "typia";
// Define an interface strictly for query parameters using Typia tags for validation.
interface QueryParams {
// Validate that `userId` is a 32-bit unsigned integer.
userId: number & typia.tags.Type<"uint32">;
// Ensure that `email` is formatted correctly as an email address.
email: string & typia.tags.Format<"email">;
// Optional `age`: if provided, it must be greater than 17.
age?: number & typia.tags.ExclusiveMinimum<17>;
}
export default createConsumptionApi<QueryParams>(
async (params, { client, sql }) => {
// The query parameters are auto-validated against the Typia constraints.
const query = sql`
SELECT *
FROM Users
WHERE userId = ${params.userId}
AND email = ${params.email}
${params.age ? sql`AND age > ${params.age}` : sql``}
`;
return client.query(query);
}
);
Safe SQL Construction
The framework provides utilities for safely constructing SQL queries with dynamic parameters:
// Using template literals with the sql helper
const query = sql`
SELECT *
FROM MyTable
ORDER BY ${CH.column(sort_by)} DESC
LIMIT ${n_days}
`;
// ConsumptionHelpers (CH) prevent SQL injection
CH.column(sort_by) // Safely interpolates column names
The framework provides safe query construction through parameterization:
# Define valid sort orders
sort_orders = {
"total_value": "ORDER BY total_value DESC",
"unique_ids": "ORDER BY unique_ids DESC"
}
# Safe query construction using f-strings for structure
# and parameterized values for user input
query = f"""
{base_query}
{sort_orders[params.sort_by]}
LIMIT {params.n_days}
"""
# Execute with parameters
client.query.execute(query, {"n_days": params.n_days})
Consumption APIs support only GET requests to ensure optimized data retrieval.
Tips & Best Practices
- Testing Your APIs: Use tools like Postman, Thunder Client, or cURL to test your endpoints.
- Utilize Default Values: Leverage defaults in your interfaces or dataclasses to reduce manual parsing.
- Write Safe SQL: Always use the provided SQL helpers or parameterized queries to avoid injection vulnerabilities.
- Automatic API Documentation: Take advantage of the auto generated OpenAPI specification, hosted on your local dev server at http://localhost:5001/openapi.yaml (opens in a new tab), for interactive API documentation and easier client integration.
Automatically Generated OpenAPI Documentation
The framework automatically generates an up-to-date OpenAPI specification during local development. This spec provides a comprehensive overview of all consumption API endpoints, including:
- Endpoint Paths & HTTP Methods: Lists each API endpoint and the HTTP method (GET) it supports.
- Request Parameters: Details on the expected query parameters, including types, default values, and validation rules.
- Response Formats: Information about the structure of the responses and potential status codes.
How to Use the OpenAPI Spec
Make sure you are running your local development server before accessing the OpenAPI specification. Run moose dev
to start your local server.
-
Visit the Specification URL:
The OpenAPI specification is hosted on your local development server on port 5001. Simply visit http://localhost:5001/openapi.yaml (opens in a new tab) in your browser to access the YAML file that outlines your API. -
Integrate with API Tools:
You can point your API client tool of choice—such as Swagger UI, Postman, or Thunder Client—to http://localhost:5001/openapi.yaml (opens in a new tab). This will automatically generate an interactive UI where you can explore and test your API endpoints. -
Generate Client Code:
Leverage the specification to automatically generate client libraries and SDKs. Many development frameworks and tools, such as OpenAPI Generator (opens in a new tab), support importing OpenAPI specs to streamline client integration. -
Documentation is Automatically Updated:
The documentation is regenerated each time you save your API files while running your local development server, ensuring that any new endpoints or changes in parameter validation are immediately reflected.