Skip to content

feat: ApiExecutor for raw requests#176

Merged
SoulPancake merged 25 commits intomainfrom
feat/raw-requests-api-executor
Feb 27, 2026
Merged

feat: ApiExecutor for raw requests#176
SoulPancake merged 25 commits intomainfrom
feat/raw-requests-api-executor

Conversation

@SoulPancake
Copy link
Member

@SoulPancake SoulPancake commented Jan 28, 2026

Generated from SDK generator PR : openfga/sdk-generator#676

Description

What problem is being solved?

How is it being solved?

What changes are made to solve it?

References

Review Checklist

  • I have clicked on "allow edits by maintainers".
  • I have added documentation for new/changed functionality in this PR or in a PR to openfga.dev [Provide a link to any relevant PRs in the references section above]
  • The correct base branch is being used, if not main
  • I have added tests to validate that the change in functionality is working as expected

Summary by CodeRabbit

  • New Features

    • Added ApiExecutor to perform raw HTTP requests against the OpenFGA API without SDK's typed methods
    • Introduced fluent builder for constructing custom API requests with path, query, headers, and body parameters
    • Added response wrapper providing access to both typed and raw JSON response data
    • Exposed method on client to retrieve the executor instance
  • Documentation

    • Added comprehensive documentation and example project demonstrating raw request usage patterns
    • Updated README with new example references
  • Tests

    • Added test coverage for ApiExecutor, request builder, and response handling

@coderabbitai
Copy link

coderabbitai bot commented Jan 28, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Introduces ApiExecutor feature enabling raw HTTP requests against OpenFGA APIs without typed SDK methods. Adds public classes ApiExecutor, ApiExecutorRequestBuilder, and ApiResponse. Includes internal ApiClient method for response wrapper support and public GetApiExecutor() on OpenFgaClient. Adds comprehensive example project and tests.

Changes

Cohort / File(s) Summary
Core ApiExecutor Implementation
src/OpenFga.Sdk/Client/ApiExecutor/ApiExecutor.cs, src/OpenFga.Sdk/Client/ApiExecutor/ApiExecutorRequestBuilder.cs, src/OpenFga.Sdk/Client/ApiExecutor/ApiResponse.cs
New public classes for executing raw API requests with fluent request building, typed and raw JSON response handling, and response metadata access.
Client Integration
src/OpenFga.Sdk/Client/Client.cs
Added lazy-loaded ApiExecutor instance and public GetApiExecutor() method; enhanced Dispose to clean up executor.
Supporting Infrastructure
src/OpenFga.Sdk/Api/OpenFgaApi.cs, src/OpenFga.Sdk/ApiClient/ApiClient.cs
Exposed internal API client via ApiClientInternal property and added SendRequestWithWrapperAsync method returning full response wrapper with raw content.
Test Coverage
src/OpenFga.Sdk.Test/Client/ApiExecutor/ApiExecutorTests.cs, src/OpenFga.Sdk.Test/Client/ApiExecutor/ApiExecutorRequestBuilderTests.cs, src/OpenFga.Sdk.Test/Client/ApiExecutor/ApiResponseTests.cs, src/OpenFga.Sdk.Test/OpenFga.Sdk.Test.csproj
Comprehensive unit tests for ApiExecutor, ApiExecutorRequestBuilder, and ApiResponse; added Testcontainers NuGet dependency.
Example Project
example/ApiExecutorExample/.gitignore, example/ApiExecutorExample/ApiExecutorExample.csproj, example/ApiExecutorExample/Makefile, example/ApiExecutorExample/Program.cs, example/ApiExecutorExample/README.md
Complete working example demonstrating store operations, authorization models, tuple handling, permission checks, raw JSON responses, and custom headers with Docker-based lifecycle management.
Documentation
CHANGELOG.md, README.md, example/README.md, src/OpenFga.Sdk/Client/IOpenFgaClient.cs
Updated changelog and README files with new feature descriptions; added example project documentation with prerequisites, quick start, and troubleshooting guides; minor formatting fix in interface declaration.
Minor Cleanup
src/OpenFga.Sdk/Model/TupleChange.cs
Simplified null checks in Equals and GetHashCode methods for Timestamp field (non-nullable DateTime).

Sequence Diagram

sequenceDiagram
    participant Client as OpenFgaClient
    participant Executor as ApiExecutor
    participant Builder as ApiExecutorRequestBuilder
    participant ApiClient as ApiClient
    participant HTTP as HttpClient
    participant Response as ApiResponse

    Client->>Client: GetApiExecutor() creates lazy instance
    Client->>Executor: new ApiExecutor(apiClient, config)
    
    Note over Client,Builder: Build Request
    Builder->>Builder: Of(method, path)
    Builder->>Builder: PathParam/QueryParam/Header/Body
    Builder->>Builder: Build() validates
    
    Note over Executor,HTTP: Send Request
    Executor->>Executor: SendAsync(request)
    Executor->>ApiClient: SendRequestWithWrapperAsync<T>()
    ApiClient->>ApiClient: BuildHeaders (merge defaults + custom)
    ApiClient->>HTTP: Send with auth, retries
    HTTP-->>ApiClient: HttpResponseMessage
    ApiClient-->>Executor: ResponseWrapper<T>
    
    Note over Executor,Response: Process Response
    Executor->>Response: FromHttpResponse(message, raw, data)
    Response-->>Executor: ApiResponse<T>
    Executor-->>Client: ApiResponse<T> with StatusCode, Headers, Data
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • evansims
  • rhamzeh
  • ewanharris
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.24% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: ApiExecutor for raw requests' clearly and concisely describes the main change: introduction of a new ApiExecutor feature for performing raw API requests.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/raw-requests-api-executor

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.

@socket-security
Copy link

socket-security bot commented Jan 28, 2026

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

Copy link
Contributor

@github-advanced-security github-advanced-security bot left a comment

Choose a reason for hiding this comment

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

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

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 introduces an ApiExecutor feature that allows developers to make custom API requests to OpenFGA endpoints using a fluent builder pattern. The implementation provides both typed and raw JSON response handling while automatically leveraging the SDK's authentication, retry logic, and error handling infrastructure.

Changes:

  • Added ApiExecutor class for executing arbitrary API requests with automatic auth/retry handling
  • Added ApiExecutorRequestBuilder for fluent request construction with path params, query params, headers, and body
  • Added ApiResponse<T> class to encapsulate response data including status, headers, raw response, and typed data
  • Exposed internal ApiClient through OpenFgaApi to enable ApiExecutor functionality
  • Added comprehensive unit tests, integration tests, and documentation

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 31 comments.

Show a summary per file
File Description
src/OpenFga.Sdk/Client/Client.cs Adds ApiExecutor property with lazy initialization and disposal handling
src/OpenFga.Sdk/Client/ApiExecutor/ApiResponse.cs Response wrapper providing status, headers, raw and typed response data
src/OpenFga.Sdk/Client/ApiExecutor/ApiExecutorRequestBuilder.cs Fluent builder for constructing API requests with validation
src/OpenFga.Sdk/Client/ApiExecutor/ApiExecutor.cs Executes requests with typed or raw responses using SDK infrastructure
src/OpenFga.Sdk/ApiClient/ApiClient.cs Adds SendRequestWithWrapperAsync to expose raw response alongside typed data
src/OpenFga.Sdk/Api/OpenFgaApi.cs Exposes internal ApiClient for ApiExecutor usage
src/OpenFga.Sdk.Test/OpenFga.Sdk.Test.csproj Adds Testcontainers dependency for integration testing
src/OpenFga.Sdk.Test/Client/ApiExecutor/*.cs Comprehensive unit and integration tests for all ApiExecutor components
README.md Documents ApiExecutor usage with examples and feature descriptions
CHANGELOG.md Records new ApiExecutor feature in unreleased section

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

@SoulPancake SoulPancake changed the title feat: Api Executor for raw requests feat: ApiExecutor for raw requests Jan 29, 2026
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

Copilot reviewed 101 out of 102 changed files in this pull request and generated 2 comments.


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

@SoulPancake SoulPancake marked this pull request as ready for review February 4, 2026 12:25
@SoulPancake SoulPancake requested review from a team as code owners February 4, 2026 12:25
@dosubot
Copy link

dosubot bot commented Feb 4, 2026

Documentation Updates

2 document(s) were updated by changes in this PR:

Custom HTTP Headers Support
View Changes
@@ -55,6 +55,86 @@
 - All API methods accept an optional `IRequestOptions` parameter for per-request headers.
 
 [Source](https://github.com/openfga/dotnet-sdk/blob/97552d60fd779db36836abeaa0e2bb9d58f87c70/CHANGELOG.md#L11-L60)
+
+#### Calling Other Endpoints with ApiExecutor
+For advanced use cases where you need to call API endpoints not yet available in the SDK's typed methods, or when you need access to full response details (status code, headers, raw response), you can use `ApiExecutor`.
+
+**Basic Usage:**
+```csharp
+using OpenFga.Sdk.ApiClient;
+
+var client = new OpenFgaClient(configuration);
+var executor = client.ApiExecutor;
+
+// Build a request using RequestBuilder
+var request = new RequestBuilder<object> {
+    Method = HttpMethod.Get,
+    BasePath = configuration.ApiUrl,
+    PathTemplate = "/stores/{store_id}",
+    PathParameters = new Dictionary<string, string> { { "store_id", storeId } },
+    QueryParameters = new Dictionary<string, string>()
+};
+
+// Execute and get full response details
+var response = await executor.ExecuteAsync<object, GetStoreResponse>(request, "GetStore");
+
+// Always check if the request was successful
+if (!response.IsSuccessful) {
+    Console.WriteLine($"Request failed: {response.StatusCode}");
+    return;
+}
+
+// Access response details
+Console.WriteLine($"Status: {response.StatusCode}");
+Console.WriteLine($"Headers: {response.Headers.Count}");
+Console.WriteLine($"Data: {response.Data.Name}");
+```
+
+**Custom Headers with ApiExecutor:**
+```csharp
+var options = new ClientRequestOptions {
+    Headers = new Dictionary<string, string> {
+        { "X-Custom-Header", "value" },
+        { "X-Trace-Id", traceId }
+    }
+};
+
+var response = await executor.ExecuteAsync<object, TResponse>(
+    request, 
+    "CustomEndpoint", 
+    options
+);
+```
+
+**Fluent API Style:**
+```csharp
+var request = RequestBuilder<object>
+    .Create(HttpMethod.Post, configuration.ApiUrl, "/stores/{store_id}/check")
+    .WithPathParameter("store_id", storeId)
+    .WithQueryParameter("consistency", "HIGHER_CONSISTENCY")
+    .WithBody(checkRequest);
+
+var response = await executor.ExecuteAsync<object, CheckResponse>(request, "Check");
+```
+
+**Key Features:**
+- Full access to response details: status code, headers, raw JSON response, and typed data
+- Integrates with SDK's authentication, retry logic, and error handling
+- Supports custom headers via ClientRequestOptions
+- Fluent API for building requests
+- Type-safe request and response handling
+
+**Response Object (ApiResponse):**
+The `ApiResponse<T>` object provides:
+- `IsSuccessful` - Boolean indicating if the request succeeded
+- `StatusCode` - HTTP status code
+- `Headers` - Response headers
+- `RawResponse` - Raw JSON response string
+- `Data` - Typed response object (when successful)
+
+For more examples, see the [ApiExecutor Example](https://github.com/openfga/dotnet-sdk/tree/main/example/ApiExecutorExample/).
+
+**Note:** This feature replaces the deprecated `SendRequestAsync` method.
 
 ---
 
StreamedListObjects Feature Overview
View Changes
@@ -5,7 +5,7 @@
 | Python   | Yes                        | Async generator   | [Example](https://github.com/openfga/python-sdk/blob/fd04bc4b8525e536208e2091dce16edf2f220250/example/streamed-list-objects/README.md) | Both async and sync versions. Yields results as they arrive.                               |
 | Go       | Yes                        | Channel           | [Example](https://github.com/openfga/go-sdk/blob/main/example/streamed_list_objects/main.go) | The `StreamedListObjects` method is public and available for use. Streams results using channels. |
 | JS       | Yes (>= v0.9.3)     | Async iterator    | [Documentation](#streamed-list-objects)                                                     | Supported as of v0.9.3. Streams results using async iterators.                      |
-| .NET     | No                         | N/A               | [Issue](https://github.com/openfga/dotnet-sdk/issues/110)                                   | Feature request open. Implementation in progress, release pending review.                  |
+| .NET     | Via ApiExecutor            | Request builder   | [Example](https://github.com/openfga/dotnet-sdk/tree/main/example/ApiExecutorExample/)      | Use `ApiExecutor` to call `/streamed-list-objects` endpoint. No dedicated typed method yet. |
 
 ## Feature Details
 
@@ -102,7 +102,44 @@
 ```
 
 ### .NET SDK
-The .NET SDK does not currently support StreamedListObjects. There is an open feature request and ongoing work to add support ([.NET issue](https://github.com/openfga/dotnet-sdk/issues/110)). The current workaround is to call the `/streamed-list-objects` API directly.
+The .NET SDK provides `ApiExecutor` for calling custom API endpoints, including `/streamed-list-objects`. While there is no dedicated typed method for StreamedListObjects yet, you can use ApiExecutor to call the endpoint directly:
+
+**Example:**
+```csharp
+using OpenFga.Sdk.ApiClient;
+
+var client = new OpenFgaClient(configuration);
+var executor = client.ApiExecutor;
+
+// Build request to call /streamed-list-objects
+var request = RequestBuilder<ListObjectsRequest>
+    .Create(HttpMethod.Post, configuration.ApiUrl, "/stores/{store_id}/streamed-list-objects")
+    .WithPathParameter("store_id", storeId)
+    .WithBody(new ListObjectsRequest {
+        Type = "document",
+        Relation = "viewer",
+        User = "user:anne"
+    });
+
+// Execute the request
+var response = await executor.ExecuteAsync<ListObjectsRequest, StreamedListObjectsResponse>(
+    request, 
+    "StreamedListObjects"
+);
+
+if (response.IsSuccessful) {
+    Console.WriteLine($"Objects: {string.Join(", ", response.Data.Objects)}");
+}
+```
+
+**Key Points:**
+- `ApiExecutor` provides a lower-level API for calling any OpenFGA endpoint
+- Integrates with SDK authentication, retry logic, and error handling
+- Supports custom headers via `ClientRequestOptions`
+- Provides full response details: status code, headers, and typed data
+- Similar to Java's `StreamingApiExecutor` and JS's `executeApiRequest` for custom endpoint access
+
+For more details on using ApiExecutor, see the [ApiExecutor Example](https://github.com/openfga/dotnet-sdk/tree/main/example/ApiExecutorExample/).
 
 ## Benefits Over ListObjects
 StreamedListObjects removes the 1000-object pagination limit, streams results as they are available, reduces memory usage, enables early termination, and is better suited for large or computed result sets ([Java example](https://github.com/openfga/java-sdk/blob/main/examples/api-executor/README.md), [Go example](https://github.com/openfga/go-sdk/blob/e6dd3e6c12ecb6654531d5febe80404db4018c26/example/streamed_list_objects/README.md)).
@@ -111,5 +148,6 @@
 - [Java SDK StreamingApiExecutor Example](https://github.com/openfga/java-sdk/blob/main/examples/api-executor/README.md)
 - [Python SDK StreamedListObjects Example](https://github.com/openfga/python-sdk/blob/fd04bc4b8525e536208e2091dce16edf2f220250/example/streamed-list-objects/README.md)
 - [Go SDK StreamedListObjects Example](https://github.com/openfga/go-sdk/blob/e6dd3e6c12ecb6654531d5febe80404db4018c26/example/streamed_list_objects/README.md)
+- [.NET SDK ApiExecutor Example](https://github.com/openfga/dotnet-sdk/tree/main/example/ApiExecutorExample/)
 - [JS SDK Feature Request](https://github.com/openfga/js-sdk/issues/236)
 - [.NET SDK Feature Request](https://github.com/openfga/dotnet-sdk/issues/110)

How did I do? Any feedback?  Join Discord

Copy link

@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: 3

🤖 Fix all issues with AI agents
In `@example/ApiExecutorExample/Makefile`:
- Line 1: Add the missing run-all target to the .PHONY declaration so the
Makefile treats the run-all target as phony; update the .PHONY line (which
currently lists help start-openfga stop-openfga run clean) to include run-all to
prevent accidental conflicts with a filesystem entry named "run-all".

In `@example/ApiExecutorExample/README.md`:
- Line 66: Update the fenced code block that contains "=== OpenFGA ApiExecutor
Example ===" to include a language specifier (e.g., add ```text or ```console)
so the block renders consistently; locate the markdown fenced block in README.md
and replace the opening ``` with ```text (or ```console) ensuring the closing
``` remains unchanged.

In `@src/OpenFga.Sdk/Api/OpenFgaApi.cs`:
- Around line 44-48: The ApiClientInternal property will be overwritten by
codegen; remove it from the generated OpenFgaApi class and instead add it to a
new partial class declaration (e.g., class OpenFgaApi in OpenFgaApi.partial.cs)
in the same namespace so the generated class and your partial class merge at
compile time; implement the internal ApiClientInternal => _apiClient property in
that partial file and ensure the new file is kept outside the generator's
overwrite scope (or add the generated file to the generator ignore list if you
intend to fully manage it manually).
🧹 Nitpick comments (6)
example/README.md (1)

7-18: Documentation additions look good.

Minor grammar nit: "bare bones" on line 8 should be hyphenated as "bare-bones" when used as a compound adjective.

📝 Suggested fix
 **Example 1:**
-A bare bones example. It creates a store, and runs a set of calls against it including creating a model, writing tuples and checking for access.
+A bare-bones example. It creates a store, and runs a set of calls against it including creating a model, writing tuples and checking for access.
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)

127-153: Header validation is bypassed for additionalHeaders.

The SendRequestWithWrapperAsync method calls BuildHeaders with null for options (line 135), then manually merges additionalHeaders (lines 138-142). This bypasses the Configuration.ValidateHeaders call that occurs in BuildHeaders for per-request headers.

Additionally, the manual merge allows additionalHeaders to override the Authorization header set by the SDK's authentication flow. While this may be intentional for advanced use cases, it could also inadvertently allow callers to corrupt authentication.

Consider:

  1. Validating additionalHeaders before merging
  2. Preventing override of security-sensitive headers like Authorization unless explicitly intended
♻️ Suggested validation
     internal async Task<ResponseWrapper<TRes>> SendRequestWithWrapperAsync<TReq, TRes>(
         RequestBuilder<TReq> requestBuilder,
         string apiName,
         IDictionary<string, string>? additionalHeaders = null,
         CancellationToken cancellationToken = default) {
         var sw = Stopwatch.StartNew();

         var authToken = await GetAuthenticationTokenAsync(apiName);
         var mergedHeaders = BuildHeaders(_configuration, authToken, null);

+        // Validate additional headers
+        Configuration.Configuration.ValidateHeaders(additionalHeaders, "additionalHeaders");
+
         // Merge additional headers (from ApiExecutor) with auth headers
         if (additionalHeaders != null) {
             foreach (var header in additionalHeaders) {
+                // Optionally protect Authorization header from being overwritten
+                // if (string.Equals(header.Key, "Authorization", StringComparison.OrdinalIgnoreCase)) continue;
                 mergedHeaders[header.Key] = header.Value;
             }
         }
src/OpenFga.Sdk/Client/Client.cs (1)

61-66: Minor: Redundant null-conditional operator.

On line 63, _apiExecutor.Value?.Dispose() uses a null-conditional operator, but Lazy<T>.Value will never be null when IsValueCreated is true. This is harmless but slightly misleading.

🔧 Optional simplification
 public void Dispose() {
     if (_apiExecutor.IsValueCreated) {
-        _apiExecutor.Value?.Dispose();
+        _apiExecutor.Value.Dispose();
     }
     api.Dispose();
 }
example/ApiExecutorExample/Makefile (1)

17-19: Fixed sleep duration may cause flaky behavior.

A fixed 3-second sleep may not be enough on slower systems or may be unnecessarily long on fast systems. Consider a retry loop with the health check instead.

🔧 Suggested improvement with retry loop
 start-openfga:
 	`@echo` "Starting OpenFGA server on localhost:8080..."
 	`@docker` run -d --name openfga-example -p 8080:8080 openfga/openfga:latest run
 	`@echo` "Waiting for OpenFGA to be ready..."
-	`@sleep` 3
-	`@curl` -s http://localhost:8080/healthz || (echo "OpenFGA failed to start" && exit 1)
+	`@for` i in 1 2 3 4 5 6 7 8 9 10; do \
+		curl -s http://localhost:8080/healthz && break || sleep 1; \
+	done || (echo "OpenFGA failed to start within 10 seconds" && exit 1)
 	`@echo` "✅ OpenFGA is ready!"
src/OpenFga.Sdk/Client/ApiExecutor/ApiExecutor.cs (1)

83-115: Non-generic SendAsync will fail if response is not valid JSON.

The use of JsonElement as an intermediate type means deserialization is attempted, which will throw if the response body is not valid JSON (e.g., plain text error messages or empty responses). If the goal is truly "raw" response handling, consider catching deserialization errors or using a different approach that doesn't require JSON parsing.

src/OpenFga.Sdk.Test/Client/ApiExecutor/ApiExecutorTests.cs (1)

298-327: Consider using mocked HttpClient for consistency.

These tests create OpenFgaClient without a mocked HttpClient. While they work correctly (the null request test throws before network access, and the singleton test doesn't make requests), using mocked clients would be more consistent with the other tests and prevent any accidental network calls if the implementation changes.

Copy link
Member

@rhamzeh rhamzeh left a comment

Choose a reason for hiding this comment

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

This SDK already includes an early version of the API executor called RequestBuilder + SendRequestAsync that handles the retry logic, auth and standardizes the interface, e.g.

var requestBuilder = new RequestBuilder<BatchCheckRequest> {
    Method = new HttpMethod("POST"),
    BasePath = _configuration.BasePath,
    PathTemplate = "/stores/{store_id}/batch-check",
    PathParameters = pathParams,
    Body = body,
    QueryParameters = queryParams,
};

return await _apiClient.SendRequestAsync<BatchCheckRequest, BatchCheckResponse>(requestBuilder,
    "BatchCheck", options, cancellationToken);

What the API Executor should be doing is either building on top of that work fully or replacing it. Currently, it is building on top of RequestBuilder, but not fully.

It is duplicating the logic by introducing ApiExecutorRequestBuilder, that is separate from RequestBuilder, duplicating some of the work.

It should just be an improved iteration/abstraction on top of RequestBuilder + SendRequestAsync.

Basically what I expect to see is:

  1. OpenFgaApi using ApiExecutor for all API interactions instead of directly calling RequestBuilder (if it offers a material improvement), otherwise enhancing RequestBuilder to include what is needed.
  2. The ApiExecutor should be part of the ApiClient, rather than Client. The difference is that ApiClient is the core building block that both OpenFgaApi and Client share and the executor belong there alongside/replacing RequestBuilder
  3. I would expect it to have a streaming variant that works for StreamedListObjects too, but I'm OK with that being a separate PR

@SoulPancake
Copy link
Member Author

@rhamzeh
Thanks a lot!
Do you think it would be worth adding the fluent builder methods for method chaining in the existing RequestBuilder
( These are all non-breaking ) I believe they are

  1. cleaner to use as a dev
  2. consistent with the other SDKs ( Go,Java )

@SoulPancake SoulPancake requested a review from rhamzeh February 19, 2026 11:04
Copy link
Member

@rhamzeh rhamzeh left a comment

Choose a reason for hiding this comment

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

One more minor thing @SoulPancake - can the OpenFgaApi use the api executor completely? That way it functions as a sort of example.

I don't think we still need SendRequestAsync with the executor available?

@SoulPancake SoulPancake requested a review from rhamzeh February 23, 2026 15:22
@SoulPancake SoulPancake added this pull request to the merge queue Feb 27, 2026
Merged via the queue into main with commit 1099b78 Feb 27, 2026
23 checks passed
@SoulPancake SoulPancake deleted the feat/raw-requests-api-executor branch February 27, 2026 15:36
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.

3 participants