dalgo2postgrest

package module
v0.0.0-...-3273dc0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 9, 2026 License: MIT Imports: 22 Imported by: 1

README

dalgo2postgrest

A bounded, read-only DALgo adapter for relations exposed by PostgREST. It reads native application tables and views through PostgREST's HTTP API, including Supabase's /rest/v1 endpoint. It does not speak the PostgreSQL wire protocol, does not create a DALgo storage layout, and never writes.

Tested against PostgREST v16.5 with PostgreSQL 18. CI runs the adapter against that real pair on every push and pull request.

Security and credentials

PostgREST authenticates an HTTP request and the database authorizes it. The adapter never holds database credentials.

  • Supply a per-request Authorization (and, for Supabase, an apikey) header through Config.Headers. The provider receives the operation context, so it can refresh a short-lived token or read a request-scoped value.
  • Use a dedicated read-only database role with only the required SELECT grants and row-level-security policies. The adapter exposes no write path, but the database role remains the authority for what can be read.
  • Config.Endpoint must be HTTPS. Plain HTTP is accepted only for a loopback host when Config.AllowInsecureLoopback is true; use that for local development and tests, not hosted deployments.
  • Userinfo, a query string and a fragment are rejected in the endpoint.
  • Redirects are always refused, including when the caller supplies an http.Client whose own policy would follow them.
  • Header values containing CR/LF or NUL, invalid header names, hop-by-hop headers and headers the adapter owns (Accept, Accept-Profile, Prefer, Range, Host, Content-Length, ...) are rejected before I/O.
  • PostgREST error bodies can contain row values and SQL. They are never logged or returned. A non-2xx response becomes an *HTTPError carrying only the status; errors.Is(err, ErrUnauthorized) is true for 401 and 403.
  • Transport, header-provider, redirect and body failures become redacted sentinel errors. Context cancellation and deadline errors pass through normally.

Every operation has one deadline that covers header retrieval, every HTTP exchange, response-body reading and all pages.

Install

go get github.com/dal-go/dalgo2postgrest

The module is MIT licensed. Releases are cut by the fleet CI from a reviewed main commit; do not tag or publish a release by hand.

Public API

Compile the examples below with go test ./...; the same snippets live as Example functions in example_test.go, so a wrong import path or a shadowed package name fails validation instead of staying in prose.

package main

import (
	"context"
	"fmt"
	"net/http"
	"os"

	"github.com/dal-go/dalgo/dal"
	"github.com/dal-go/dalgo2postgrest"
)

func main() {
	ctx := context.Background()
	db, err := dalgo2postgrest.NewDB(ctx, dalgo2postgrest.Config{
		Endpoint: "https://<project>.supabase.co/rest/v1",
		// Optional: selects a PostgREST schema profile with Accept-Profile.
		Schema: "",
		// Optional: defaults to "id".
		IDColumn: "id",
		Headers: func(ctx context.Context) (http.Header, error) {
			return http.Header{
				"Authorization": {"Bearer " + os.Getenv("SUPABASE_ACCESS_TOKEN")},
				"apikey":        {os.Getenv("SUPABASE_ANON_KEY")},
			}, nil
		},
		// Optional: use MaxRows, MaxPageSize, MaxRequests,
		// MaxResponseBytes, MaxMetadataBytes, MaxOffset, MaxGetManyKeys and
		// Timeout to narrow the defaults.
		MaxRows: 100,
	})
	if err != nil {
		panic(err)
	}
	defer db.(*dalgo2postgrest.Database).Close()

	query := dal.From(dal.NewRootCollectionRef("items", "")).NewQuery().
		WhereField("qty", dal.GreaterOrEqual, 10).
		OrderBy(dal.DescendingField("qty")).
		Limit(50). // a positive limit is required
		SelectColumns(dal.Column{Expression: dal.Field("title")})

	reader, err := db.ExecuteQueryToRecordsReader(ctx, query)
	if err != nil {
		panic(err)
	}
	defer func() { _ = reader.Close() }()
	for {
		rec, err := reader.Next()
		if err == dal.ErrNoMoreRecords {
			break
		}
		if err != nil {
			panic(err)
		}
		fmt.Printf("%v %v\n", rec.Key().ID, rec.Data())
	}
}

NewDB(ctx, Config) (dal.DB, error) validates configuration and performs no network I/O; the first read authenticates. The returned value is a *dalgo2postgrest.Database, which embeds the sealed dal.DB, adds Close() error and implements dbschema.SchemaReader. Close releases idle HTTP connections and makes later operations return ErrClosed; it is idempotent, and callers that only hold a dal.DB can use db.(interface{ Close() error }).

Optional capabilities are reachable through the wrapper:

schema, ok := dal.As[dbschema.SchemaReader](db)
concrete, ok := dal.As[*dalgo2postgrest.Database](db)
Point reads
rec := record.NewRecordWithData(record.NewKeyWithID("items", int64(1)), map[string]any{})
if err := db.Get(ctx, rec); err != nil {
	if rec.IsNotFound(err) {
		// absent
	}
	panic(err)
}
found, err := db.Exists(ctx, record.NewKeyWithID("items", int64(1)))

Get keeps the caller's record key and fills its data target with the native row, including the configured ID column. GetMulti is a sequential loop under one deadline and one shared response-byte budget; a missing record is left with a not-found error and does not stop the other reads. At most MaxGetManyKeys records are accepted. An IDColumn named like a PostgREST control (limit, offset, select, ...) is quoted in the equality predicate instead of overwriting that control.

Supported query surface

ExecuteQueryToRecordsReader and ExecuteQueryToRecordsetReader accept a DALgo dal.StructuredQuery over one exposed relation.

Capability Mapping
Relation One root dal.CollectionRef. No parent, join, collection group or named database. Names must be simple SQL identifiers ([A-Za-z_][A-Za-z0-9_]*, at most 63 bytes).
Projection Plain field references with optional aliases. select=alias:field,.... A hidden id column is fetched for the key and removed from the row when the caller did not project it. A bare * is supported; a wildcard mixed with any explicit column or alias is refused in either order, as are wildcard exclusions and source-qualified wildcards.
Filters =, >, >=, <, <=, IN, NOT IN, explicit dal.IsNullCondition / dal.IsNotNullCondition (is.null / not.is.null), and nested AND/OR groups. Values are URL parameters, never SQL text. A comparison with nil is refused before I/O because DALgo defines it as UNKNOWN, not as a NULL match.
Ordering Plain ascending/descending fields. The configured ID column is appended as id.asc unless the caller already orders by it, so offset pagination has a stable tie-breaker.
Paging offset and limit. A positive limit is required; pages never exceed MaxPageSize, MaxRows, MaxRequests or the total response-byte budget. A short page is verified with Content-Range and continuation rather than assumed to be EOF.
Records Numeric keys and string keys are preserved. JSON integers that fit int64 become int64. Decimals, exponents and integers beyond int64 stay json.Number, so no digits pass through float64.
Recordsets A materialized columnar recordset. Explicit projections keep their column order and enforce the same declared-output contract as records: a declared column missing from the response is ErrInvalidResponse, while an explicit JSON null stays nil. A wildcard derives sorted column names. Array/object values and decimals remain native JSON values.
Not queried No Prefer: count=exact, no hidden count, no RPC, no resource embedding, no full-text search and no server-side aggregation.

PostgREST reserves the query parameters select, columns, on_conflict, order, limit, offset, and and or. A physical column with one of those exact names remains usable: filter keys and the equality predicate of a reserved IDColumn are double-quoted with PostgREST's official identifier quoting (for example ?"limit"=eq.10), so a constraint is never silently consumed as a control. The same quoting is used inside nested AND/OR expressions.

Unsupported operations wrap dal.ErrNotSupported and are refused before any HTTP request: joins, aggregates, GROUP BY, HAVING, subqueries, cursors, native text/SQL queries, grouping, array operators other than membership, writes and callback transactions. PostgREST cannot provide an atomic callback transaction across HTTP requests, so both transaction methods refuse rather than pretend.

The core DALgo aggregation fallback removes the caller's limit and asks the adapter for unbounded source rows. This adapter refuses every limit-zero read, so a local aggregate fails instead of returning a partial aggregate computed from a hidden cap. A CLI integration must supply an explicit positive default limit for ordinary queries.

Bounds
Config Default Hard maximum Meaning
MaxRows 1000 10000 Largest accepted query limit.
MaxPageSize 200 MaxRows Rows requested per HTTP page.
MaxRequests 20 100 Pages one query may request.
MaxResponseBytes 4 MiB 64 MiB Total response bytes of one operation, across all pages.
MaxMetadataBytes 8 MiB 64 MiB OpenAPI document byte budget.
MaxOffset 100000 10000000 Largest accepted offset.
MaxGetManyKeys 100 1000 Keys accepted by GetMulti.
Timeout 15s 2m One operation deadline, including auth, HTTP and body.

A query whose page count, row count or offset cannot fit the configured budget is refused with ErrBudgetExceeded before I/O. A response above the byte budget stops the operation with ErrResponseTooLarge; a reader never reports a successful partial aggregate.

Shape limits are fixed: one top-level relation, at most 16 ordering fields, 256 projection columns, 64 filter conditions nested at most 8 levels deep, 100 values in one IN/NOT IN list, 4096 bytes per string filter value, and 32 KiB of encoded query parameters. A query above a shape limit is refused before I/O.

Pagination integrity

PostgREST can return fewer rows than requested even when more exist, for example when its db-max-rows setting is below MaxPageSize. A short page is therefore not treated as end of data. The adapter:

  1. validates a present Content-Range against the requested offset and the returned row count, rejecting a malformed, repeated or out-of-order range;
  2. continues from the next offset while rows remain and the caller's limit has not been reached;
  3. finishes only on an empty page, a known total from Content-Range, or the caller's positive limit.

A missing Content-Range is tolerated because continuation still requires an empty page, so a missing header cannot silently truncate a result. If the configured MaxRequests is too small for the continuation pages, the read fails with ErrBudgetExceeded; it never returns a successful short result. The same budget applies when a provider returns one row per request.

Metadata

*Database implements dbschema.SchemaReader using the permitted, bounded PostgREST OpenAPI document at the API root. The same header provider and Accept-Profile profile are used, and only one root request is made per call.

  • ListCollections lists relations in definitions and one-segment paths the role can see. A valid OpenAPI document that exposes no relation to the role is an empty list; a missing, unauthorized or malformed document is an error, never an empty list.
  • DescribeCollection preserves the property order, reports portable field types (integer, number, boolean, string, date/time formats, binary), derives Nullable from OpenAPI required, and reports a primary key only when PostgREST's <pk/> marker is present. Native OpenAPI formats are retained in SourceDefinition.Columns[].DeclaredType. Arrays, objects and json/jsonb are reported as dbschema.Null because the portable type enumeration has no JSON type.
  • Local $refs are resolved inside the same document. External $refs are never fetched; the call fails with ErrMetadataUnavailable.
  • ListIndexes, ListConstraints and ListReferrers return *dbschema.NotSupportedError (wrapping dal.ErrNotSupported): PostgREST OpenAPI does not expose that metadata, and the adapter does not invent it.
  • ErrMetadataUnauthorized and *MetadataError distinguish a denied root document from an unusable one. Error messages never include the body.

Known limitations

  • Offset pagination is stable within one request but not a database snapshot. Concurrent writes can shift rows between pages. Use a stable filter on the ID or another unique key when that matters.
  • The configured ID column must be unique and exposed. Composite primary keys, non-unique IDs and relations without the configured column are not supported as record identities.
  • Relation, field and alias names must be simple ASCII SQL identifiers. Metadata can still list a relation whose name a query would refuse.
  • A column whose name is a PostgREST control (limit, offset, select, order, or, ...) is targeted with quoted filter keys. This is verified against PostgREST v16.5; a server or proxy that rewrites or rejects quoted query keys fails the read instead of silently dropping the predicate.
  • Explicit projections are exact for both records and recordsets: every declared output column must be present in the response. An explicit JSON null is a present value and stays nil; a missing column fails with ErrInvalidResponse.
  • PostgREST's own db-max-rows may cap a page below the requested MaxPageSize. The adapter continues with Content-Range-aware paging and needs enough MaxRequests to cover the resulting pages; if the budget is too small the read fails with ErrBudgetExceeded rather than returning a short success. It does not send count=exact.
  • A wildcard recordset read derives its columns from the rows it receives, so an empty result has no columns. Use an explicit projection when an empty result must still carry its column shape.
  • Timestamps are returned as the strings PostgREST writes; metadata reports their portable type as time.
  • The adapter supports PostgREST tables/views only. RPC functions, embedding, full-text search, aggregates, grouping and writes are deliberately absent.
  • Hosted Supabase acceptance is a manual gate (below). The checked-in integration suite proves the owned PostgREST v16.5 fixture, not a hosted project, its JWT verifier, RLS policy or network path.

Tests

Unit and protocol tests use httptest and need no external service:

go test ./...
go test -race -count=1 ./...
go vet ./...

The opt-in real-service suite starts a disposable PostgreSQL 18 + PostgREST v16.5 Compose project with random loopback ports and a unique Compose project name, seeds application rows through PostgreSQL's init hook, exercises rows, filters, ordering, paging, recordsets, metadata and auth failures, then removes its containers and volume. The fixture sets db-max-rows=3 (below the adapter's default MaxPageSize) and seeds distinct empty-string, NULL and non-null values in app.labels, so the real server proves both pagination continuation and NULL/empty-string separation. It also seeds app.reserved_words and app.reserved_ids, whose column names are PostgREST controls (limit, offset, select, order, or, ...), to prove the quoted filters and the reserved IDColumn point reads against the real service.

./scripts/integration.sh

The script also verifies that the served Server header is postgrest/16.5, that exactly four items and six labels rows were seeded externally, and that the app_reader role has SELECT but not INSERT. It fails if the integration tests skip, and it fails if teardown leaves a container.

Fixture files: testdata/docker-compose.yml and testdata/seed.sql. Environment variables for a direct run: POSTGREST_TEST_URL, POSTGREST_TEST_JWT_SECRET, POSTGREST_TEST_READER_ROLE, POSTGREST_TEST_NOACCESS_ROLE; optional POSTGREST_TEST_READER_TOKEN and POSTGREST_TEST_API_KEY for a hosted Supabase run.

Manual hosted Supabase acceptance gate

Local PostgREST proves the protocol and bounded adapter behaviour. A hosted Supabase project remains a separate gate because its JWT verifier, CORS, network, grants and RLS policies are deployment-specific. To run it:

  1. Use a project and schema where creating a dedicated items table is allowed. Create the table and four synthetic rows from testdata/seed.sql (drop the role creation and local app schema parts, or use a dedicated schema and set Config.Schema).

  2. Grant SELECT on the table to a scoped read-only role (or use Supabase RLS with the anon/authenticated role), and deny INSERT, UPDATE and DELETE.

  3. Obtain a short-lived access token for that identity. Do not use the service-role key and do not commit any secret.

  4. Run the hosted read journey with the Supabase anon key and token supplied as headers:

    POSTGREST_TEST_URL="https://<ref>.supabase.co/rest/v1" \
    POSTGREST_TEST_READER_TOKEN="<scoped access token>" \
    POSTGREST_TEST_API_KEY="<anon key>" \
    go test -run '^TestIntegration_ReadsSeededRelation$' -v -count=1 ./...
    
  5. Also exercise a deliberately invalid token, a page after page, metadata and Close through the real application, and confirm that error text contains neither the token nor the URL.

  6. Record the Supabase project/region, PostgREST version reported by the endpoint, role, RLS policy and date. Until that record exists, treat hosted Supabase support as unverified in this repository.

License

MIT. See LICENSE.

Documentation

Overview

Package dalgo2postgrest provides a bounded, read-only DALgo adapter for relations exposed by PostgREST, including Supabase's REST endpoint.

It reads native application tables and views through PostgREST's HTTP API. It does not speak the PostgreSQL wire protocol, does not create a DALgo storage layout, and never writes.

NewDB validates the endpoint, limits and header provider and returns a dal.DB. The optional dbschema.SchemaReader capability is implemented by the concrete *Database value and is reachable with dal.As. Call Close when the handle is no longer needed.

Index

Examples

Constants

View Source
const Version = "0.1.0"

Version is the adapter version reported by Adapter(). Releases are cut by the fleet CI after review; this constant is not a tag and is not bumped by hand.

Variables

View Source
var (
	// ErrInvalidConfig reports a configuration that cannot be used. Its message
	// never contains the endpoint or a header value.
	ErrInvalidConfig = errors.New("postgrest: invalid configuration")

	// ErrUnsafeHeader reports a header provider result containing a CR/LF, an
	// invalid field name, a hop-by-hop header or a header this adapter owns.
	ErrUnsafeHeader = errors.New("postgrest: unsafe configured header")

	// ErrRedirectRefused reports a 3xx response. Redirects are never followed.
	ErrRedirectRefused = errors.New("postgrest: redirect refused")

	// ErrRequestFailed reports a failure to obtain headers or to complete the
	// HTTP exchange. The underlying error is deliberately not wrapped: it can
	// contain a URL, credentials or a response body.
	ErrRequestFailed = errors.New("postgrest: request could not be completed")

	// ErrInvalidResponse reports a body that is not one complete JSON array of
	// row objects, or a body that was truncated.
	ErrInvalidResponse = errors.New("postgrest: invalid response")

	// ErrResponseTooLarge reports a response above the configured byte budget.
	ErrResponseTooLarge = errors.New("postgrest: response exceeds byte budget")

	// ErrBudgetExceeded reports a query whose pages, rows or offset do not fit
	// the configured budgets. It is returned before any request is sent.
	ErrBudgetExceeded = errors.New("postgrest: query exceeds configured budget")

	// ErrUnauthorized reports HTTP 401 or 403, or metadata the active role may
	// not see.
	ErrUnauthorized = errors.New("postgrest: authentication or authorization failed")

	// ErrClosed reports use of a handle after Close.
	ErrClosed = errors.New("postgrest: database is closed")

	// ErrMetadataUnavailable reports a missing, malformed or non-OpenAPI root
	// document. It is never used for a valid OpenAPI document that exposes no
	// relations to the active role.
	ErrMetadataUnavailable = errors.New("postgrest: schema metadata unavailable")

	// ErrMetadataUnauthorized reports a root document that the active role may
	// not read.
	ErrMetadataUnauthorized = errors.New("postgrest: schema metadata not authorized")

	// ErrRelationNotFound reports a relation absent from the permitted OpenAPI
	// document.
	ErrRelationNotFound = errors.New("postgrest: relation not found in schema metadata")
)

Functions

func NewDB

func NewDB(ctx context.Context, cfg Config) (dal.DB, error)

NewDB validates cfg and returns a read-only dal.DB backed by the PostgREST endpoint. It performs no network I/O: the first read authenticates. A canceled ctx is returned as its error. Call Close on the concrete *Database (or through interface{ Close() error }) when finished.

Example
package main

import (
	"context"
	"fmt"
	"net/http"
	"os"

	"github.com/dal-go/dalgo/dal"
	"github.com/dal-go/dalgo2postgrest"
)

func main() {
	ctx := context.Background()
	db, err := dalgo2postgrest.NewDB(ctx, dalgo2postgrest.Config{
		Endpoint: "https://<project>.supabase.co/rest/v1",
		// Optional: selects a PostgREST schema profile with Accept-Profile.
		Schema: "",
		// Optional: defaults to "id".
		IDColumn: "id",
		Headers: func(ctx context.Context) (http.Header, error) {
			return http.Header{
				"Authorization": {"Bearer " + os.Getenv("SUPABASE_ACCESS_TOKEN")},
				"apikey":        {os.Getenv("SUPABASE_ANON_KEY")},
			}, nil
		},
		// Optional: use MaxRows, MaxPageSize, MaxRequests,
		// MaxResponseBytes, MaxMetadataBytes, MaxOffset, MaxGetManyKeys and
		// Timeout to narrow the defaults.
		MaxRows: 100,
	})
	if err != nil {
		panic(err)
	}
	defer db.(*dalgo2postgrest.Database).Close()

	query := dal.From(dal.NewRootCollectionRef("items", "")).NewQuery().
		WhereField("qty", dal.GreaterOrEqual, 10).
		OrderBy(dal.DescendingField("qty")).
		Limit(50). // a positive limit is required
		SelectColumns(dal.Column{Expression: dal.Field("title")})

	reader, err := db.ExecuteQueryToRecordsReader(ctx, query)
	if err != nil {
		panic(err)
	}
	defer func() { _ = reader.Close() }()
	for {
		rec, err := reader.Next()
		if err == dal.ErrNoMoreRecords {
			break
		}
		if err != nil {
			panic(err)
		}
		fmt.Printf("%v %v\n", rec.Key().ID, rec.Data())
	}
}

Types

type Config

type Config struct {
	// Endpoint is the PostgREST API root, for example
	// "https://project.supabase.co/rest/v1". It must be HTTPS. Plain HTTP is
	// accepted only for a loopback host when AllowInsecureLoopback is true.
	// Credentials, a query and a fragment are rejected.
	Endpoint string

	// IDColumn is the unique column that carries a DALgo record ID. It defaults
	// to "id". The relation must expose it; queries use it as the stable
	// tie-breaker and Get/Exists filter on it. A name that is also a PostgREST
	// query control ("limit", "offset", "select", ...) is quoted in the
	// equality filter rather than colliding with that control.
	IDColumn string

	// Schema optionally selects a PostgREST schema profile with the
	// Accept-Profile request header. It does not change the URL. Leave empty to
	// use the server's default exposed schema.
	Schema string

	// Headers obtains per-request credentials and other headers. It may be nil.
	Headers HeaderProvider

	// HTTPClient optionally supplies the transport and cookie jar. The adapter
	// clones it, replaces CheckRedirect so redirects are refused, and sets
	// Timeout to the operation deadline. The caller's client is not mutated.
	HTTPClient *http.Client

	// AllowInsecureLoopback permits plain HTTP for localhost, 127.0.0.0/8 and
	// ::1 only. It is a development and test option; leave it false for hosted
	// deployments.
	AllowInsecureLoopback bool

	// MaxRows is the largest positive query limit accepted. Default 1000,
	// maximum 10000.
	MaxRows int

	// MaxPageSize is the largest number of rows requested in one HTTP page.
	// Default 200, never larger than MaxRows.
	MaxPageSize int

	// MaxRequests is the largest number of pages one query may request.
	// Default 20, maximum 100.
	MaxRequests int

	// MaxResponseBytes is the total response-byte budget of one operation.
	// Default 4 MiB, maximum 64 MiB.
	MaxResponseBytes int64

	// MaxMetadataBytes is the byte budget of one OpenAPI document. Default
	// 8 MiB, maximum 64 MiB.
	MaxMetadataBytes int64

	// MaxOffset is the largest query offset accepted. Default 100000, maximum
	// 10000000.
	MaxOffset int

	// MaxGetManyKeys is the largest number of records accepted by GetMulti.
	// Default 100, maximum 1000.
	MaxGetManyKeys int

	// Timeout is the single deadline of one operation, covering header
	// retrieval, every HTTP exchange, body reading and all pages. Default 15s,
	// maximum 2m.
	Timeout time.Duration
}

Config configures a read-only PostgREST database.

type Database

type Database struct {
	dal.DB
	// contains filtered or unexported fields
}

Database is the concrete PostgREST handle returned by NewDB. It embeds the sealed dal.DB so the framework read path is preserved, and adds Close and the optional dbschema.SchemaReader capability. Use dal.As[dbschema.SchemaReader] rather than asserting an interface on the DB value directly.

func (*Database) Close

func (d *Database) Close() error

Close releases idle HTTP connections and makes later operations fail with ErrClosed. It is idempotent.

func (*Database) DescribeCollection

func (d *Database) DescribeCollection(ctx context.Context, ref *dal.CollectionRef) (*dbschema.CollectionDef, error)

DescribeCollection maps one OpenAPI definition to a dbschema.CollectionDef. Field order follows the definition. Nullable follows OpenAPI's required list. A primary key is reported only when PostgREST's <pk/> marker is present; indexes, constraints and foreign keys are not invented.

func (*Database) ListCollections

func (d *Database) ListCollections(ctx context.Context, parent *record.Key) ([]dal.CollectionRef, error)

ListCollections returns the relations the active role can see in the PostgREST OpenAPI response. A valid document that exposes no relation to the role is an empty list. A missing, unauthorized or malformed document is an error, never an empty list.

func (*Database) ListConstraints

func (d *Database) ListConstraints(ctx context.Context, ref *dal.CollectionRef) ([]dbschema.ConstraintDef, error)

func (*Database) ListIndexes

func (d *Database) ListIndexes(ctx context.Context, ref *dal.CollectionRef) ([]dbschema.IndexDef, error)

func (*Database) ListReferrers

func (d *Database) ListReferrers(ctx context.Context, ref *dal.CollectionRef) ([]dbschema.Referrer, error)

func (*Database) Unwrap

func (d *Database) Unwrap() dal.DB

Unwrap returns the sealed DALgo DB the concrete handle embeds.

type HTTPError

type HTTPError struct {
	StatusCode int
}

HTTPError is a non-2xx PostgREST response. It carries only the status code: PostgREST error bodies may contain row values, SQL and role names.

func (*HTTPError) Error

func (e *HTTPError) Error() string

func (*HTTPError) Is

func (e *HTTPError) Is(target error) bool

Is lets callers test 401/403 with errors.Is(err, ErrUnauthorized) without inspecting the numeric status.

type HeaderProvider

type HeaderProvider func(ctx context.Context) (http.Header, error)

HeaderProvider returns headers for one request. It runs inside the operation deadline and receives the operation context, so a credential broker can refresh a short-lived Bearer token or read a request-scoped value. The adapter may call it concurrently for independent operations, so the provider must be safe for concurrent use. Returned values are never logged or included in an error.

Authorization and Cookie belong here. Do not put credentials in Endpoint.

type MetadataError

type MetadataError struct {
	Op         string
	StatusCode int
	Reason     string
	Err        error
}

MetadataError reports a schema-metadata failure. StatusCode is zero when the root was reached but its document was unusable. The error wraps either ErrMetadataUnavailable or ErrMetadataUnauthorized; it never carries a body.

func (*MetadataError) Error

func (e *MetadataError) Error() string

func (*MetadataError) Unwrap

func (e *MetadataError) Unwrap() error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL