Consuming Data

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 files to the /apis folder. Each file automatically becomes an API endpoint:

      • getFoo.ts
      • getBar.ts
  • Automatic Endpoint Mapping

    Your files are automatically mapped to /consumption/ endpoints:

    • getFoo.ts/consumption/getFoo
    • getBar.ts/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

    This command will:

    1. Create a new file getBar.ts in your /apis directory
    2. Scaffold the basic API structure with type definitions
    3. 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:

    /apis/getBar.ts
     
    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`);
      }
    );
     

    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.

    /apis/getBar.ts
    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:

    1. QueryParams interface defines the expected URL parameters and their types
    2. createConsumptionApi helper provides type safety and automatic parameter parsing
    3. Built-in client and sql utilities for safe query construction
    4. ConsumptionHelpers (CH) for secure parameter injection of SQL identifiers

    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.

    Query Parameter Interface

    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

    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.

    Warning:

    Note: This section applies only to TypeScript projects. Python support for advanced validation using Pydantic will be available soon.

    How It Works
    1. 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.

    2. Type-Safe Constraints: For instance, specifying: userId: number & typia.tags.Type<"uint32"> ensures that the userId 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
    HTTP Methods

    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

    OpenAPI Specification URL

    Make sure you are running your local development server before accessing the OpenAPI specification. Run moose dev to start your local server.

    1. 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.

    2. 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.

    3. 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.

    4. 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.