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 ¶
- Constants
- Variables
- func NewDB(ctx context.Context, cfg Config) (dal.DB, error)
- type Config
- type Database
- func (d *Database) Close() error
- func (d *Database) DescribeCollection(ctx context.Context, ref *dal.CollectionRef) (*dbschema.CollectionDef, error)
- func (d *Database) ListCollections(ctx context.Context, parent *record.Key) ([]dal.CollectionRef, error)
- func (d *Database) ListConstraints(ctx context.Context, ref *dal.CollectionRef) ([]dbschema.ConstraintDef, error)
- func (d *Database) ListIndexes(ctx context.Context, ref *dal.CollectionRef) ([]dbschema.IndexDef, error)
- func (d *Database) ListReferrers(ctx context.Context, ref *dal.CollectionRef) ([]dbschema.Referrer, error)
- func (d *Database) Unwrap() dal.DB
- type HTTPError
- type HeaderProvider
- type MetadataError
Examples ¶
Constants ¶
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 ¶
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") // 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") // 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") // 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 ¶
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())
}
}
Output:
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 ¶
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 ¶
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 (*Database) ListReferrers ¶
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.
type HeaderProvider ¶
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 ¶
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