databuild/plans/webapp_v1/chunk-1-client-generation.md
2025-07-09 15:05:57 -07:00

6.6 KiB

Chunk 1: TypeScript Client Generation

Parent: Build Graph Dashboard
Next: Chunk 2: Hello World App

Overview

Generate TypeScript client code from the DataBuild Build Graph Service OpenAPI specification to provide type-safe API access for the dashboard.

Scope

In Scope

  • Generate OpenAPI specification from Build Graph Service using aide
  • Create typed TypeScript client from OpenAPI spec using OpenAPI Generator
  • Set up Bazel rules for OpenAPI-to-TypeScript generation
  • Implement path parameter structs for proper aide OpenAPI generation
  • CLI-based OpenAPI spec extraction

Out of Scope

  • HTTP-based spec extraction (use CLI flag instead)
  • External Bazel modules (use OpenAPI Generator JAR directly)
  • Custom operation IDs (use auto-generated method names)
  • Client-side validation (rely on server-side validation)

Technical Approach

OpenAPI-Based Generation

Generate TypeScript client from OpenAPI specification automatically derived from Build Graph Service:

Key API endpoints to generate TypeScript client for:

  • POST /api/v1/builds - Submit build request
  • GET /api/v1/builds/:id - Get build status
  • DELETE /api/v1/builds/:id - Cancel build request
  • GET /api/v1/partitions/:ref/status - Get partition status
  • GET /api/v1/partitions/:ref/events - Get partition events
  • POST /api/v1/analyze - Analyze build graph

Generated Client Structure

// Generated types from OpenAPI spec (auto-generated models)
export * from './AnalyzeRequest';
export * from './BuildRequest';
export * from './BuildStatusResponse';
export * from './PartitionStatusResponse';
// ... all other model types

// Generated client API with auto-generated method names
export class DefaultApi {
  constructor(configuration?: Configuration);
  
  // Auto-generated method names from OpenAPI paths
  async apiV1BuildsPost(requestParameters: ApiV1BuildsPostRequest): Promise<BuildRequestResponse>;
  async apiV1BuildsIdGet(requestParameters: ApiV1BuildsIdGetRequest): Promise<BuildStatusResponse>;
  async apiV1BuildsIdDelete(requestParameters: ApiV1BuildsIdDeleteRequest): Promise<any>;
  async apiV1PartitionsRefStatusGet(requestParameters: ApiV1PartitionsRefStatusGetRequest): Promise<PartitionStatusResponse>;
  async apiV1PartitionsRefEventsGet(requestParameters: ApiV1PartitionsRefEventsGetRequest): Promise<PartitionEventsResponse>;
  async apiV1AnalyzePost(requestParameters: ApiV1AnalyzePostRequest): Promise<AnalyzeResponse>;
}

// Type-safe request parameter interfaces
export interface ApiV1BuildsPostRequest {
  buildRequest: BuildRequest;
}

export interface ApiV1BuildsIdGetRequest {
  id: string;
}

Bazel Integration

  • Create //databuild/client:typescript target
  • Extract OpenAPI spec using CLI flag --print-openapi-spec
  • Use OpenAPI Generator JAR to create TypeScript client
  • Ensure hermetic build process with JAR download
  • Output client code to databuild/client/typescript_generated/

Path Parameter Requirements

For aide to properly generate OpenAPI path parameters, all path parameters must be wrapped in structs:

#[derive(Deserialize, JsonSchema)]
pub struct BuildStatusRequest {
    pub id: String,
}

pub async fn get_build_status(
    Path(request): Path<BuildStatusRequest>
) -> Result<Json<BuildStatusResponse>, ...> {
    // Use request.id instead of direct string
}

Implementation Strategy

  1. Add OpenAPI Generation to Service

    • Add aide dependency for OpenAPI spec generation
    • Modify Axum service to use aide::ApiRouter
    • Add JsonSchema derives to request/response types
    • Add path parameter structs for proper aide integration
    • Create CLI flag --print-openapi-spec for spec generation
  2. OpenAPI Spec Extraction

    • Create Bazel rule using CLI flag instead of HTTP endpoint
    • Extract spec using build_graph_service --print-openapi-spec
    • Save spec as build artifact (JSON)
  3. TypeScript Client Generation

    • Use OpenAPI Generator JAR directly (not Bazel module)
    • Create Bazel rule to generate TypeScript client from spec
    • Use typescript-fetch generator for modern fetch-based client
    • Configure with camelCase naming and single request parameters
  4. Bazel Rules

    • Create //databuild/client:typescript_client target
    • Generate client code during build with proper file copying
    • Ensure hermetic build with JAR download

Deliverables

  • OpenAPI spec generation from Build Graph Service via CLI flag
  • TypeScript interfaces for all API request/response types (auto-generated)
  • Typed HTTP client for Build Graph Service endpoints with fetch API
  • Bazel rules for automated client generation using OpenAPI Generator JAR
  • Path parameter struct implementation for aide compatibility

Success Criteria

  • OpenAPI spec accurately reflects all service endpoints with proper path parameters
  • Generated TypeScript compiles without errors using typescript-fetch generator
  • Client provides type safety for all API endpoints with auto-generated method names
  • Bazel build integrates seamlessly and generates client automatically
  • Path parameter structs enable proper aide OpenAPI generation
  • Ready for use in Chunk 2 (Hello World App)

Testing

  • Verify OpenAPI spec generation from service using CLI flag
  • Verify generated TypeScript compiles without errors
  • Validate OpenAPI spec passes OpenAPI Generator validation
  • Ensure build process is hermetic and reproducible with JAR download
  • Verify all API endpoints generate proper TypeScript method signatures
  • Confirm path parameters are properly typed in generated client

Implementation Notes

Lessons Learned

  1. aide Path Parameter Requirements: aide requires path parameters to be wrapped in structs with JsonSchema derives, not simple Path<String> extractors.

  2. OpenAPI Generator Bazel Integration: The official Bazel module was not available in registry, so we used the JAR directly via genrule for better reliability.

  3. CLI vs HTTP Extraction: Using a CLI flag for spec extraction is simpler and more reliable than starting an HTTP server.

  4. Auto-generated Method Names: Without custom operationIds, OpenAPI Generator creates method names like apiV1BuildsPost. This can be improved in future iterations.

Build Targets

  • //databuild/client:extract_openapi_spec - Extracts OpenAPI spec JSON
  • //databuild/client:typescript_client - Generates TypeScript client
  • //databuild/client:typescript - Main target for consuming the client