Skip to content

Conversation

@zjpiazza
Copy link

Change Description

Background

This PR adds TypeScript client SDK generation to the lakeFS project, bringing it in line with existing Python, Java, and Rust clients. TypeScript developers will now have a fully type-safe, modern SDK for interacting with the lakeFS API.

The implementation uses OpenAPI Generator's typescript-fetch template, which generates a lightweight client using the native Fetch API. This makes it compatible with Node.js 18+, browsers, Deno, Bun, and React Native.

Key design decisions:

  1. Generator choice: Selected typescript-fetch over other TypeScript generators for its universal compatibility (browser + Node.js) and minimal runtime dependencies.

  2. Handling duplicate exports: The lakeFS OpenAPI spec includes operations with multiple tags (e.g., [auth, external, experimental]), which causes OpenAPI Generator to create duplicate API classes and TypeScript TS2308 errors. After researching solutions (see GitHub issues #7519 and #15637), a post-generation cleanup script was implemented that removes duplicate exports from the API index file. This is the recommended community approach and keeps the solution maintainable without modifying the OpenAPI spec.

  3. Build structure: Follows the existing pattern used by other clients (python-static/python/, typescript-static/typescript/), where static configuration files are maintained separately from the generated code.

New Feature

Adds TypeScript SDK generation with the following capabilities:

  • Type-safe client: Full TypeScript type definitions inferred from OpenAPI schema
  • Modern runtime: Uses native Fetch API (no Axios/Superagent dependencies)
  • Universal compatibility: Works in Node.js 18+, browsers, Deno, Bun, React Native
  • Build integration: New make client-typescript target integrated into existing client generation workflow
  • Validation target: Added validate-client-typescript for CI/CD checks

Files added:

  • clients/typescript-static/ - Static configuration files:

    • package.json - npm package configuration
    • tsconfig.json - TypeScript compiler settings
    • typescript-codegen-config.yaml - OpenAPI Generator configuration
    • .openapi-generator-ignore - Files to preserve during generation
    • cleanup-exports.js - Script to remove duplicate exports from multi-tagged operations
  • clients/typescript/ - Generated SDK (committed to git, like other clients):

    • src/ - Generated TypeScript source code
    • .gitignore - Ignores node_modules/, dist/, and package-lock.json

Makefile changes:

  • Added client-typescript target to generate and build the SDK
  • Added validate-client-typescript target for git diff validation
  • Integrated into clients and validate-client targets

GitHub Actions workflow:

  • Added .github/workflows/typescript-api-client.yaml for automated npm publishing
  • Triggers on releases or manual workflow dispatch
  • Supports version extraction from release tags
  • Includes skip_publish_npm option for testing
  • Requires NPM_TOKEN secret to be configured in repository settings before publishing

Testing Details

Build verification:

make client-typescript

Results:

  • ✅ Generated TypeScript source code successfully
  • ✅ Zero TypeScript compilation errors
  • ✅ Compiled JavaScript and type definitions created in dist/
  • ✅ Build completes with exit code 0

Validation:

ls -la clients/typescript/dist/
# Output shows:
# - index.js, index.d.ts (entry point)
# - runtime.js, runtime.d.ts (HTTP client runtime)
# - apis/ directory (all API classes)
# - models/ directory (all TypeScript interfaces)

The cleanup script successfully removes duplicate exports:

Removed duplicate export: ExternalApi
Removed duplicate export: ExperimentalApi
✓ Cleaned up duplicate API exports

Breaking Change?

No breaking changes.

This PR only adds new functionality (TypeScript client generation). It does not modify:

  • Existing API endpoints
  • Other client SDKs (Python, Java, Rust)
  • CLI behavior
  • Server functionality

Copilot AI review requested due to automatic review settings December 26, 2025 19:54
@CLAassistant
Copy link

CLAassistant commented Dec 26, 2025

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions bot added area/testing Improvements or additions to tests infrastructure build, deploy and release processes area/ci labels Dec 26, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements TypeScript client SDK generation for the lakeFS project using OpenAPI Generator's typescript-fetch template. The generated SDK provides type-safe client code compatible with Node.js 18+, browsers, Deno, Bun, and React Native, bringing TypeScript support in line with existing Python, Java, and Rust clients.

Key changes:

  • Added TypeScript client generation configuration and build tooling
  • Generated type-safe TypeScript models from OpenAPI schema
  • Implemented cleanup script to resolve duplicate export issues from multi-tagged operations

Reviewed changes

Copilot reviewed 140 out of 305 changed files in this pull request and generated 5 comments.

File Description
clients/typescript/src/models/*.ts Auto-generated TypeScript interface definitions and JSON serialization functions for all API models
clients/typescript/src/index.ts Main entry point exporting runtime, APIs, and models
clients/typescript/.openapi-generator/VERSION OpenAPI Generator version tracking (7.0.1)
clients/typescript/.npmignore NPM package exclusion configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


'blockstoreType': json['blockstore_type'],
'blockstoreNamespaceExample': json['blockstore_namespace_example'],
'blockstoreNamespaceValidityRegex': json['blockstore_namespace_ValidityRegex'],
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent casing in JSON key name. The key 'blockstore_namespace_ValidityRegex' uses mixed camelCase and PascalCase. This should be 'blockstore_namespace_validity_regex' to match the snake_case pattern used consistently throughout other fields.

Copilot uses AI. Check for mistakes.

'blockstore_type': value.blockstoreType,
'blockstore_namespace_example': value.blockstoreNamespaceExample,
'blockstore_namespace_ValidityRegex': value.blockstoreNamespaceValidityRegex,
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent casing in JSON key name. The key 'blockstore_namespace_ValidityRegex' uses mixed camelCase and PascalCase. This should be 'blockstore_namespace_validity_regex' to match the snake_case pattern used consistently throughout other fields.

Copilot uses AI. Check for mistakes.
return {

'tokenExpirationDuration': !exists(json, 'token_expiration_duration') ? undefined : json['token_expiration_duration'],
'identityRequest': json['identityRequest'],
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent JSON key casing. The key 'identityRequest' uses camelCase while all other JSON keys in the codebase use snake_case (e.g., 'token_expiration_duration'). This should be 'identity_request' for consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +64 to +65
'featureUpdates': json['featureUpdates'],
'securityUpdates': json['securityUpdates'],
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent JSON key casing. The keys 'featureUpdates' and 'securityUpdates' use camelCase while all other JSON keys in the codebase use snake_case. These should be 'feature_updates' and 'security_updates' for consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +79 to +80
'featureUpdates': value.featureUpdates,
'securityUpdates': value.securityUpdates,
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent JSON key casing. The keys 'featureUpdates' and 'securityUpdates' use camelCase while all other JSON keys in the codebase use snake_case. These should be 'feature_updates' and 'security_updates' for consistency.

Copilot uses AI. Check for mistakes.
@arielshaqed
Copy link
Contributor

Hi @zjpiazza , and welcome to the lake!
Thanks for this PR. Given its size, I'm sure you'll appreciate it will require some effort on both sides to review. In order to speed things up, I'd like to ask some questions in advance:

  1. Parts of this pr may have been generated by using an LLM. If so, could you provide the prompt you used, and then the changes you performed manually?
  2. lakeFS has strict versioning rules for the 1.0 API. Our users rely on these when they develop code, so naturally this is very important to us. Not all OpenAPI generators can guarantee sufficient compatibility, and those that do may require particular options to generate appropriate code. Please go over this summary; is the generated code suitable, or do we need to change sine options?
  3. Is this client used in the webui?

Obviously I would like to improve support for Typescript. At the same time, I hesitate to add to our maintenance burden. If the generated client is not used in the webui, would you be open to adding this client to contrib/?

--http-user-agent "lakefs-typescript-sdk/$(PACKAGE_VERSION)" \
--git-user-id treeverse --git-repo-id lakeFS \
-o /mnt/clients/typescript
@echo "Cleaning up duplicate exports from multi-tagged operations"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why we want this. All other generated clients allow access from each tag afaiu.

@zjpiazza
Copy link
Author

Hi @arielshaqed, yes the test stubs and other components were generated via LLM. And yes absolutely I can share the prompts used. I basically just asked it follow the existing pattern for Python and Java clients. I will review the compatibility information you linked when I have a chance. Considering that the project is already using the OpenAPI client generation, I guess I didn't see it as being a big burden to maintain the typescript client generation. I apologize if that was an oversight on my part.

@arielshaqed
Copy link
Contributor

Thanks!

Additionally, a team member points out that this PR requires us to start publishing an NPM package:

Requires NPM_TOKEN secret to be configured in repository settings before publishing

I'd like us to figure out a usage and support model for this feature. We have had very mixed results with supplying prebuilt clients: on the one hand the convenience when it works is great, but on the other hand we regularly receive requests for other forms of generated clients, even in supported languages, and for bug-fixes which can only be performed upstream, and similar. Examples include - in no particular order, and I'm sure I missed many - #8817, #8832, #8547, #7830 and #6970.

I guess a use-case for this client would really help here. I am not saying we will not do this, I am saying that the required commitment is nontrivial.

Would this alternative be usable?

  1. We put this code in contrib/, where it is unsupported.

    This immediately removes the requirement for backwards compatibility and the expectation of support.

  2. You undertake to publish the client. We can call out to activate your workflow on another GitHub organization (or anywhere else, really), if that helps your publish flow.

  3. We can probably add a link to that client from our docs.

  4. We revisit the decision in 12 or 18 months.

This may give all sides what they really need in this case.

@zjpiazza
Copy link
Author

Looking at the PRs you attached, I definitely see what you mean now regarding the type of requests that would arise from publishing additional clients. In terms of use case, I am building out a self service deployment portal which leverages lakeFS. Originally I was going to do this with Terraform but I have since made the switch to Pulumi, so having a TypeScript for a better developer experience made sense.

I would not mind maintaining the client but I guess I'm a little confused on what the workflow would be from my side? If it isn't serving the wider community then I don't want to push the responsibility upstream 🤷

@arielshaqed
Copy link
Contributor

What would it take to publish a client

(Obviously I would love for you to do this! Just covering all the steps.)

Decide on a compatibility story

We like semantic versioning (you don't have to, but this how we describe our API).

The lakeFS REST API compatibility guarantees are a good start. But consider a lakeFS version bump which adds an optional parameter. This will be a minor version bump on lakeFS. The addition of that parameter will change the TypeScript API of your generated client. If that TypeScript API is no longer backwards compatible with the API of your previous version, then the generated client needs a major version bump.

You might:

  • Use an OpenAPI generator where this doesn't happen. This is what we do for our Python and Java generated clients.
  • Decide not to use semantic versioning in your published client.
  • Decide to use semantic versioning in your published client, but detach its version from those of lakeFS.
  • (Most likely other options exist!)

Decide when to update

When do you update the generated client? You could auto-generate for every lakeFS release, or you could generate at will. The first keeps everything up-to-date, but it might limit your options regarding compatibility of course.

Publish!

With all that set up, you can publish your client.

What would it take to build a client only for your app?

For a single project, I would probably recommend that you build the client as part of the app. So your Makefile1 would fetch the OpenAPI spec for a tag from GitHub and generate the client.

The good news is that you do not need to keep your client "up to date"! The same compatibility guarantees which make auto-generating the client so much work will also mean that any 1.x client will work with any 1.y server, as long as 1.x <= 1.y OR the client uses only features available in 1.y. So in practice:

  • Generate the client once and commit it with your code.
  • Update the client only if you need it to include some new lakeFS feature that your app needs.

How can the lakeFS project help?

  • If you auto-publish, we would certainly accept a post-release workflow that triggers your workflow.
  • We will accept documentation and code into our contrib/ subdirectory with many fewer requirements. For instance, we obviously have no compatibility guarantees there.
  • We really stick to our REST API guarantees! Your clients will remain valid. If we break that, it is a breaking API change - these have thankfully been rare so far, and we treat them very seriously and fix2.

Footnotes

  1. Of course many "modern" workflow tools replace make and go out of their way to make this harder than it should be. I would probably have an npm script openapi to fetch the OpenAPI and generate the client you need - and then commit that generated client along with the rest of your code.

  2. Example of such breakage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/ci area/testing Improvements or additions to tests infrastructure build, deploy and release processes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants