Skip to content

feat(postgrest): abort requests#1368

Merged
spydon merged 10 commits into
mainfrom
feat/abort
Jul 10, 2026
Merged

feat(postgrest): abort requests#1368
spydon merged 10 commits into
mainfrom
feat/abort

Conversation

@Vinzent03

@Vinzent03 Vinzent03 commented May 29, 2026

Copy link
Copy Markdown
Collaborator

What kind of change does this PR introduce?

feature

What is the current behavior?

One cannot abort a started http request. Even by adding a .timeout to the feature only ignores the result of the future but does not cancel its execution.

What is the new behavior?

Starting with http v1.5.0` it is now possible to abort requests.

final abortCompleter = Completer<void>();
Timer(Duration(seconds: 5), () => abortCompleter.complete());
try {
  final response = await client
  .from('table')
  .select()
  .abortCompleter(abortCompleter);
} on RequestAbortedException catch (e) {
 print('Request was aborted: $e');
}

Supabase-js uses .abortSignal, but since the dart implementation uses futures/completer I switched to .abortCompleter. But I'm not entirely sure and open for discussion about choosing a different name.

related: #898
The issue covers the general feature to abort requests, so we can cover aborting the requests in functions in another pr.

@Vinzent03 Vinzent03 changed the title feat: abort requests feat(postgrest): abort requests May 29, 2026
@github-actions github-actions Bot added the postgrest This issue or pull request is related to postgrest label May 29, 2026
@Vinzent03

Copy link
Copy Markdown
Collaborator Author

For now waiting for the Dart sdk bump across all packages.

@Vinzent03 Vinzent03 marked this pull request as ready for review July 10, 2026 10:16
@Vinzent03 Vinzent03 requested a review from a team as a code owner July 10, 2026 10:17
@Vinzent03

Copy link
Copy Markdown
Collaborator Author

Now that we've already bumped the minimum versions in v2 this has not to wait for v3.

@spydon spydon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Lgtm, I'll make sure that we add an example in the docs for this.

@spydon spydon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LLM review:

Two things I'd like to sort out before merging:

  1. API shape. The builder only ever reads abortCompleter.future, it never completes it. Taking a Completer forces callers to allocate one even when they already have a future. Accepting the trigger directly is strictly more flexible and matches how http's Abortable models it:

    final response = await client.from('table').select()
        .abortSignal(Future.delayed(Duration(seconds: 5)));

    On naming: you raised this yourself, and I'd lean towards abortSignal(Future<void> signal). It mirrors supabase-js's .abortSignal, and a Future is the natural Dart analog of an AbortSignal. If we change the name, let's update the sdk-compliance.yaml symbol to match.

  2. The exception isn't reachable by consumers. RequestAbortedException comes from package:http and isn't re-exported by postgrest (or supabase), so the on RequestAbortedException catch in your example only compiles if the user adds http as a direct dependency. Could we re-export it from postgrest.dart, or wrap it in a postgrest-native exception?

A few minor things, none blocking:

  • The empty if/else branches in the new send closure are a bit awkward. Setting request.body only for post/put/patch and validating unknown methods separately would read cleaner.
  • Bumping the shared _buildClient retry delay to 500ms plus the mock's 200ms delay slows the whole retry suite noticeably. Could the delay be scoped to just the abort test?
  • The basic_test abort test catches the exception and only prints it, relying on the elapsed-time bounds. An explicit throwsA(isA<RequestAbortedException>()) would fail louder if abort silently regressed.

Deferring functions/storage to a follow-up is fine by me.

More info:

The PR's Completer is one indirection above the http primitive. It only unwraps to .future internally anyway, so wrapping it adds nothing. Two supporting points from http's own docs:

  • It explicitly blesses both patterns: Completer for event-driven abort, Future.delayed for timeouts. Accepting a Future supports both; accepting a Completer only covers the event case cleanly (a timeout caller would have to build a Completer + Timer instead of a one-liner Future.delayed).
  • "This future must not complete with an error." — a contract worth mirroring in our doc comment.

@spydon spydon self-requested a review July 10, 2026 11:34
Comment thread packages/postgrest/lib/src/postgrest_builder.dart Outdated
Comment thread packages/postgrest/lib/src/postgrest_builder.dart Outdated
Comment thread packages/postgrest/lib/src/postgrest_builder.dart Outdated
Comment thread packages/postgrest/lib/src/postgrest_builder.dart Outdated
@spydon spydon merged commit c267e4f into main Jul 10, 2026
33 checks passed
@spydon spydon deleted the feat/abort branch July 10, 2026 13:01
spydon added a commit that referenced this pull request Jul 10, 2026
…eout

Makes the automatic retry behavior configurable through retryEnabled,
retryCount and retryableStatusCodes on PostgrestClient and
PostgrestClientOptions, and per request via PostgrestBuilder.retry (which
now also accepts a count). All defaults preserve the previous behavior.

Rebased on top of the request abort feature (#1368) and now builds the
request timeout on top of it. A configured requestTimeout aborts the
in-flight request through AbortableRequest and stops any pending retries
instead of merely abandoning the future like Future.timeout would. The
retry loop rethrows RequestAbortedException so an abort always stops
retries, and requestTimeout and abortSignal are threaded through the
reconstructing builders so they survive withConverter and count chains.
spydon added a commit that referenced this pull request Jul 10, 2026
…eout

Makes the automatic retry behavior configurable through retryEnabled,
retryCount and retryableStatusCodes on PostgrestClient and
PostgrestClientOptions, and per request via PostgrestBuilder.retry (which
now also accepts a count). All defaults preserve the previous behavior.

Rebased on top of the request abort feature (#1368) and now builds the
request timeout on top of it. A configured requestTimeout aborts the
in-flight request through AbortableRequest and stops any pending retries
instead of merely abandoning the future like Future.timeout would. The
retry loop rethrows RequestAbortedException so an abort always stops
retries, and requestTimeout and abortSignal are threaded through the
reconstructing builders so they survive withConverter and count chains.
spydon added a commit that referenced this pull request Jul 12, 2026
…eout

Makes the automatic retry behavior configurable through retryEnabled,
retryCount and retryableStatusCodes on PostgrestClient and
PostgrestClientOptions, and per request via PostgrestBuilder.retry (which
now also accepts a count). All defaults preserve the previous behavior.

Rebased on top of the request abort feature (#1368) and now builds the
request timeout on top of it. A configured requestTimeout aborts the
in-flight request through AbortableRequest and stops any pending retries
instead of merely abandoning the future like Future.timeout would. The
retry loop rethrows RequestAbortedException so an abort always stops
retries, and requestTimeout and abortSignal are threaded through the
reconstructing builders so they survive withConverter and count chains.
spydon added a commit that referenced this pull request Jul 12, 2026
…eout

Makes the automatic retry behavior configurable through retryEnabled,
retryCount and retryableStatusCodes on PostgrestClient and
PostgrestClientOptions, and per request via PostgrestBuilder.retry (which
now also accepts a count). All defaults preserve the previous behavior.

Rebased on top of the request abort feature (#1368) and now builds the
request timeout on top of it. A configured requestTimeout aborts the
in-flight request through AbortableRequest and stops any pending retries
instead of merely abandoning the future like Future.timeout would. The
retry loop rethrows RequestAbortedException so an abort always stops
retries, and requestTimeout and abortSignal are threaded through the
reconstructing builders so they survive withConverter and count chains.
spydon added a commit that referenced this pull request Jul 12, 2026
…eout

Makes the automatic retry behavior configurable through retryEnabled,
retryCount and retryableStatusCodes on PostgrestClient and
PostgrestClientOptions, and per request via PostgrestBuilder.retry (which
now also accepts a count). All defaults preserve the previous behavior.

Rebased on top of the request abort feature (#1368) and now builds the
request timeout on top of it. A configured requestTimeout aborts the
in-flight request through AbortableRequest and stops any pending retries
instead of merely abandoning the future like Future.timeout would. The
retry loop rethrows RequestAbortedException so an abort always stops
retries, and requestTimeout and abortSignal are threaded through the
reconstructing builders so they survive withConverter and count chains.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

postgrest This issue or pull request is related to postgrest

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants