rp-postgrest
A concrete asynchronous PostgREST client owned by this workspace. Requires Rust 1.88 or newer. The package is rp-postgrest; the Rust library is rp_postgrest.
Install and configure
[]
= "3.0"
= { = "1", = ["derive"] }
Both constructors are fallible. Postgrest::new builds a transport that follows a redirect only to the same origin (scheme, host, and port), up to 10 hops, so the API key does not leak to another host. A supplied Reqwest client keeps its connection pool, default headers, timeout, proxy, TLS policy, and redirect policy. Cloning Postgrest shares its immutable configuration and transport.
use ;
use Duration;
Bases must be HTTP(S) URLs without credentials, query strings, or fragments. Client header/auth validation is eager. Request-local header/auth, schema validation, and JSON serialization errors are deferred until build or execution. build() returns Result<reqwest::RequestBuilder, Error>, so caller request customization remains available.
The default rustls feature enables HTTPS. For native TLS, disable default features and enable native-tls.
rustls takes precedence when both are enabled. With neither backend enabled, HTTP still works.
A supplied client does not add a TLS backend absent from the dependency graph.
with_retry_policy(RetryPolicy) controls bounded retries for explicit HTTP 429 responses.
The default permits two additional attempts for GET, HEAD and OPTIONS.
The policy honors Retry-After without shortening server delays.
Mutations require explicit replay opt-in; transport failures and other status codes are not replayed.
Requests sent through the raw builder returned by build() bypass this policy.
Literal resources and raw grammar
Pass literal SQL resource names to from, rpc, and rpc_json. The client encodes the resource segment once, including %, /, spaces, Unicode, backslashes, and reserved punctuation. Do not pre-encode names. from("a#b") addresses a%23b, not a URL fragment. Dot-only resources . and .. return a deferred Error::Configuration(ConfigError::DotOnlyResource) because the URL parser normalizes those segments.
Filters and selections are PostgREST grammar, not SQL or pre-encoded URLs. Scalar helpers leave the value literal after the operator. append_query appends an unencoded key/value pair without deduplication or interpretation; query_pairs exposes the ordered pairs read-only. The HTTP serializer performs URL encoding.
use Postgrest;
in_ retains its raw-fragment contract. Use in_values for untrusted literal text elements. Neither helper quotes column identifiers. Raw and/or, containment, range, and full-text helpers require caller grammar. Repeated filters stay ordered; repeated order calls compose one effective ordering value.
Execution and decoding
use ;
use Deserialize;
async
Choose the response contract explicitly:
execute()returns the rawreqwest::Response. It does not check HTTP status or consume the body.execute_checked()rejects every non-2xx status, including HTTP 300. Successful responses remain unread.fetch::<T>()checks status and decodes successful JSON intoTthrough the central decoder.single()requests server-enforced single-object cardinality; it does not select the first row locally.
HTTP 204 decodes through Serde's unit deserializer, so fetch::<()>() supports void RPC responses. It does not fabricate an empty row array. An empty HTTP 200 body is invalid JSON and returns Error::ResponseDecode. Scalar, object, and array RPC responses must match the requested type. rpc_json serializes arguments without adding relation select, single, or representation preferences.
insert, upsert, update, and rpc accept raw JSON strings. Their _json counterparts serialize typed payloads and retain serialization errors until execution. Inserts, updates, deletes, and upserts request representations by default. Upsert adds resolution=merge-duplicates; on_conflict selects conflict columns.
Preferences, counts, and minimal writes
Prefer directives compose by directive key. The last value for a key wins; unrelated directives survive. This includes inherited and request-local Prefer headers. return_minimal() changes only return, and count(Count) changes only count.
use ;
use Deserialize;
async
Count::Exact, Planned, and Estimated select server count modes. exact_count, planned_count, and estimated_count are fluent conveniences. Count preferences do not change pagination. Ranges are inclusive; limit(0) requests zero rows.
fetch_with_count returns decoded data and the server's Content-Range total. execute_count turns a GET into HEAD, retaining its filters, profile, and pagination. For mutations it retains the method and requests minimal return. RPC is not changed into a read. Missing, wildcard, or malformed totals return Error::Count, never a fabricated zero. ResponseMetadata::count() parses an observed total.
To write without decoding rows, use .return_minimal().execute_checked().await?. Raw limits and ranges describe response pagination; they are not a guarantee of safely limited UPDATE or DELETE on current servers.
One error result
All checked and typed operations return one Result<T, rp_postgrest::Error>. The non-exhaustive error distinguishes configuration, serialization, request, response-body, structured PostgREST, malformed error-envelope, successful-response decoding, and count failures.
postgrest_body() exposes the canonical decoded code, message, details, and hint. postgrest_error() exposes its structured source. HTTP status remains authoritative. PostgREST 16.2 ambiguous embedding errors such as PGRST201 can use HTTP 300 and an array of relationship details; the canonical ErrorDetails supports that array as well as ordinary text. Malformed error envelopes retain exact bytes in the Error::Decode source. Valid structured errors do not retain the original JSON bytes, and failed body reads do not promise partial bytes. status(), url(), response_metadata(), and the standard error source chain support diagnostics without nested results.
postgrest_response() returns the observed HTTP status and a borrowed typed ErrorResponse together. It does not allocate or infer status from the error code. It returns None for transport failures, malformed error envelopes, and other errors without a decoded server response.
server_response() also returns the observed non-success status when the error envelope is malformed or reading the body failed. Its optional body is None in those cases. Successful-status decoding and count errors are not server errors. is_jwt_expired() requires PGRST301 or PGRST303 and the message JWT expired, case-insensitive with an optional final period. Invalid signatures and other JWT failures return false.
With the test-util feature, Error::from_response(status, body: &[u8]) -> Option<Error> decodes fixtures through the production error-envelope decoder. Successful statuses return None without decoding. Malformed bytes are copied into the error; valid envelopes retain decoded fields only. Fixture metadata uses empty headers and http://localhost/, not an actual request URL.
Enable serde_json/arbitrary_precision in the dependency graph when decoding exact PostgreSQL numerics. This avoids an intermediate floating-point conversion; the requested Rust response type still determines its own numeric representation.
Migration from 2.1
- Import
rp_postgrest, not the oldpostgrestlibrary name. - Add
?toPostgrest::new, clientauth/insert_header, andBuilder::build. Configuration fields are private; use constructors and fluent methods. - Pass literal resources instead of percent-encoded names. Dot-only names fail explicitly.
Builderis notClone. Start a new query from a cloned/sharedPostgrestinstead of copying mutable request/error state.- Use
executeonly for deliberate raw status/body handling. Useexecute_checkedorfetchfor checked responses and handle the flatError; old execute/query error aliases are absent. - Counts no longer force
Range: 0-0. Add your desired range or limit explicitly, or useexecute_countfor body-free reads. - Preserve raw
in_grammar or switch literal inputs toin_values. Do not pre-encode query pairs. - See rp-supabase-client for removal of the product's old response wrapper and nested raw-result decoding.