Skip to content

Conversation

@daniel-jonathan
Copy link

@daniel-jonathan daniel-jonathan commented Oct 28, 2025

Summary

Adds NDJSON streaming parser template to support streamedListObjects in the JavaScript SDK. Templates provide parsing utilities; streaming implementation remains in custom code maintained in js-sdk repository.

Fixes #76 (JavaScript SDK)
Fixes openfga/js-sdk#236

Changes

Templates Modified (5 files):

  • streaming.mustache (NEW): NDJSON parser for Node.js streams with error handling
  • index.mustache: Export parseNDJSONStream utility
  • config.overrides.json: Register streaming template and feature flag
  • README_calling_api.mustache: Usage documentation
  • README_api_endpoints.mustache: API endpoint table entry

Architecture:

  • Templates generate utilities (streaming.ts, exports, documentation)
  • Custom js-sdk code implements streaming (common.ts, client.ts)
  • No OpenAPI spec changes required

Testing

Generated SDK verified:

  • streaming.ts includes error handling fixes
  • parseNDJSONStream exported correctly
  • Works with custom js-sdk streaming implementation

Related

@daniel-jonathan daniel-jonathan requested a review from a team as a code owner October 28, 2025 05:47
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Walkthrough

This PR enables streaming support for the StreamedListObjects endpoint in the JavaScript SDK generator by adding a configuration flag, implementing an NDJSON parser, conditionally updating API method signatures to support streaming return types, and documenting the new streaming API functionality.

Changes

Cohort / File(s) Change Summary
Configuration
config/clients/js/config.overrides.json
Enables streaming support by adding supportsStreamedListObjects: true flag and introduces a new supporting file template entry for streaming.mustache that generates streaming.ts.
Documentation
config/clients/js/template/README_api_endpoints.mustache, config/clients/js/template/README_calling_api.mustache
Adds documentation sections for StreamedListObjects endpoint (Node.js only), gated by the supportsStreamedListObjects flag. Includes API endpoint row and usage examples with async iteration.
API Generation Templates
config/clients/js/template/api.mustache
Imports createStreamingRequestFunction from common module to enable streaming request support.
API Implementation
config/clients/js/template/apiInner.mustache
Introduces streaming-aware conditional return types and request routing: when x-fga-streaming vendor extension is true, methods return Promise<any> via createStreamingRequestFunction; otherwise return Promise<CallResult<ReturnType>> via createRequestFunction. Updates method signatures in param creator, factory, and class implementations.
Public API Exports
config/clients/js/template/index.mustache
Exports parseNDJSONStream function from the new streaming module into the public API.
New Streaming Module
config/clients/js/template/streaming.mustache
Implements parseNDJSONStream async generator function that accepts multiple input types (Readable, AsyncIterable, string, Buffer/Uint8Array), converts them to AsyncIterable, iterates chunks with UTF-8 decoding, accumulates into buffer, splits by newline, and yields parsed JSON objects with per-line error handling.

Sequence Diagram(s)

sequenceDiagram
    participant Client as OpenFgaClient
    participant Factory as {{classname}}Factory
    participant ParamCreator as {{classname}}AxiosParamCreator
    participant RequestFn as Request Function
    participant Stream as Node.js Stream

    Client->>Factory: Call streamedListObjects(params)
    
    alt x-fga-streaming flag enabled
        Factory->>ParamCreator: Call (async)
        ParamCreator->>RequestFn: Return (axios) => Promise<any>
        ParamCreator-->>Factory: via createStreamingRequestFunction
        Factory->>RequestFn: Invoke with axios instance
        RequestFn->>Stream: Return streaming response
        Stream->>Client: AsyncIterable<parsed JSON objects>
    else x-fga-streaming flag disabled
        Factory->>ParamCreator: Call (async)
        ParamCreator->>RequestFn: Return (axios) => Promise<CallResult<ReturnType>>
        ParamCreator-->>Factory: via createRequestFunction
        Factory->>RequestFn: Invoke with axios instance
        RequestFn->>Client: Promise<CallResult<ReturnType>>
    end
Loading
sequenceDiagram
    participant App as Application
    participant Parser as parseNDJSONStream
    participant Decoder as UTF-8 Decoder
    participant Buffer as Internal Buffer
    participant JSON as JSON Parser

    App->>Parser: Pass Readable stream / AsyncIterable / string
    
    alt Input is string or Buffer
        Parser->>JSON: Split by newline and parse each line
        JSON-->>App: Yield parsed JSON objects
    else Input is stream-like
        Parser->>Decoder: Iterate chunks, decode UTF-8
        Decoder->>Buffer: Accumulate into buffer
        Buffer->>JSON: Split buffer by newline, parse complete lines
        JSON->>App: Yield parsed JSON objects
        Parser->>Buffer: On stream end, flush and parse remaining
        Buffer->>JSON: Parse final buffer content
        JSON->>App: Yield final JSON objects
    end
    
    alt Error during iteration
        Parser-->>App: Reject with error, cleanup
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas requiring extra attention:

  • config/clients/js/template/apiInner.mustache — Complex conditional logic for streaming-aware return types across multiple method signatures (param creator, factory, and class methods). Verify that the templating correctly selects createStreamingRequestFunction vs. createRequestFunction based on the x-fga-streaming vendor extension, and that return type unions are properly formed in all code paths.
  • config/clients/js/template/streaming.mustache — New async generator implementation with multi-input handling, UTF-8 decoding, buffering, and line-by-line JSON parsing. Review error handling strategy (console.warn vs. rejection), buffer flushing logic at stream end, and edge cases (empty lines, incomplete JSON, non-string/Buffer inputs).
  • Cross-template consistency — Ensure that the streaming flag propagation from config.overrides.json through templates (especially in apiInner.mustache and index.mustache) is consistent and that generated code correctly exports and routes streaming methods.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "feat: add streamedListObjects for unlimited object retrieval" directly and accurately summarizes the main objective of the changeset. It clearly indicates the primary feature being added (streamedListObjects), and the phrase "unlimited object retrieval" appropriately conveys the benefit of removing the pagination ceiling. The title is concise, specific, and relates directly to the substantial changes in templates, configuration, and streaming utilities included in the PR.
Linked Issues Check ✅ Passed The PR successfully addresses the coding requirements from both linked issues. For issue #76, the changes enable StreamedListObjects in the OpenFgaApi interface by adding template infrastructure (streaming.mustache, conditional logic in apiInner.mustache) and configuration (supportsStreamedListObjects flag). For issue #236, the PR delivers SDK method generation capability through template updates and introduces parseNDJSONStream for streaming response handling, aligning with Python SDK behavior. The templates and generator-side implementation are complete and ready to generate JavaScript SDK methods, satisfying the requirement to add SDK methods capable of calling the StreamedListObjects API. While the PR objectives note that the OpenAPI spec vendor extension remains outstanding, that is a dependency external to the SDK generator and does not affect the PR's delivery of the required generator functionality.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/streaming-only

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
config/clients/js/template/apiInner.mustache (1)

285-285: Critical: Interface and implementation return type mismatch for streaming operations.

The interface method signature always declares a return type of PromiseResult<ReturnType>, but the class implementation (lines 351, 356) returns Promise<any> when x-fga-streaming is enabled. This creates a type mismatch that will cause issues when streaming operations are used through the interface.

Apply this diff to add conditional return types to the interface:

-    {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: any): PromiseResult<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>;
+    {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: any): {{#vendorExtensions}}{{#x-fga-streaming}}Promise<any>{{/x-fga-streaming}}{{^x-fga-streaming}}PromiseResult<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>{{/x-fga-streaming}}{{/vendorExtensions}}{{^vendorExtensions}}PromiseResult<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>{{/vendorExtensions}};
🧹 Nitpick comments (5)
config/clients/js/template/api.mustache (1)

13-14: Gate createStreamingRequestFunction import to avoid unused-imports when no streamed endpoints are present.

If a spec has no x-fga-streaming paths, this import may be unused and trigger TS/Lint noise. Gate it with the same flag you use for docs/templates.

Apply:

 import {
   DUMMY_BASE_URL,
   setSearchParams,
   serializeDataIfNeeded,
   toPathString,
   createRequestFunction,
-  createStreamingRequestFunction,
+{{#supportsStreamedListObjects}}
+  createStreamingRequestFunction,
+{{/supportsStreamedListObjects}}
   RequestArgs,
   CallResult,
   PromiseResult
 } from './common';

Please generate a JS client from a spec without streamed-list-objects and confirm there are no unused-import warnings in the generated api.ts.

config/clients/js/config.overrides.json (1)

12-12: Confirm versioning policy; consider a minor bump.

New public surface (streaming) warrants a semver minor bump. Ensure packageVersion here and the base config remain consistent.

Please confirm/update packageVersion and cross-check config/common/config.base.json for consistency. Based on coding guidelines.

config/clients/js/template/README_api_endpoints.mustache (1)

17-18: Consider consistent labelling with ListObjects.

ListObjects is tagged “[EXPERIMENTAL]”; consider mirroring that tag for StreamedListObjects, or remove from ListObjects if the status changed.

config/clients/js/template/streaming.mustache (1)

118-129: Remove unnecessary encode/decode for string chunks; append directly.

Avoid TextEncoder and double-decoding for string chunks.

Apply:

-  for await (const chunk of source) {
-    // Node.js streams can return Buffer or string chunks
-    // Convert to Uint8Array if needed for TextDecoder
-    const uint8Chunk = typeof chunk === "string"
-      ? new TextEncoder().encode(chunk)
-      : chunk instanceof Buffer
-        ? new Uint8Array(chunk)
-        : chunk;
-
-    // Append decoded chunk to buffer
-    buffer += decoder.decode(uint8Chunk, { stream: true });
+  for await (const chunk of source) {
+    if (typeof chunk === "string") {
+      buffer += chunk;
+    } else {
+      const uint8Chunk = chunk instanceof Buffer ? new Uint8Array(chunk) : (chunk as Uint8Array);
+      buffer += decoder.decode(uint8Chunk, { stream: true });
+    }
config/clients/js/template/apiInner.mustache (1)

213-213: Consider improving type safety for streaming return types.

Streaming operations return Promise<any>, which loses type safety compared to the typed PromiseResult or Promise<CallResult<ReturnType>> used for non-streaming operations. While streaming return types can be complex, consider defining a more specific type such as Promise<ReadableStream> or Promise<AsyncIterable<T>> to provide better type information and improve developer experience.

Also applies to: 256-256, 351-351, 356-356

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 19d1271 and 1d2c25e.

📒 Files selected for processing (7)
  • config/clients/js/config.overrides.json (2 hunks)
  • config/clients/js/template/README_api_endpoints.mustache (1 hunks)
  • config/clients/js/template/README_calling_api.mustache (1 hunks)
  • config/clients/js/template/api.mustache (1 hunks)
  • config/clients/js/template/apiInner.mustache (4 hunks)
  • config/clients/js/template/index.mustache (1 hunks)
  • config/clients/js/template/streaming.mustache (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
config/**/*.mustache

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Validate mustache syntax and variable references across all template files, including CHANGELOG.md.mustache

Files:

  • config/clients/js/template/api.mustache
  • config/clients/js/template/streaming.mustache
  • config/clients/js/template/index.mustache
  • config/clients/js/template/README_calling_api.mustache
  • config/clients/js/template/apiInner.mustache
  • config/clients/js/template/README_api_endpoints.mustache
config/**/*.{json,mustache}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Never hardcode API keys or credentials in configuration or template files

Files:

  • config/clients/js/template/api.mustache
  • config/clients/js/template/streaming.mustache
  • config/clients/js/template/index.mustache
  • config/clients/js/template/README_calling_api.mustache
  • config/clients/js/config.overrides.json
  • config/clients/js/template/apiInner.mustache
  • config/clients/js/template/README_api_endpoints.mustache
config/clients/*/config.overrides.json

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

config/clients/*/config.overrides.json: Always update the packageVersion in each language-specific config.overrides.json when making version changes
Maintain FOSSA compliance notice IDs in each language’s config.overrides.json

Files:

  • config/clients/js/config.overrides.json
config/{common/config.base.json,clients/*/config.overrides.json}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Ensure consistent versioning across base and language override configuration files to avoid version conflicts

Files:

  • config/clients/js/config.overrides.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-and-test-java-sdk
  • GitHub Check: build-and-test-go-sdk
  • GitHub Check: build-and-test-dotnet-sdk
🔇 Additional comments (5)
config/clients/js/template/index.mustache (1)

16-16: Export looks good; verify browser builds still type-check.

streaming.ts imports Node types (type-only). Ensure consumers without @types/node (browser-only) don’t fail type-checks.

Run a browser-target TS build (no @types/node) against the generated SDK and confirm type-check passes when not importing parseNDJSONStream.

config/clients/js/config.overrides.json (1)

28-31: Supporting file mapping for streaming.mustache looks correct.

Generates streaming.ts via SupportingFiles — aligned with the new export in index.mustache.

config/clients/js/template/README_calling_api.mustache (1)

469-490: Current code is correct; review comment is based on incorrect assumptions about the SDK.

The JavaScript template correctly uses the async iterable API. Both the Python example and the web-verified JavaScript SDK v1 behavior confirm that streamedListObjects returns an async iterable—not a raw stream—where each response contains an object field. The parseNDJSONStream utility does not exist in this codebase, and the current pattern (direct for await iteration) matches the actual SDK design across all languages. No changes needed.

Likely an incorrect or invalid review comment.

config/clients/js/template/apiInner.mustache (2)

11-11: LGTM: Import addition for streaming support.

The createStreamingRequestFunction import is correctly added and used conditionally throughout the template when the x-fga-streaming vendor extension is present.


213-230: Code duplication and telemetry gap for streaming confirmed—verify intentionality.

The observations are valid:

  1. Code duplication confirmed (lines 217-221 and 223-227): Identical storeId and request body telemetry attributes appear in two separate conditional blocks—one for {{#vendorExtensions}}{{^x-fga-streaming}} and another for {{^vendorExtensions}}.

  2. Telemetry gap for streaming confirmed: When x-fga-streaming is true, the {{^x-fga-streaming}} condition is false, so storeId and body telemetry are omitted. Streaming operations only include the request method in telemetry.

Before refactoring, verify:

  • Is the omission of storeId and body telemetry for streaming operations intentional or an oversight?
  • Are there operational or performance reasons streaming should exclude this telemetry?
  • Is this template manually maintained or auto-generated?

Once intentionality is confirmed, the duplication can be refactored to improve maintainability.

Adds NDJSON streaming parser template to support streamedListObjects in the
JavaScript SDK. Templates provide parsing utilities; actual streaming logic
remains in custom code (client.ts, common.ts) maintained in js-sdk repository.

Templates Added/Modified (5 files):
- streaming.mustache (NEW): NDJSON parser for Node.js streams
  - Proper error propagation (reject pending promises on error)
  - onEnd guard prevents processing after error
  - Uint8Array handling alongside string/Buffer
  - Stream destruction in return()/throw() methods
  - Widened type signature for better DX
- index.mustache: Export parseNDJSONStream utility
- config.overrides.json: Register streaming + supportsStreamedListObjects flag
- README_calling_api.mustache: Usage documentation
- README_api_endpoints.mustache: API endpoint table entry

Architecture:
- Templates generate utilities (streaming.ts, exports)
- Custom js-sdk code implements streaming (common.ts, client.ts)
- No OpenAPI spec changes required

Generated & Verified:
- streaming.ts includes all error handling fixes
- parseNDJSONStream exported correctly
- Works with custom js-sdk streaming implementation

Related:
- Fixes #76 (JavaScript SDK)
- Implements openfga/js-sdk#236
- Related PR: openfga/js-sdk#280
daniel-jonathan added a commit to openfga/js-sdk that referenced this pull request Oct 28, 2025
Adds streamedListObjects method for retrieving unlimited objects via the
streaming API endpoint. Node.js-only implementation with resilient NDJSON
parsing, proper error handling, and automatic resource cleanup.

Requires OpenFGA server v1.2.0+

Features:
- Streams beyond 1000-object limit (tested with 2000 objects)
- Memory-efficient incremental results via async generators
- Automatic stream cleanup prevents connection leaks
- Proper error propagation through async iterators
- Flexible input types (Readable|AsyncIterable|string|Buffer|Uint8Array)
- Telemetry maintained through streaming request helper

Implementation:
- streaming.ts: NDJSON parser with robust error handling
- common.ts: createStreamingRequestFunction for axios streaming
- client.ts: streamedListObjects() async generator wrapper
- Error handling: Pending promises reject on error, onEnd guarded
- Resource management: Stream destruction in return()/throw()/finally
- Type safety: Wide signature eliminates unnecessary casts

Testing (153/153 tests passing):
- 17 streaming tests (parsing, errors, cleanup, edge cases)
- 95% coverage on streaming.ts
- Live tested: 3-object and 2000-object streaming verified

Examples:
- example/streamed-list-objects: Full model with 2000 tuples
- example/streamed-list-objects-local: Minimal local setup

Related:
- Fixes #236
- Parent issue: openfga/sdk-generator#76
- Related PR: openfga/sdk-generator#654 (templates)

import type { Readable } from "node:stream";

// Helper: create async iterable from classic EventEmitter-style Readable streams
Copy link
Member

@SoulPancake SoulPancake Oct 30, 2025

Choose a reason for hiding this comment

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

I think we don't need this file anymore in the generator, since this would require reverse synching it back from the SDK, we can just do the changes in the SDK directly
Unless you're using something from the generator here

Copy link
Member

Choose a reason for hiding this comment

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

https://github.com/openfga/js-sdk/blob/0c2a0ab16995585d98582f32efec8b82090a1249/streaming.ts
If this is using some config/constants that should come from the generator, maybe then we can continue having it here

Copy link
Author

Choose a reason for hiding this comment

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

Good catch. I included it, so that all files would come from the generator. That said, looks like the need for the generator has been simplified and this can be removed.

daniel-jonathan added a commit to openfga/openfga.dev that referenced this pull request Nov 3, 2025
Add documentation for the StreamedListObjects API to the List Objects guide.

Changes:
- Add Streamed List Objects section explaining streaming differences
- Include Node.js and Python SDK examples
- Add note about SDK availability
- Add related link to StreamedListObjects API reference

Related:
- openfga/js-sdk#280
- openfga/sdk-generator#654
- openfga/sdk-generator#76
@daniel-jonathan daniel-jonathan deleted the feat/streaming-only branch November 3, 2025 19:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Support for StreamedListObjects Endpoint [All SDKs] Support streaming endpoints (StreamedListObjects)

3 participants