Compare commits

..
510 Commits
Author SHA1 Message Date
Wolfgang Walther 6a506d18c0 ci: Actually pass GHC_VERSION to arm build script 2024-05-09 21:54:11 +02:00
Wolfgang Walther 711202c2a0 ci: Make arm scripts fail on error 2024-05-09 21:43:42 +02:00
Wolfgang Walther 9dd3f3c094 ci: Tag arm docker image properly for releases 2024-05-09 21:42:18 +02:00
Wolfgang Walther cf4aa20d7c ci: Extract changelog properly for releases 2024-05-09 21:42:17 +02:00
Wolfgang WaltherandWolfgang Walther 1afd98a739 bump version to 12.0.3 2024-05-09 18:56:47 +02:00
Taimoor ZaeemandWolfgang Walther 077e4a857e fix: fix wrong http status on pg error 42P17 infinite recursion 2024-05-09 18:56:47 +02:00
Laurence IslaandWolfgang Walther 903537d39d fix: OpenAPI now tags a FK correctly on O2O relationships 2024-05-09 18:56:47 +02:00
Taimoor ZaeemandWolfgang Walther 67f6b1c7e8 fix: remove rejected mediatype application/vnd.pgrst.object+json from response 2024-05-09 18:56:47 +02:00
steve-chavezandWolfgang Walther 977710f442 fix: clarify PGRST204 error message 2024-05-09 18:56:47 +02:00
Wolfgang WaltherandWolfgang Walther 687558937c fix: Avoid casting to table type when select= and media type handler are used
Previously using a generic mimetype handler failed when any kind of select= was given, because
we tried to cast the select-result to the original table type. With this change, this cast is
only applied when select=* is given implicitly or explicitly. This is the only case where this
makes sense, because this guarantees that correct columns are selected in the correct order for
this cast to succeed.

Resolves #3160
2024-05-09 18:56:47 +02:00
Wolfgang WaltherandWolfgang Walther 491c7a0891 fix: Return 406 instead of 415 for non-acceptable media type
415 is for Content-Type and 406 for Accept headers.
2024-05-09 18:56:47 +02:00
steve-chavezandWolfgang Walther e4984dc751 fix: wrong subquery error returning as 400 status 2024-05-09 18:56:47 +02:00
steve-chavezandWolfgang Walther 1b0f29a8cc fix: misleading "Starting.." logs on scache reload 2024-05-09 18:56:47 +02:00
Wolfgang Walther a0d3ebf590 ci: Fetch tags before checking whether tag exists 2024-05-09 14:53:16 +02:00
Wolfgang WaltherandWolfgang Walther ac217c120f ci: Automate patch releases and pre-releases
This work by automatically pushing a new tag on main and release
branches after each commit. The tag will be "devel" on main and the
version from postgrest.cabal for release branches. The release
workflow then runs as a tag pipeline, making the actual release.

For release branches, the tag will only be created if a tag for this
version doesn't exist, yet. This means to actually make a new patch
release, we still need to bump the version in postgrest.cabal. We
can automate this later as part of our backport-bot.

This is a back-port of the following commits:
- dd8d51ab
- fe0f2f70
- 58d81334
- 9fe90bf9
- c67f1c39
- 8433f981
- d9ba9a82
- a57d12b1
- b006016d
2024-05-09 14:08:42 +02:00
renovate[bot]andWolfgang Walther 961db7c7ef chore(deps): update actions/checkout action to v4.1.5 2024-05-09 13:37:11 +02:00
Wolfgang Walther c128f03b61 ci: Improve performance for nix jobs in CI
Defaulting to max-jobs = auto should improve build times by using more
cores.

Setting always-allow-substitutes to true should cause all nix
derivations to be cached on cachix, which should improve performance of
the MacOS job dramatically, when no rebuilds need to happen.
2024-05-09 13:10:05 +02:00
Wolfgang Walther b8b34f05df ci: Make artifact-from-cirrus action succeed when cirrus job doesn't start up in PR 2024-05-09 13:10:05 +02:00
Wolfgang Walther 608ee5a9ad ci: Lift restriction to nix 2.13.6 2024-05-05 19:01:52 +02:00
renovate[bot]andWolfgang Walther 9737c5c5f6 chore(deps): update all dependencies 2024-05-05 18:25:18 +02:00
Wolfgang Walther e520f32297 ci: Refactor get_cirrusci_freebsd script to GitHub action
This should make it more reliable and also easier to re-use, if we need
to.

Resolves #2555
2024-05-04 15:02:25 +02:00
renovate[bot]andWolfgang Walther 19dcf7b344 chore(deps): update all dependencies 2024-05-04 12:20:53 +02:00
renovate[bot]andWolfgang Walther a2b7b0e69d chore(deps): update all dependencies 2024-04-26 08:31:43 +02:00
renovate[bot]andWolfgang Walther e01f08392f chore(deps): update actions/download-artifact action to v4.1.7 2024-04-24 20:53:27 +02:00
renovate[bot]andWolfgang Walther e6282d88d8 chore(deps): update all dependencies 2024-04-23 08:07:48 +02:00
Wolfgang Walther 5cab03ad5d ci: Use explicit runner image versions 2024-04-22 21:25:54 +02:00
renovate[bot]andWolfgang Walther b71739ee50 chore(deps): update all dependencies 2024-04-20 12:45:26 +02:00
renovate[bot]andWolfgang Walther 0d60a79532 chore(deps): update codecov/codecov-action action to v4.2.0 2024-04-04 07:17:34 +02:00
renovate[bot]andWolfgang Walther d38e5cdb02 chore(deps): update codecov/codecov-action action to v4.1.1 2024-03-26 19:56:25 +01:00
renovate[bot]andWolfgang Walther 908cdf0b6f chore(deps): update actions/cache action to v4.0.2 2024-03-19 21:59:00 +01:00
renovate[bot]andWolfgang Walther ee55efe0c9 chore(deps): update nixbuild/nix-quick-install-action action to v27 2024-03-15 17:13:16 +01:00
renovate[bot]andWolfgang Walther e60bae8e32 chore(deps): update cachix/install-nix-action action to v26 2024-03-08 16:29:32 +01:00
renovate[bot]andWolfgang Walther ffe44a2e71 chore(deps): update ubuntu:jammy docker digest to 77906da 2024-03-07 16:28:05 +01:00
renovate[bot]andWolfgang Walther dcc6772ce1 chore(deps): update actions/download-artifact action to v4.1.4 2024-03-02 12:10:17 +01:00
Wolfgang Walther 91ffee1d2e chore: Add full semver comment to used actions 2024-03-01 08:58:33 +01:00
renovate[bot]andWolfgang Walther 1b52ce1958 chore(deps): update actions/cache digest to ab5e6d0 2024-03-01 08:12:03 +01:00
Wolfgang Walther 8186e78691 nix: Remove broken docker-based nix development environment
It used to be possible to spin up a nix environment via docker container this way,
but the upstream nixos/nix image has changed and the docker build doesn't succeed
anymore. Since nobody complained about that, we can assume it is not being used
anyway.
2024-02-29 08:22:52 +01:00
renovate[bot]andWolfgang Walther b9186b6d9a chore(deps): update all dependencies 2024-02-27 09:10:29 +01:00
Wolfgang Walther b663a9aa37 ci: Use actions/download-artifact instead of dawidd6/action-download-artifact 2024-02-24 21:58:19 +01:00
renovate[bot]andWolfgang Walther 4d874fb725 chore(deps): pin dependencies 2024-02-24 21:55:30 +01:00
renovate[bot]andWolfgang Walther 68e145550e chore(deps): update all dependencies 2024-02-24 20:38:54 +01:00
Wolfgang Walther 3406e62a77 ci: Remove nix actions cache and prepopulate job
The nix actions cache currently leads to repeated "no space left on
devices" errors for jobs in CI.

The prepopulate job is useless without the nix actions cache, so it
will go away at the same time.
2024-02-24 19:52:25 +01:00
Wolfgang Walther 3e5e8a22e4 ci: Switch action-get-latest-tag to github-action-get-previous-tag
The former is not maintained anymore and produces deprecation warnings in CI.
2024-02-24 19:04:23 +01:00
Wolfgang Walther 414a0daca1 chore(deps): update codecov/codecov-action action to v4 2024-02-24 18:43:11 +01:00
Wolfgang Walther 30f18cf22c docs: Display version number more prominently
Resolves https://github.com/PostgREST/postgrest-docs/issues/734
2024-02-19 21:50:54 +01:00
Wolfgang Walther fd9192836e chore: Add .docs-build to .gitignore to allow switching branches easily
This is only used on the latest main branch, but switching to release branches
is annoying otherwise.
2024-02-19 21:49:48 +01:00
Wolfgang Walther 02d8990ce2 ci: Change names for release branches from rel-MAJOR.MINOR to vMAJOR
This naming scheme gives us the best support for readthedocs.

References #2814
2024-02-17 17:46:11 +01:00
Wolfgang Walther 06821fdbca ci: Refactor conditions to check for branch events 2024-02-17 17:45:44 +01:00
Wolfgang Walther b77a08ff10 ci: Add freebsd executable to releases
This was temporarily disabled, because of timeouts in Cirrus. This seems to work well again.
2024-02-17 15:24:05 +01:00
Wolfgang Walther 278be7074e ci: Fix FreeBSD build on cirrus 2024-02-17 15:24:05 +01:00
Wolfgang Walther b9d46812f8 Merge branch 'docs/v12.0' into rel-12.0 2024-02-17 13:46:34 +01:00
Wolfgang Walther 7f410f3be0 chore: Run postgrest-style on docs files
This prevents CI from failing after the merge.
2024-02-17 13:43:11 +01:00
Wolfgang Walther 8aa8e682a2 chore: Prepare merge of postgrest-docs into postgrest main repo
This avoids some merge conflicts to allow git blame to detect renames properly.
2024-02-17 13:43:11 +01:00
Laurence Isla 867dc58ecc Update sponsor logo 2024-01-15 20:33:30 -05:00
Laurence Isla 6962e41a37 Fix Sphinx version error in RTD (#739) 2024-01-15 20:33:30 -05:00
ParashoeandGitHub 8605edcb6f Spelling correction (#740)
Spell correct "callounter" to "callcounter"
2024-01-15 20:18:20 -05:00
Gabriel MirandaandSteve Chavez efcf36d970 Fix secret command to avoid non-ascii alphanumerics 2023-12-30 23:10:37 -05:00
Laurence Isla f95c5127df Update sponsor image (#728) 2023-12-21 15:22:04 -05:00
Laurence Isla a4e00ffdf6 bump version to 12.0.2 2023-12-20 18:21:19 -05:00
steve-chavezandLaurence Isla cd62e39cd1 fix: empty row on handler function
Closes https://github.com/PostgREST/postgrest/issues/3126
2023-12-20 16:33:34 -05:00
steve-chavezandLaurence Isla ec0f99c686 fix: aggregates not working for all schemas
Closes https://github.com/PostgREST/postgrest/issues/3124
2023-12-20 16:33:31 -05:00
Laurence Isla b2cd365866 chore: add new sponsor 2023-12-20 16:29:37 -05:00
dependabot[bot]andLaurence Isla fcc330311f build(deps): bump actions/upload-artifact from 3 to 4 (#3123) 2023-12-20 16:29:37 -05:00
dependabot[bot]andLaurence Isla e557161b84 build(deps): bump dawidd6/action-download-artifact from 2 to 3 (#3122) 2023-12-20 16:29:37 -05:00
dependabot[bot]andLaurence Isla 962268fd6b build(deps): bump actions/download-artifact from 3 to 4 (#3121) 2023-12-20 16:29:37 -05:00
Laurence Isla cb501a9041 Add warning and fix to htmx how-to 2023-12-19 13:32:51 -05:00
steve-chavez a7d2c8e6ad docs: soap how-to is outdated
`*/*` can now be handled with media type handlers
2023-12-19 00:00:56 -05:00
Laurence Islaandsteve-chavez 40e16bd468 Add new sponsor 2023-12-18 18:23:39 -05:00
Laurence Isla 49e257617e Priorize query parameters instead of headers in limits and pagination 2023-12-13 11:48:53 -05:00
Laurence Isla 474bb93e89 Add meta tags using an Open Graph extension 2023-12-13 11:48:41 -05:00
Laurence Isla d75538ec4a Add missing curl requests in Preferences section 2023-12-13 11:48:32 -05:00
Laurence Islaandsteve-chavez fc58262cb2 Add new sponsor (#723) 2023-12-12 23:05:43 -05:00
steve-chavez fdfc095b0e fix: correct any media type handler 2023-12-12 22:46:56 -05:00
Laurence Islaandsteve-chavez eb582fb4b0 Add missing in-db configuration for jwt-cache-max-lifetime 2023-12-12 22:46:46 -05:00
Laurence Isla cd38da56d5 bump version to 12.0.1 2023-12-12 19:20:47 -05:00
steve-chavezandLaurence Isla 229a4e4cd6 fix: any handler sets a default application/json
Now it sets application/octet-stream as the generic type.
2023-12-12 18:46:40 -05:00
Laurence Isla c3f7440e33 fix: add jwt_cache_max_lifetime as an in-database configuration option (#3102) 2023-12-12 18:46:40 -05:00
Laurence Isla 4e03ee252b chore: add new sponsor 2023-12-12 18:46:40 -05:00
Andrei DziahelandLaurence Isla 0ac4d0d0a9 fix: replace json parser error with generic msg (#3090) 2023-12-12 18:46:40 -05:00
Andrei DziahelandLaurence Isla ceda77c5de ci: cancel in-progress loadtests for PRs as well 2023-12-12 18:46:40 -05:00
Andrei DziahelandLaurence Isla a6e4a0078b ci: test only head of branch 2023-12-12 18:46:40 -05:00
Laurence Isla 15f4157cc5 changelog: move missplaced unreleased fix (#3094) 2023-12-12 18:46:40 -05:00
Laurence Isla 50ea1999b2 fix: allow using special characters in json keys (#3081)
* increase memory size test
2023-12-12 18:46:40 -05:00
Andrei DziahelandLaurence Isla c92f16a2cb ci: similar jobs use a single cache for Nix stores
Jobs that use the setup-nix action will share a single cache, which is created by a previous warm up Job.
2023-12-12 18:46:40 -05:00
dependabot[bot]andLaurence Isla 1cebc03313 build(deps): bump cachix/cachix-action in /.github/actions/setup-nix (#3085) 2023-12-12 18:46:40 -05:00
steve-chavez 9f252678a4 fix: add missing pgrst.server_timing_enabled 2023-12-12 10:57:47 -05:00
steve-chavez 802131aa96 fix: any media type should be bytea 2023-12-05 17:49:46 -05:00
steve-chavez 275bdee47c fix server-timing header section
Also link it to jwt caching
2023-12-02 01:36:26 -05:00
steve-chavez f70e852a0a bump to 12.0.0 2023-12-01 19:41:04 -05:00
steve-chavez 00580bc8cb bump version to 12.0.0 2023-12-01 19:18:07 -05:00
steve-chavez 70c0d88fa7 bring back schema isolation on its own page 2023-12-01 19:03:23 -05:00
steve-chavez 2b4e0cc10a move return representation to preferences page 2023-12-01 18:32:38 -05:00
steve-chavez 1268b0a258 split pagination/count into own page 2023-12-01 16:24:52 -05:00
steve-chavez 11f40c384a use the clearer START TRANSACTION 2023-12-01 16:24:52 -05:00
Tim AbdullaandGitHub 2bb9cee996 Add documentation for aggregate functions (#701) 2023-12-01 11:38:42 -05:00
steve-chavez a38eee399e chore: improve mt handlers snippets 2023-12-01 10:59:48 -05:00
steve-chavez ee4321623f chore: improve intro of media type handlers 2023-12-01 10:51:46 -05:00
steve-chavez 49903ca6db chore: wording of media type handlers 2023-12-01 10:08:12 -05:00
steve-chavez d15e357d2e reference: media type handlers 2023-11-30 23:27:06 -05:00
Laurence IslaandGitHub 62512268cd Add PostgREST installation using package managers to tut0 2023-11-30 18:20:54 -05:00
Laurence IslaandGitHub 84152b483a Remove db-use-legacy-gucs 2023-11-29 15:37:25 -05:00
steve-chavez 9fe11249f9 feat: custom SQL handler for the "*/*" media type 2023-11-28 23:14:38 -05:00
Laurence IslaandGitHub 7640de34e2 refactor: use a data type instead of Map for Server Timing 2023-11-28 18:26:56 -05:00
Andrei DziahelandSteve Chavez 31ce39ba36 ci: cabal+GHC: tweak caching
Makes it to cache only relevant directories, adds `dist-newstyle` to prevent needless rebuilding even harder and shortens cache key name by supplying `hashFiles`multiple arguments
2023-11-28 12:48:57 -05:00
Laurence IslaandGitHub ca5eb64deb chore: fix server-timing metric order and doctest 2023-11-28 12:45:39 -05:00
Andrei DziahelandSteve Chavez a72241ad4e ci: fixes job caches not producing after recent cabal upgrade
Recent cabal adopted XDG guidelines and stores data
across multiple directories under $HOME.
Creating ~/.cabal manually returns old behavior and allows caching single directory again.
2023-11-28 11:52:52 -05:00
steve-chavez b080f59bac test: server timing on root and options method 2023-11-28 10:25:25 -05:00
steve-chavez 558e9d40e8 changelog: join server-timing entries 2023-11-28 10:25:25 -05:00
steve-chavez 958339b8d3 chore: change server timing render to response
"render" is a loaded term than might be thought as the generation
of a full HTML page. While for this phase we only process the status
and the headers.

Changing it to "response" so is not misleading at least. Users can check the
docs for clarification.
2023-11-28 10:25:25 -05:00
steve-chavez 3d56f8435d chore: delete dead error codes 2023-11-28 10:25:25 -05:00
steve-chavez e620ae5e9c pin sphinx-tabs ver 2023-11-28 07:46:13 -05:00
steve-chavez bd6d8624ab fix: sphinx breaking change
see https://github.com/sphinx-doc/sphinx/issues/10474
2023-11-28 07:46:13 -05:00
steve-chavez 03e384d55b remove release notes 2023-11-28 07:46:13 -05:00
Laurence IslaandSteve Chavez dfa875c8c7 fix: do not log internal db errors like 'acquisition timeout' when log-level=crit 2023-11-27 23:06:40 -05:00
Laurence IslaandSteve Chavez 33891e3a73 feat: log all internal database errors to stderr 2023-11-27 23:06:40 -05:00
Laurence IslaandSteve Chavez 8483459d59 chore: add missing --example for server-timing-enabled 2023-11-24 17:41:16 -05:00
Andrei DziahelandGitHub 7b5c2f03fc introducing server-timing-header config parameter (#700) 2023-11-24 16:50:28 -05:00
Laurence IslaandSteve Chavez b538ab9823 feat: add timing for the api request parse 2023-11-24 16:29:25 -05:00
Laurence IslaandSteve Chavez 850b15fe13 fix: change timing name from 'query' to 'transaction' 2023-11-24 16:29:25 -05:00
Andrei DziahelandSteve Chavez 9eda78eda5 server-port can be 0
Introduces special value of 0 for port to be assigned randomy
2023-11-23 22:35:28 -05:00
Andrei DziahelandGitHub df97a5071f Display an actual TCP port app is bound to (#3034) 2023-11-23 18:17:16 -05:00
Tim AbdullaandGitHub 1c60b50e2e Add aggregate functions (#2925)
The aggregate functions SUM(), MAX(), MIN(), AVG(), and COUNT() are now supported.
2023-11-23 14:03:03 -05:00
Andrei DziahelandGitHub c3301a1653 feat: implement server-timing-enabled config parameter (#3064) 2023-11-22 17:57:50 -05:00
Andrei DziahelandGitHub abcb21c69a ci: cache Nix store with cache-nix-action (#2992)
* ci: try nix-community/cache-nix-action

* ci: add cache-id param to setup-nix action

* ci: tidy up cache keys for non-nix jobs

* ci: merge-nix-caches-linux job

* ci: merge caches other way around

* ci: reduce number of caches

Should prevent disk overflow

* ci: change cache id prefix for merge

* ci: comment out cache merging job

* ci: revert cache id prefix

* ci: setup-nix: use latest cache-nix-action

Among others, makes action logs look more tidy (see https://github.com/nix-community/cache-nix-action/commit/17d19d3d8be918757589635bc1a6830be0b129d2)

* ci: use test-pg cache key for loadtest
2023-11-21 18:56:23 -05:00
steve-chavez 475b4601ca split role settings from function settings
Also add doc for GRANT SET ON PARAMETER, see
https://github.com/PostgREST/postgrest/pull/3058.
2023-11-21 17:49:35 -05:00
Taimoor ZaeemandSteve Chavez 49e3d2c4b4 add documentation for statement_timeout set on functions 2023-11-21 17:49:35 -05:00
steve-chavez f7bf2157f3 feat: apply super settings on impersonated roles
If they have GRANT SET ON PARAMETER <setting> TO authenticator
2023-11-21 10:54:56 -05:00
Taimoor ZaeemandGitHub 125f10a60f feat: add statement_timeout set on functions (#3056) 2023-11-17 12:36:51 -05:00
Taimoor ZaeemandGitHub 5502dce556 add documentation for timezone preference (#698) 2023-11-15 08:24:25 -05:00
Anwar KnyaneandGitHub 084e8c2950 make the jwt generation easier :) (#697) 2023-11-14 11:31:33 -05:00
Taimoor ZaeemandGitHub 3c1a7f2641 feat: add timezone in Prefer header (#3024)
* increase reloading timeout in io-tests
* increase memory test by 1M
2023-11-13 14:16:27 -05:00
omahsandGitHub f10d4139fe nix: fix typos in README 2023-11-08 11:43:42 -05:00
Laurence IslaandGitHub 7e7e494b31 Remove former sponsors (#694) 2023-11-07 17:24:46 -05:00
Laurence IslaandSteve Chavez 99b705d44f chore: remove former sponsors 2023-11-07 16:25:47 -05:00
Laurence IslaandGitHub e0586c3afa add Neon as new sponsor (#693) 2023-11-06 22:49:53 -05:00
Laurence IslaandGitHub 379d9d6298 chore: add Neon as sponsor 2023-11-06 20:02:58 -05:00
Laurence IslaandSteve Chavez 2c35257eec chore: add prefix info to the PR template 2023-11-03 19:42:06 -05:00
steve-chavez ed90774f53 test: query the empty string 2023-11-03 12:58:54 -05:00
Laurence Isla 96ca177c31 transaction-scoped settings are now shown clearly in the postgres logs 2023-11-02 15:50:22 -05:00
Laurence Isla 226400a5bc break:remove the db-use-legacy-gucs config
BREAKING CHANGE

All PostgreSQL versions will use JSON GUCs for headers, cookies and JWT claims.
2023-11-02 15:50:22 -05:00
Taimoor ZaeemandGitHub 57ceabb260 add documentation for server-cors-allowed-origins config (#688) 2023-10-30 11:08:12 -05:00
b85ee953b7 doc: media type handlers (#689)
* reference: media type handlers

* Use function instead of computed field in editable task

---------

Co-authored-by: Laurence Isla <lau.isla.c@gmail.com>
2023-10-28 12:00:37 -05:00
Laurence IslaandGitHub b235227119 allow all origins when server-cors-allowed-origins is an empty string 2023-10-27 21:30:56 -05:00
Taimoor ZaeemandGitHub 5c9c7f4ff4 fix: HTTP status responses for upserts
* PUT returns 201 instead of 200 when rows are inserted
* POST with "Prefer: resolution=merge-duplicates" returns 200 instead of 201 when no rows are inserted
2023-10-26 20:49:20 -05:00
steve-chavez 82b38341bf feat: sql handlers for custom media types
* test text/html and drop HtmlRawOutputSpec.hs
* all tests passing, removed all pendingWith
* make functions compatible with pg <= 12
* move custom media types tests to own spec
* anyelement aggregate
* apply aggregates without a final function
* overriding works
* overriding anyelement with particular agg
* cannot override vendored media types
* plan spec works with custom aggregate
* renamed media types to make clear which ones are overridable
* correct content negotiation with same weight
* text/tab-separated-values media type
* text/csv with BOM plus content-disposition header
2023-10-26 09:04:02 -05:00
steve-chavez 4a90e9fbd9 break: db-root-spec default app/openapi+json media
BREAKING CHANGE

Can be done later with custom media types
2023-10-26 09:04:02 -05:00
steve-chavez 14d030b96c break: remove binary field logic, raw-media-types
BREAKING CHANGE

Can be done later with custom media types
2023-10-26 09:04:02 -05:00
Wolfgang Walther 6920a88dc0 nix: Fix postgrest-release on rel-branches pushing to the right branch 2023-10-25 19:09:21 +02:00
Wolfgang Walther 966f8df33f changelog: update to 11.2.2 2023-10-25 19:08:04 +02:00
Wolfgang Walther 98bd1de1cd nix: Allow running postgrest-release on rel- branches 2023-10-25 16:29:03 +02:00
Taimoor ZaeemandGitHub d94286c185 feat: add config to specify CORS origins (#2986) 2023-10-24 09:38:25 -05:00
Taimoor ZaeemandGitHub 618f93dec1 test: fix typos in batch upsert tests 2023-10-21 16:29:43 -05:00
Laurence IslaandGitHub 317619bf62 fix: regression by reverting fix that returned 206 when first-pos=length in Range header 2023-10-21 16:28:08 -05:00
steve-chavez eb238ad678 test: DRY scache/config sleep in io tests
Also increase schema cache sleep to 0.2 as 0.1 tends to fail in CI.
2023-10-20 17:53:54 -05:00
steve-chavez 54786a6c04 fix: unnecessary count() on RPC returning single 2023-10-19 15:54:22 -05:00
steve-chavez 00f3cb3746 test: prove RPC returning single always gives 1 2023-10-19 15:54:22 -05:00
Andrei DziahelandGitHub e977032847 build: add cabal.project.freeze (#3004) 2023-10-19 09:26:46 -05:00
dependabot[bot]andSteve Chavez 5c314e3f02 Bump urllib3 from 2.0.6 to 2.0.7
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.0.6 to 2.0.7.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.0.6...2.0.7)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-18 13:09:04 -05:00
Taimoor ZaeemandGitHub f10b4c3268 test: add tests for batch upserts (#3005) 2023-10-13 09:47:26 -03:00
Andrei DziahelandGitHub dc01c748ae feat: add more perf counters to Server-Timing (#2983) 2023-10-12 09:55:30 -03:00
Taimoor ZaeemandGitHub bae8dc3283 add documentation for JWT caching (#683) 2023-10-10 23:22:41 -03:00
Laurence IslaandGitHub 056c748c5f refactor: DRY and enforce error format 2023-10-10 11:42:36 -05:00
Steve ChavezandGitHub 93f2b0093c references: remark NOLOGIN impersonated roles 2023-10-07 19:20:48 -03:00
Taimoor ZaeemandGitHub 5ba370fe0a add documentation for handling preference (#684) 2023-10-07 00:23:41 -03:00
Kam Ting HoiandGitHub 818387f24e fix: range request with 0 rows and 0 offset return status 416 (#2991) 2023-10-07 00:10:17 -03:00
steve-chavez a9d6c318fd nix: instructions for static binary 2023-10-06 19:54:05 -03:00
steve-chavez 2aa58164ab refactor: move errors to ApiRequestError 2023-10-06 01:16:45 -03:00
steve-chavez df08d7f3ff refactor: rename JSONParseError to PGRSTParseError
The error name is too generic otherwise.
2023-10-06 01:16:45 -03:00
steve-chavez 3dd292be46 refactor: DRY application/json on Error.hs
Small refactor to help with:

https://github.com/PostgREST/postgrest/issues/2901
2023-10-06 01:16:45 -03:00
Laurence IslaandGitHub e29260d754 Version not set when invalid URI or key/value format 2023-10-05 15:18:25 -05:00
steve-chavez d9857e1c54 releases: add v11.2.1 2023-10-04 12:25:09 -03:00
steve-chavez 0703f27d2b changelog: update to 11.2.1 2023-10-04 12:12:30 -03:00
dependabot[bot]andSteve Chavez 8a7889b9b1 Bump urllib3 from 2.0.2 to 2.0.6
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.0.2 to 2.0.6.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.0.2...2.0.6)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-03 23:41:04 -03:00
Laurence IslaandGitHub af0e369c65 fix: regression that rejects URI connection strings with certain unescaped characters in the password 2023-10-03 10:57:21 -05:00
Laurence IslaandGitHub 910950dbac fix: RPCs not embedding correctly when using overloaded functions for computed relationships 2023-10-02 16:00:33 -05:00
Andrei DziahelandSteve Chavez 4c44782d15 refactor: complete purifying Response module 2023-09-30 09:35:50 -03:00
steve-chavez 3b1eb51744 ci: fix MacOS CI 2023-09-29 19:50:15 -03:00
steve-chavez cf7ee67dfe fix: arrow filter on RPC returning TABLE+composite 2023-09-28 20:26:38 -03:00
steve-chavez 290d90609b fix: unnecessary set default_transaction_isolation 2023-09-27 17:48:48 -03:00
Taimoor ZaeemandSteve Chavez 3c1cdd434a feat: add handling=strict/lenient for Prefer header 2023-09-27 15:00:52 -03:00
steve-chavez 2825ac059e refactor: make Response module pure 2023-09-27 11:50:22 -03:00
Taimoor ZaeemandGitHub a6e3eda5b2 feat: implement JWT caching (#2928) 2023-09-25 14:46:55 -03:00
steve-chavez 10231bc1a8 reorganize errors page 2023-09-23 20:29:01 -03:00
Laurence IslaandSteve Chavez 90e3a5e29f Add test option for PostgreSQL 16 2023-09-21 15:38:46 -03:00
Andrei DziahelandSteve Chavez add4dfeed5 ci: loadtest PRs against latest releases
Leveraging the previously-improved postgrest-loadtest-against
introduces loadtesting PRS against latest release as well.
2023-09-21 14:08:53 -03:00
Andrei DziahelandSteve Chavez fca039a54d nix:postgrest-loadtest-against multiple branches
Improve postgrest-loadtest-against script to accept multiple refs
to perform loadtests against *all* of them
2023-09-21 14:08:53 -03:00
Laurence IslaandSteve Chavez 1b4dae5a7a Add missing changelog entries 2023-09-19 00:15:48 -03:00
Laurence IslaandGitHub 37a3f818dd fix: error when requesting "Prefer: count=<type>" with null filters on embedded resources 2023-09-16 15:02:59 -05:00
Andrei DziahelandSteve Chavez c3169b7dc2 ci: less strict conditions for loadtest inmain 2023-09-15 16:35:20 -03:00
Taimoor ZaeemandSteve Chavez d64b71cbf0 refactor: rename test module NullsStrip.hs to NullsStripSpec.hs 2023-09-15 14:39:12 -03:00
Taimoor ZaeemandSteve Chavez c195eece65 feat: add Server-Timing header with JWT duration 2023-09-15 14:39:12 -03:00
Laurence IslaandGitHub fa182c216e fix: bug when Null Filtering on embedded resources
When doing Null Filtering, the to-one embed resources were not included if they had a NULL value in any of the selected fields.
2023-09-14 21:30:48 -05:00
Andrei DziahelandSteve Chavez 30474c424c ci: loadtest commits to main against latest release
Introduces loadtest job for main branch
that runs loadtest against latest release
2023-09-14 17:10:48 -03:00
Laurence IslaandGitHub 2888f351d1 Remove former sponsor 2023-09-12 22:41:39 -05:00
Taimoor ZaeemandGitHub 69eaef461f nix: add postgrest-docs-check (#676) 2023-09-12 23:18:17 -03:00
steve-chavez 8d3c9f8435 refactor: move media type logic to Plan module 2023-09-12 19:24:26 -03:00
steve-chavez d8a91453d1 fix: inconsistent Preference-Applied
* Don't apply `tx=commit` if the transaction doesn't commit
* Apply `count=exact`
* Also simplifies the Preference-Applied logic, removing the need for
  some functions.
2023-09-12 13:52:49 -03:00
dependabot[bot]andGitHub 50c87fec88 Bump actions/checkout from 3 to 4 (#678) 2023-09-11 18:33:29 -03:00
Laurence IslaandGitHub 4ee5410c3d Remove former sponsor 2023-09-11 15:19:10 -05:00
dependabot[bot]andGitHub 3f5e840baf build(deps): bump cachix/install-nix-action (#2935) 2023-09-11 03:00:29 -03:00
dependabot[bot]andGitHub 2358b6670f build(deps): bump actions/checkout from 3 to 4 (#2936) 2023-09-11 02:34:35 -03:00
Taimoor ZaeemandGitHub 96b8fccd2a allow add-headers-with-raise (#675) 2023-09-06 13:00:01 -03:00
Laurence IslaandGitHub d719cbf59d Fix and add images to htmx how-to 2023-09-05 12:38:51 -05:00
steve-chavez a6f1eefe55 fix linkcheck 2023-09-04 19:23:07 -03:00
steve-chavez 3dead2ac8b clarify access mode 2023-09-04 19:23:07 -03:00
steve-chavez 5a1ed11924 note on current_setting inconsistency
Closes #362.
2023-09-04 19:23:07 -03:00
dependabot[bot]andSteve Chavez 46eae105b7 Bump cachix/install-nix-action from 22 to 23
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 22 to 23.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v22...v23)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-04 17:14:42 -03:00
Taimoor ZaeemandSteve Chavez 8eed576826 fix: range request with first position same as length return status 206 2023-09-04 15:50:00 -03:00
Laurence IslaandSteve Chavez d643aab261 Add html-htmx how-to 2023-09-02 20:26:55 -05:00
Taimoor ZaeemandGitHub 07fef25591 feat: allow full response control when raising exceptions 2023-09-01 14:02:03 -05:00
Haowu GeandSteve Chavez 9352a72195 Add a regular account to reduce operational security risks 2023-08-29 14:22:11 -05:00
Taimoor ZaeemandSteve Chavez 7dc6e2b899 fix: duplicate headers in response 2023-08-25 13:52:50 -05:00
Taimoor ZaeemandSteve Chavez ff3a3a9100 add db-pool-automatic-recovery 2023-08-25 13:34:48 -05:00
Taimoor ZaeemandGitHub 57fa2719dd feat: add db-pool-automatic-recovery configuration to disable connection retrying 2023-08-23 21:08:04 -05:00
Diogo BiazusandGitHub b8b3145c5c fix: schema cache and configuration reloading with NOTIFY not working on Windows 2023-08-21 10:25:44 -05:00
steve-chavez 531a183b44 nix: postgrest-coverage notice 2023-08-17 13:43:48 -05:00
steve-chavez 739f056b0a nix: add postgrest-repl command 2023-08-17 13:43:48 -05:00
Steve Chavez 32b77cae6c Revert "nix: Update nixpkgs to 2023-08-04"
This reverts commit 2434724edd.
2023-08-15 23:55:35 -05:00
Laurence IslaandSteve Chavez 46548dd09b Fix typo and version 2023-08-14 22:54:39 -05:00
Taimoor ZaeemandSteve Chavez 2434724edd nix: Update nixpkgs to 2023-08-04 2023-08-14 15:00:20 -05:00
Taimoor ZaeemandSteve Chavez 87d6a0d0fe fix: application/vnd.pgrst.array not accepted as a valid mediatype 2023-08-11 12:41:04 -05:00
steve-chavez c820efb64a bump version to 11.2.0 2023-08-10 08:51:58 -05:00
steve-chavez cacbbc31fe add setof record functions 2023-08-10 08:49:55 -05:00
steve-chavez 78d9880607 add nulls=stripped 2023-08-10 08:49:55 -05:00
steve-chavez b33bfb656d add preference-applied for return=* 2023-08-10 08:49:55 -05:00
steve-chavez daad21e9ec add note to impersonated role settings 2023-08-10 08:49:55 -05:00
steve-chavez c229bee681 add disamb error 2023-08-10 08:49:55 -05:00
steve-chavez 9ed7ff8846 clarify FK joins 2023-08-10 08:49:55 -05:00
Laurence IslaandSteve Chavez 562f699770 Revert disamb using FK instead of computed rels 2023-08-10 08:49:55 -05:00
Laurence IslaandSteve Chavez 3f112553b0 Add missing ERD/SQL tabs and fix some requests 2023-08-10 08:49:55 -05:00
steve-chavez 637165ba5b use the foreign key join term 2023-08-10 08:49:55 -05:00
steve-chavez 095d43ff75 add dynamic schemas example
Closes #644
2023-08-10 08:49:55 -05:00
steve-chavez 49d4b9b7db fix dict 2023-08-10 08:49:55 -05:00
steve-chavez afee9b73a4 add return=minimal and organize other returns
Closes #624
2023-08-10 08:49:55 -05:00
steve-chavez 459f3f8173 add apache apisix tutorial 2023-08-10 08:49:55 -05:00
steve-chavez b0aaf7afe8 mention header names are lowercased
Closes #627
2023-08-10 08:49:55 -05:00
steve-chavez 54bfefdb55 move heroku outside Installation 2023-08-10 08:49:55 -05:00
steve-chavez a6f93f55b1 clear supported pg versions
closes #592
2023-08-10 08:49:55 -05:00
steve-chavez 291b6f3587 add different ways to specify the connection string
Closes #576
2023-08-10 08:49:55 -05:00
steve-chavez 43a8745e62 link connection string from connection pool
Closes #647
2023-08-10 08:49:55 -05:00
steve-chavez e82dc41a6a add link to github discussions
closes #401
2023-08-10 08:49:55 -05:00
steve-chavez 1b036dfab9 release page for v11.2.0
Closes #644
2023-08-10 08:49:55 -05:00
steve-chavez e30bf53afa deprecate: target embedding disambiguation 2023-08-10 08:14:23 -05:00
steve-chavez 5ce020d5bc fix: impersonated role applying superuser settings 2023-08-09 20:05:22 -05:00
Taimoor ZaeemandGitHub fbf9bf21c2 feat: add optional nulls=stripped parameter for mediatypes applicatio… (#2894) 2023-08-09 18:49:22 -05:00
Taimoor ZaeemandSteve Chavez d490bf09fd feat: add Preference-Applied header in response for Prefer: return=representation/minimal/headers-only 2023-08-05 02:21:50 -05:00
Laurence IslaandGitHub aa53623aac fix: error when a function returns RECORD or SETOF RECORD 2023-08-03 20:09:55 -05:00
steve-chavez 0fce7ca361 refactor: Response rm Paths_postgrest dependency
This allows using doctests on the Response.hs module
2023-08-02 16:28:48 -05:00
Taimoor ZaeemandGitHub d0a71de2da chore: improve details field of the singular error response (#2876) 2023-08-01 19:37:54 -05:00
steve-chavez fefd0e2e83 dry config and make it responsive
* shorten connection pool
* move securing execution plan
* shorten config intro page
2023-07-31 19:22:26 -05:00
Laurence IslaandGitHub 40c2bcd4a1 fix: paramaters of type character and bit not ignoring length
- Fixes the error "value too long for type character(1)" when the char length of the parameter was bigger than one.
2023-07-31 18:36:37 -05:00
dependabot[bot]andGitHub 0dc67bed0b build(deps): bump LouisBrunner/checks-action from 1.6.1 to 1.6.2 (#2883) 2023-07-31 00:17:36 -05:00
steve-chavez 2d00d7d248 fix: null filtering on embed when column=relation 2023-07-27 22:43:13 -05:00
Taimoor ZaeemandGitHub 2977d09779 chore: maintenance to the Nix development and build guide 2023-07-26 20:28:59 -05:00
Laurence IslaandGitHub 630e0a1691 fix: character and bit columns with fixed length not inserting/updating properly
Fixes the error "value too long for type character(1)" when the char length of the column was bigger than one.
2023-07-21 11:30:01 -05:00
steve-chavez 220683cef0 add HEAD optimization 2023-07-20 18:09:47 -05:00
steve-chavez 4c4a1de832 to_jsonb index no longer required on json/jsonb 2023-07-20 18:09:47 -05:00
a6aa2adf04 Remove embedding disambiguation in favor of computed rels (#660)
* Add SQL snippets for ERDs
---------

Co-authored-by: steve-chavez <stevechavezast@gmail.com>
2023-07-20 09:02:33 -05:00
steve-chavez 28d5278d62 fix: use index on jsonb/jsonb arrow filter/order 2023-07-12 14:51:21 -05:00
Steve ChavezandGitHub c74b9c4efa add version querying (#657) 2023-07-10 17:04:23 -05:00
steve-chavez e332f038ef fix: HEAD unnecessarily executing aggregates 2023-07-10 15:05:51 -05:00
steve-chavez 905fcb05cc refactor: add ResultAggregate type 2023-07-10 15:05:51 -05:00
Laurence IslaandGitHub 774d015eb5 feat: add the --version CLI option to print the version information 2023-07-07 18:19:02 -05:00
Laurence IslaandGitHub e752224f14 feat: show PostgREST version in the logs 2023-07-07 10:28:27 -05:00
steve-chavez add10bd10c test: doctest for addNullEmbedFilters 2023-07-06 16:52:46 -05:00
steve-chavez 52d3026133 refactor: remove unneeded FilterNullEmbed 2023-07-06 16:52:46 -05:00
Laurence IslaandGitHub 5cdafb23e7 ci: fix ARM docker containers not stopping immediately 2023-07-06 11:20:32 -05:00
steve-chavez 20b06efabe chore: show on ReadPlan and MediaType 2023-07-05 17:35:54 -05:00
Grantandsteve-chavez 4706472df9 Update connection_pool.rst typo 2023-07-05 12:13:23 -05:00
Laurence IslaandGitHub 7508230760 feat: allow to verify the PostgREST version through SQL 2023-07-03 18:31:32 -05:00
ab8ccf43a6 add domain representations (#655)
* add domain representations

* Update docs/references/api/domain_representations.rst

Co-authored-by: Alexander Ljungberg <aljungberg@wireload.net>

* Update docs/references/api/domain_representations.rst

Co-authored-by: Alexander Ljungberg <aljungberg@wireload.net>

* Update docs/references/api/domain_representations.rst

Co-authored-by: Alexander Ljungberg <aljungberg@wireload.net>

* Update docs/references/api/domain_representations.rst

Co-authored-by: Alexander Ljungberg <aljungberg@wireload.net>

* Update docs/references/api/domain_representations.rst

Co-authored-by: Alexander Ljungberg <aljungberg@wireload.net>

* Update docs/references/api/domain_representations.rst

Co-authored-by: Alexander Ljungberg <aljungberg@wireload.net>

* add custom domain subheading and TOC

this way it can be seen at the beggining there are advantages over views
and computed fields.

* important note for scache reloading

* clarify advantages

* why not create a type?

---------

Co-authored-by: Alexander Ljungberg <aljungberg@wireload.net>
2023-07-02 14:51:47 -05:00
steve-chavez a17dd41d6b fix: Prefer: missing=default with DOMAIN defaults 2023-06-30 17:46:16 -05:00
Alexander LjungbergandSteve Chavez 0a1564ba5a feat: data representations allow custom parsing and formatting of API fields.
See PR #2523. Most notable code changes:

- Load data representation casts into schema cache.
- Data representations for reads, filters, inserts, updates, views, over joins.
- `CoercibleField` represents name references in queries where coercion may be needed.
- `ResolverContext` help facilitate field resolution during planning.
- Planner 'resolves' names in the API query and pairs them with any implicit conversions to be used in the query builder stage.
- Tests for all of the above.
- More consistent naming (TypedX -> CoercibleX).

New: unit tests for more data representation use cases; helpful as examples as well.

New: update CHANGELOG with data representations feature description.

Fixed failing idempotence test.

New: replace date formatter test with one that does something.

Fixup: inadvertent CHANGELOG change after rebase.

Cleanup: `tfName` -> `cfName` and related.

Document what IRType means.

Formatting.

New: use a subquery to interpret `IN` literals requiring data rep transformation.

- With the previous method, very long queries such as `ANY (ARRAY[test.color('000100'), test.color('CAFE12'), test.color('01E240'), ...` could be generated. Consider the case where the parser function name is 45 characters and there's a hundred literals. That's 4.5kB of SQL just for the function name alone!
- New version uses `unnest`: `ANY (SELECT test.color(unnest('{000100,CAFE12,01E240,...}'::text[]))` to produce a much shorter query.
- This is likely to be more performant and either way much more readable and debuggable in the logs.
2023-06-29 15:01:58 -05:00
steve-chavez 1a9e105227 add headings to computed fields 2023-06-28 12:12:27 -05:00
Steve Chavez 3a41d55be6 Fix inserting with defaults 2023-06-27 15:37:22 -05:00
steve-chavez d59f841d15 add request body handling 2023-06-26 17:51:21 -05:00
steve-chavez 06a3898107 nix: add HISTFILE 2023-06-26 17:51:21 -05:00
steve-chavez 078c6ec08c refactor: remove SqlFragment and use SQL.Snippet 2023-06-22 21:46:16 -05:00
Steve ChavezandGitHub 991b151b69 improve connection pool wording 2023-06-22 14:44:26 -05:00
Steve ChavezandGitHub 14de9f1dfd references: add computed fields page (#650) 2023-06-22 09:14:23 -05:00
Laurence IslaandGitHub 83cf15fb7e ci: specify GHC_VERSION for ARM build on CI config
* change cabal version from recommended to latest
2023-06-21 16:54:15 -05:00
steve-chavez 40dc46ed2e fix: compilation on Ubuntu, GHC 9.0.2 compat 2023-06-21 16:22:30 -05:00
steve-chavez d9261fa674 ci: check against GHC 9.0.2 2023-06-21 16:22:30 -05:00
JoonasandSteve Chavez 9780790cee Change example getallfilms to stable instead of immutable 2023-06-21 09:51:41 -05:00
steve-chavez 856d450775 refactor: stricter plan media type 2023-06-20 19:12:17 -05:00
Laurence IslaandGitHub c1a8661ab3 ci: Update dependabot to look for dependencies in nested folder
- Bump cachix/install-nix-action from 21 to 22

- Add actions/setup-nix to dependabot dependencies
2023-06-19 12:33:42 -05:00
dependabot[bot]andGitHub ab986b77d8 Bump cachix/install-nix-action from 21 to 22 (#648) 2023-06-19 10:22:57 -05:00
steve-chavez aa15f4782e refactor: remove MTPlanAttrs to doctest 2023-06-18 15:27:25 -05:00
steve-chavez 77cd9387d4 Revert "refactor: plan media type"
This reverts commit 3cd3a3f8c6.
2023-06-18 15:27:25 -05:00
steve-chavez 3cd3a3f8c6 refactor: plan media type 2023-06-17 23:00:32 -05:00
steve-chavez 78821a8fe7 refactor: clarify content negotiation 2023-06-16 17:36:44 -05:00
steve-chavez 5c372df487 refactor: asJsonF/asJsonSingleF Routine param 2023-06-16 17:36:44 -05:00
Laurence IslaandGitHub 772046242e Add postgrest-docs-dictcheck to CI 2023-06-14 14:55:28 -05:00
Laurence IslaandGitHub fdf85773e7 Fix jwt_secret in example 2023-06-14 14:25:54 -05:00
steve-chavez fad47324c3 fix: OPTIONS not accepting all available media 2023-06-13 22:00:21 -05:00
Laurence IslaandGitHub 396afa106e Remove old or unmaintained links from the ecosystem page 2023-06-12 19:36:06 -05:00
dependabot[bot]andGitHub 11a9849152 build(deps): bump LouisBrunner/checks-action from 1.6.0 to 1.6.1 (#2817) 2023-06-12 10:34:48 -05:00
David BaynardandGitHub 018427e2b6 Note content-type for singular objects (#639)
* Clarify vnd.pgrst.object+json is also Content-Type

The previous docs explained how the `Accept` header could be set to
`application/vnd.pgrst.object+json`. This change indicates the resulting
response `Content-Type` begins likewise.

While robust clients will correctly identify that
`application/vnd.pgrst.object+json` data are json, many others which
naively check the `Content-Type` is `application/json` will fail to
recognize what should be a valid `Content-Type`.
2023-06-09 15:23:04 -05:00
steve-chavez ee6b9c5224 pin version to 11.0.1 2023-06-07 19:56:05 -05:00
steve-chavez b1f50537e9 clarify procs intro 2023-06-07 19:56:05 -05:00
steve-chavez fb59b82c35 references: updated automatic recovery
* use bullets were possible and join paragraphs.
* add bug fixes to the unreleased page
2023-06-07 19:56:05 -05:00
steve-chavez 188461af7d references: prepared statements mention 2023-06-07 19:56:05 -05:00
steve-chavez c62d24c993 references: add dynamic pool 2023-06-07 19:56:05 -05:00
steve-chavez 1f13e43abe bump version to 11.1.0 2023-06-07 19:03:31 -05:00
steve-chavez fac797c766 changelog: clarify 2781 2023-06-07 18:28:01 -05:00
steve-chavez 07cb0b582e feat: recover on pg_terminate_backend 2023-06-06 14:41:37 -05:00
steve-chavez d54a2f48de refactor: move checkIsFatal to AppState 2023-06-06 14:41:37 -05:00
steve-chavez bcce7b1c53 fix: stop retrying on "no password supplied" 2023-06-06 14:41:37 -05:00
steve-chavez 8d1961ce07 refactor: cache the isolation level 2023-06-06 14:41:37 -05:00
steve-chavez 9a19dff83e refactor: move debounceLogAcquisitionTimeout
Move it to AppState
2023-06-06 14:41:37 -05:00
paulocrandLaurence Isla e7d63a2b27 Fix boolean configuration example 2023-06-05 16:06:37 -05:00
steve-chavez a7ff2294ac references: move raise error to errors page 2023-06-03 00:20:21 -05:00
steve-chavez 54b9a0b8b3 test: fix hello function for pg 9.6 2023-06-02 17:45:25 -05:00
steve-chavez 9a3d453bf4 test: remove big_schema.sql from io tests
It was unused(test skipped) and made the scache reload slower, causing
test failure on CI.
2023-06-02 17:45:25 -05:00
steve-chavez 4f6c466031 nix: postgrest-run uses similar config to tests
Otherwise manual tests can be deceiving
2023-06-02 17:45:25 -05:00
Steve ChavezandGitHub a852b766eb Fix dropping schema cache reload notifications
* test: bad schema reload
* refactor: DRY using the "extra" lib
* refactor: move worker funtions inside AppState
* Also rename Workers.hs to Admin.hs
2023-06-02 12:22:17 -05:00
Laurence IslaandGitHub 48365188ad Add redirect to many-to-many relationships from embedding through join tables 2023-05-31 12:33:08 -05:00
dependabot[bot]andSteve Chavez d87d80108a Bump cachix/install-nix-action from 20 to 21
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 20 to 21.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v20...v21)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-29 09:56:01 -05:00
steve-chavez d59e2498be add pre-config function 2023-05-28 10:23:37 -05:00
steve-chavez 1c15d3d35b remove note and put it at the start 2023-05-28 10:23:37 -05:00
steve-chavez 232c6dc702 dry config settings
Environment variables names and in-db configs on the table.
2023-05-28 10:23:37 -05:00
steve-chavez 14be3fb671 feat: add pre-config function
Allows using the in-database configuration without SUPERUSER
2023-05-28 10:21:13 -05:00
steve-chavez 8a3686d86b refactor: whitelist in-db config settings 2023-05-28 10:21:13 -05:00
steve-chavez 8d706a342c fix broken links 2023-05-27 22:23:44 -05:00
steve-chavez fff0eae77e Fix in-db config settings 2023-05-27 22:23:44 -05:00
steve-chavez 5e19594ea4 auth: explain why jwt is used 2023-05-27 22:23:44 -05:00
steve-chavez 281d7a9223 remove db functions section on index 2023-05-27 22:23:44 -05:00
Steve Chavez 2e6ebca885 Fix link to archlinux pkg 2023-05-27 22:23:44 -05:00
steve-chavez 009250006e feat: add db-pool-timeout alias
For db-pool-max-idletime
2023-05-22 18:34:18 -03:00
steve-chavez 38ad8c04e1 nix: withTmpDb set search_path to test
It gets repetitive to set the search_path every time you go in the test
db with psql.
2023-05-22 13:46:24 -03:00
dependabot[bot]andGitHub 54cbf147e3 build(deps): bump codecov/codecov-action from 3.1.3 to 3.1.4 (#2794) 2023-05-22 11:34:18 -05:00
RobertandGitHub f9f0f79fa9 feat: use hasql-pool-0.10, add db-pool-max-idletime (#2786)
- new option db-pool-max-idletime limits the maximal idle time of a
  postgresql connection, defaults to 30 seconds
- this essentially restores the db-pool-timeout option from versions
  <=10, with more specific name
2023-05-16 23:00:40 +02:00
Laurence IslaandGitHub 05bd033539 Add JavaScript redirect from the old api.html to new pages and sections
Uses the hashtag in the url to redirect to the sections where the content was moved to
2023-05-12 21:48:01 -05:00
Laurence IslaandGitHub ce82b0c79b Re-add missing legacy gucs information 2023-05-12 21:27:12 -05:00
steve-chavez 911634ed32 move impersonated settings 2023-05-12 01:14:19 -03:00
steve-chavez 946b113912 add function inlining 2023-05-11 21:57:54 -03:00
steve-chavez d7907f4aa4 pin version to 11.0.1 2023-05-11 01:37:50 -03:00
steve-chavez ed147ce5c8 add bug fixes
simply link to github releases page.
bug fixes do not add anything for docs.
2023-05-11 01:37:50 -03:00
steve-chavez 789cedff08 add isolation level 2023-05-11 01:37:50 -03:00
steve-chavez e0b0e98816 add impersonated settings 2023-05-11 01:37:50 -03:00
steve-chavez ea7a2bd531 add bulk insert defaults 2023-05-11 01:37:50 -03:00
steve-chavez 84b01dea99 add recursive m2m disamb 2023-05-11 01:37:50 -03:00
steve-chavez f02be3fb76 add spread embed 2023-05-11 01:37:50 -03:00
steve-chavez 9595ed845e add null filtering embeds 2023-05-11 01:37:50 -03:00
steve-chavez d15dc680af add top-level ordering 2023-05-11 01:37:50 -03:00
steve-chavez 8ae6b012f4 add empty embed filter 2023-05-11 01:37:50 -03:00
steve-chavez 7832ddba52 add any/all modifiers 2023-05-11 01:37:50 -03:00
steve-chavez e813375c7d openapi title override 2023-05-11 01:37:50 -03:00
steve-chavez 1de06f5332 add openapi overriding 2023-05-11 01:37:50 -03:00
steve-chavez cb7d70c197 add trace header 2023-05-11 01:37:50 -03:00
steve-chavez 0bd43663fc add isdistinct 2023-05-11 01:37:50 -03:00
steve-chavez c56108b8a9 add breaking changes 2023-05-11 01:37:50 -03:00
steve-chavez dec1e03a0e add v11 release notes with docs improvs 2023-05-11 01:37:50 -03:00
Laurence IslaandLaurence Isla d593bcff80 Fix remaining info about pg-safeupdate in nginx docs 2023-05-10 15:35:22 -05:00
steve-chavez c5d6526d99 move pg-safeupdate to own integration 2023-05-10 14:21:31 -03:00
steve-chavez fc635d18ae split the API reference 2023-05-10 14:21:31 -03:00
steve-chavez c82b23940b add missing mention to cache reload on recovery 2023-05-10 12:52:39 -03:00
steve-chavez a530030b71 add integrations
* Move Nginx and systemd to integration
* Move External JWT Generatio to integration
* greenplum short notice
2023-05-08 14:33:29 -03:00
steve-chavez 637a6e281d Move part of admin to references
- Add integrations section with Nginx and SystemD
- Move Nginx and SystemD sections from admin on integrations
2023-05-08 13:17:33 -03:00
steve-chavez 79620396ed remove manual inclusion of pages on index
* drop unavailable doc pages from release notes
* rename releases pages so they order in TOC
2023-05-08 08:58:09 -03:00
Steve ChavezandGitHub 36300da16d connection pool reference (#616)
* add link in index to external jwt
* change pgrst errors location for better reading
2023-05-07 10:25:12 -05:00
steve-chavez a867d79c42 nix: withTmpDb psql notice can't mutate the db
Use the postgres role instead of the authenticator on the notice
2023-05-05 20:03:09 -03:00
steve-chavez 082e9b14e9 transactions reference doesn't consider anon reqs
Change authenticated to user impersonation.
2023-05-05 12:04:46 -03:00
steve-chavez 2e1dc0738a readthedocs doesn't build anymore
Pin the python version to solve it.
2023-05-05 12:04:46 -03:00
steve-chavez 9a3973be35 add transactions reference 2023-05-05 12:04:46 -03:00
steve-chavez 560ec79a90 clarify schema cache and errors page 2023-05-05 12:04:46 -03:00
steve-chavez 9de94274cf shorten some paragraphs in errors page 2023-05-03 17:47:54 -03:00
steve-chavez 837df8b045 schema cache title is redundant on navbar
Use a raw html title for correcting this
2023-05-03 17:47:54 -03:00
steve-chavez 4fb13ebae6 add tables to db authz 2023-05-03 17:47:54 -03:00
steve-chavez 8ec925c33e clarify view security invoker feature
Move RULEs limitation to Insertions
2023-05-03 17:47:54 -03:00
steve-chavez d1d159d1e0 join schema structure with db authz 2023-05-03 17:47:54 -03:00
steve-chavez 4f4ae27a2d clarify custom validation 2023-05-03 08:45:05 -03:00
steve-chavez 764f0a0352 link authn to db authz 2023-05-03 00:38:23 -03:00
steve-chavez 473035575c database as single source of truth philosophy
make it clear at the index
2023-05-03 00:06:17 -03:00
steve-chavez 638aa8369d fix broken link 2023-05-03 00:06:17 -03:00
steve-chavez 55cc616a5e Authentication as a reference
* Creates a Database Authorization topic
* Moves JWT from Auth0 into Ecosystem
* Moves SQL User Management to a how-to
2023-05-03 00:06:17 -03:00
steve-chavez 4197d2f739 bump version to 11.0.1 2023-04-27 21:30:40 -05:00
steve-chavez c10ba8e214 fix: skew of 30 seconds for JWT validation 2023-04-27 21:06:06 -05:00
steve-chavez 887948d259 fix: missing=default error msg on generated column 2023-04-27 19:27:09 -05:00
steve-chavez c63786733a fix: no schema privilege for schema cache
This was due to the usage of pg_get_serial_sequence
2023-04-27 19:27:09 -05:00
steve-chavez 4fe696dd96 nix: PGRST_DB_ANON_ROLE default for postgrest-run
Makes manually testing a feature easier
2023-04-27 19:27:09 -05:00
steve-chavez 67936b343f test: prove that authenticator is not a superuser 2023-04-27 19:27:09 -05:00
steve-chavez b0e395f495 nix: no SUPERUSER for connection role
Change :USER to :PGUSER in SQL scripts
2023-04-27 19:27:09 -05:00
Steve ChavezandLaurence Isla 4e49310d6e Clarify pool connection features 2023-04-25 20:27:02 -05:00
424eac4b3e Add documentation for v10.2.0
Co-authored-by: Steve Chavez <stevechavezast@gmail.com>
2023-04-25 20:27:02 -05:00
dependabot[bot]andGitHub 3b55a27ef3 build(deps): bump codecov/codecov-action from 3.1.2 to 3.1.3 (#2765) 2023-04-24 11:21:49 -05:00
Laurence IslaandGitHub f55ced0306 Add a warning when working with computed relationships 2023-04-21 17:04:31 -05:00
Steve Chavez 516a4daacf snippet for computed rel overload 2023-04-18 17:16:51 -05:00
Steve Chavez 3ff9b81c7d Remove public from schema cache event trigger 2023-04-18 13:25:01 -05:00
dependabot[bot]andGitHub 43da81c30c build(deps): bump codecov/codecov-action from 3.1.1 to 3.1.2 (#2757) 2023-04-18 12:25:50 -05:00
steve-chavez dd2f5511d8 bump version to 11.0.0 2023-04-16 14:22:38 -05:00
steve-chavez 49c349c846 chore: CHANGELOG to include release 10.2.0 2023-04-16 12:18:10 -05:00
steve-chavez aaf77902f6 feat: isolation level for roles/functions 2023-04-15 18:05:04 -05:00
steve-chavez 4c555cbd5d refactor: authRole to ByteString 2023-04-15 18:05:04 -05:00
steve-chavez 3e53796120 correct missing=default with GENERATED BY column 2023-04-14 22:12:34 -05:00
Laurence IslaandGitHub 0a2b7064c7 fix: PATCH requests not recognizing embedded filters
- Already fixed in #2618
- Adds tests and CHANGELOG
2023-04-14 16:42:55 -05:00
Laurence IslaandGitHub f588bb6daf Fix info on updates without filters 2023-04-13 18:38:07 -05:00
steve-chavez ce378e6b3a refactor: clarify Scalar type
It now contains the type of the scalar. This way we can discriminate the
void type in a more obvious way.
2023-04-13 18:17:43 -05:00
steve-chavez feadf59bb3 refactor: rename Proc module to Routine
Assuming that functions=procedures is wrong since pg11, which introduced
real stored procedures.
2023-04-13 18:17:43 -05:00
Laurence IslaandGitHub ad7d80a430 ci: clean files from arm server only on build failure/cancel for a version release 2023-04-13 16:29:26 -05:00
steve-chavez e572d1d1a2 feat: configurable role settings 2023-04-10 14:27:08 -05:00
steve-chavez c06237cc56 fix: db settings and pg version query not prepared 2023-04-10 14:27:08 -05:00
steve-chavez c656a870f4 bump version to 10.2.0.20230407 2023-04-07 14:02:14 -05:00
steve-chavez 1b625cb77a feat: any/all modifiers for operators
Only for the eq,like,ilike,gt,gte,lt,lte,match,imatch operators
2023-04-07 13:10:57 -05:00
Robert Vollmert 394bd22148 feat: use hasql-pool-0.9, add db-pool-max-lifetime (fixes #2638)
- db-pool-acquisition-timeout is no longer optional, defaults to 10s
- new option db-pool-max-lifetime limits the maximal lifetime of a
  postgresql connection, defaults to 30m
2023-04-06 22:40:22 +02:00
Robert Vollmert 963416ae29 test: stabilize log probe in test_pool_acquisition_timeout
There's two lines of log output, and their order is not deterministic.
2023-04-06 22:40:22 +02:00
Laurence IslaandGitHub d3b10e7b2a feat: Allow filtering by IS DISTINCT FROM using the isdistinct operator 2023-04-06 13:02:03 -05:00
Steve ChavezandGitHub acf62320ef RPC returning table alias now works for pg 11/12 (#2737)
The new LATERAL query used for calling the function, introduced on
https://github.com/PostgREST/postgrest/pull/2677, failed on functions
that returned a domain like `CREATE DOMAIN projects_domain AS projects`.

Work around that by changing the query conditionally, by
obtaining a bool that represents the composite alias on the SchemaCache
and only do this on pg 11 and 12.
2023-04-05 15:34:41 -05:00
steve-chavez 16f2849724 refactor: wrap proc logic in asJsonF
* add explicit logic for returning setof scalars
2023-04-04 19:55:10 -05:00
steve-chavez d945e8c06a refactor: remove Maybe from RetType for Proc type 2023-04-04 19:55:10 -05:00
steve-chavez bc1fb67df0 drop: Prefer: params=multiple-objects on RPC
BREAKING CHANGE

A function with a JSON array or object parameter should be used instead
2023-04-04 19:55:10 -05:00
Wolfgang Walther 1442e02f5f chore: Allow seed-cachix job to keep running when part of the matrix fail 2023-04-04 18:52:52 +02:00
Wolfgang WaltherandWolfgang Walther 68d2d834ba nix: Update nixpkgs to 2023-03-25 2023-04-04 18:51:34 +02:00
Steve ChavezandGitHub eb777cf823 Update BACKERS.md 2023-04-03 11:09:19 -05:00
Laurence IslaandGitHub 1aed55be68 Ignore the Range header when the method is different than GET
fix: bug when using Range header on PATCH/DELETE
  - Fix the "message": "syntax error at or near \"RETURNING\"" error
  - Fix doing a limited update/delete when an order query parameter was present

breaking: The Range header is now only considered on GET requests and is ignored for any other method
  - Other methods should use the `limit/offset` query parameters for sub-ranges
  - PUT requests no longer return an error when this header is present
2023-04-03 10:40:25 -05:00
steve-chavez 032f07f3f0 refactor: remove character varying casting 2023-03-29 13:17:15 -05:00
steve-chavez 7629eff51d undefined-keys=apply-defaults to missing=default 2023-03-29 05:33:38 -05:00
steve-chavez 46fd856fc6 test: tidy inlining plan test 2023-03-29 05:33:38 -05:00
Laurence IslaandGitHub caaa9a3944 ci: Update ARM build script to use GHC 9.2.4 2023-03-24 20:23:43 -05:00
Steve ChavezandGitHub 439a96c578 feat: undefined json keys as defaults w/ Prefer:undefined-keys 2023-03-22 02:44:14 -05:00
Robert Vollmert e731241b97 test: optionally build postgrest with cabal in postgrest-loadtest
By default, postgrest-with-pgrst builds postgrest as a nix package,
which means that source changes cause a full rebuild. With this
change, running the loadtest as

PGRST_BUILD_CABAL=1 postgrest-loadtest

rebuilds directly using cabal, like postgrest-build. Note that
results between nix and cabal builds aren't necessarily comparable
due to differing build parameters.
2023-03-20 15:00:11 +01:00
Robert Vollmert 216dc833fd test: add optional latency to postgrest-loadtest
This pipes data between client/postgrest and between
postgrest/database through a proxy that adds delay
(github.com/robx/slocat).
2023-03-20 15:00:11 +01:00
Robert Vollmert a8e02f766b perf: Allow preparing pre-request SQL statement
This will prepare the pre-request statement (if prepared statements
are enabled) with a possible minor performance benefit -- the load
test is not entirely conclusive.
2023-03-20 13:40:43 +01:00
steve-chavez cae1c67b00 refactor: add iPreferences to ApiRequest
It avoids adding a new iPrefer.. to the ApiRequest every time a
preference is added.
2023-03-09 14:40:32 -05:00
Steve ChavezandGitHub b05ea14122 nix: add notices for postgrest-with-* commands (#2697)
For knowing where to connect and how to get logs
2023-03-07 12:42:41 -05:00
Steve ChavezandGitHub 666114f81d refactor: remove SchemaCache from ApiRequest (#2695) 2023-03-06 13:22:35 -05:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6da204d554 Bump cachix/install-nix-action from 19 to 20 (#597)
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 19 to 20.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v19...v20)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-06 11:17:46 -05:00
Steve ChavezandGitHub 3e99995e6a feat: make db-root-spec stable (#2694) 2023-03-01 12:23:19 -05:00
steve-chavez 8213c58452 ci: pin Nix version to avoid error
To 2.13.3
2023-02-28 17:30:54 -05:00
steve-chavez ee036f8397 feat: add server-trace-header config for tracing 2023-02-28 17:30:54 -05:00
steve-chavez 5b6421d03a test: fix sporadic failure on PlanSpec 2023-02-28 17:30:54 -05:00
dependabot[bot]andSteve Chavez ba00cb98f4 build(deps): bump actions/upload-artifact from 2.3.1 to 3.1.2
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2.3.1 to 3.1.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2.3.1...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 12:00:46 -05:00
dependabot[bot]andSteve Chavez 6184921edc build(deps): bump actions/checkout from 2.4.0 to 3.3.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 2.4.0 to 3.3.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2.4.0...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 10:59:38 -05:00
Robert Vollmert 9e0ea113fb refactor: split pre-request function out of setPgLocals 2023-02-27 15:12:32 +01:00
steve-chavez 0d5d209bfb perf: use LATERAL instead of CTE for json body
* add pgbench test for INSERT and RPC
* use LATERAL for RPC and INSERT
* add tests for inlining
2023-02-25 15:01:06 -05:00
steve-chavez 253f2bf537 ci: remove GHC 8.10.7 2023-02-25 14:19:52 -05:00
steve-chavez 2a2889020a nix: fix PGTZ on postgrest-with-* commands 2023-02-20 17:23:11 -05:00
Steve ChavezandGitHub 6a79de67ce fix: log to stderr on AcquisitionTimeoutUsageError (#2667)
* refactor: remove uneeded type on checkIsFatal
* dry with a logPgrstError function
2023-02-17 17:38:31 -05:00
steve-chavez c49932d3a8 nix: UTC by default for postgrest-with-postgresql 2023-02-14 21:30:45 -05:00
RobertandGitHub 95d71281d6 refactor: fix inaccurate variable name (#2662) 2023-02-14 17:09:48 +01:00
Steve ChavezandGitHub cf8ced1014 remove wrong claim about CSV being faster (#595) 2023-02-13 22:29:38 -05:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
adeb622d31 Bump cachix/install-nix-action from 18 to 19 (#593)
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 18 to 19.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v18...v19)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-13 12:34:33 -05:00
Steve ChavezandGitHub 87b383316d fix: PGRST003 status code to 504 (#594) 2023-02-13 11:06:22 -05:00
Ian EybergandGitHub 6089a46235 adding link to ops unikernel package (#586) 2023-02-09 18:43:11 -05:00
steve-chavez a1e2fe308f bump version to 10.2.0.20230209 2023-02-09 17:18:31 -05:00
Laurence IslaandGitHub 6b65a5b151 Add HTTP status codes to PGRST errors (#590) 2023-02-09 16:48:17 -05:00
steve-chavez a9d66d1dac ci: fix ghcup 2023-02-09 16:46:27 -05:00
steve-chavez f4135bb3ff nix: correct postgrest-release pre 2023-02-09 16:46:27 -05:00
steve-chavez d7868235d8 refactor: add getRanges func to ApiRequest 2023-02-09 16:46:27 -05:00
Laurence IslaandGitHub 60f4446fd8 Change inaccurate error codes to new ones
- Code for column is not found is PGRST204
- Code for timed out when acquiring connection to db is PGRST003
2023-02-09 14:57:51 -05:00
Laurence IslaandGitHub 378a225a9b Add db-pool-acquisition-timeout configuration parameter 2023-02-09 14:56:38 -05:00
Laurence IslaandGitHub c893ac15dc Add the missing 'get' path item for RPCs to the OpenAPI output 2023-02-09 12:04:41 -05:00
dependabot[bot]andSteve Chavez ecada119f8 build(deps): bump LouisBrunner/checks-action from 1.5.0 to 1.6.0
Bumps [LouisBrunner/checks-action](https://github.com/LouisBrunner/checks-action) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/LouisBrunner/checks-action/releases)
- [Commits](https://github.com/LouisBrunner/checks-action/compare/v1.5.0...v1.6.0)

---
updated-dependencies:
- dependency-name: LouisBrunner/checks-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 09:21:31 -05:00
steve-chavez 57e32ca0a3 bump version to 10.2.0.20230203 2023-02-03 21:16:43 -05:00
Laurence IslaandGitHub eb3f7c696a Fix release date for v10.1.2 in CHANGELOG 2023-02-02 14:50:42 -05:00
Laurence IslaandSteve Chavez 09249f97ab Update CHANGELOG to include release 10.1.2 2023-02-02 14:40:34 -05:00
Laurence IslaandGitHub 754282c385 Add missing fixes to the changelog 2023-02-01 22:15:43 -05:00
steve-chavez 52ba757ebe nix: postgrest-release bumps pre minor 2023-01-31 19:11:45 -05:00
steve-chavez 7aadaa44e8 refactor: remove Target from infoResponse 2023-01-30 00:23:24 -05:00
steve-chavez 0139cd8261 refactor: move txMode to Plan 2023-01-30 00:23:24 -05:00
Steve ChavezandGitHub 082b418334 Add db-plan-enabled recommendation (#588)
* fix broken links
2023-01-28 20:20:48 -05:00
steve-chavez 7874bee879 fix: NOTIFY pgrst not reoading the catalog cache 2023-01-28 10:30:29 -05:00
RobertandGitHub 12ad7d0585 test: fix result path for load test (#2628) 2023-01-25 08:56:52 +01:00
AnthonyFisiandGitHub 9065ed65fa feat: customizable OpenAPI title 2023-01-21 19:34:55 -05:00
Tuan LeandSteve Chavez 8aa79086d2 fix: consider authentication failure as a fatal error 2023-01-20 13:51:28 -05:00
Laurence IslaandGitHub 775c006806 fix: FK pointing to VIEW instead of TABLE in OpenAPI output 2023-01-20 09:37:15 -05:00
steve-chavez be6c30a0fb refactor: dry RPC param parsing 2023-01-18 12:26:13 -05:00
RobertandGitHub 8f80cd1469 Update nixpkgs, dependencies (#2612)
* relax upper bounds on HTTP, hspec, lens-aeson, optparse-applicative (fixes #2580)
* upgrade stackage snapshot to latest LTS, with GHC 9.2.5
* bump nixpkgs to 2023-01-12
* fix complaints due to updated linters
2023-01-16 17:12:54 +01:00
RobertandGitHub 5fd6b3956e ci: Add workflow to seed cachix (#2615)
Fixes #2609.
2023-01-16 16:56:48 +01:00
RobertandGitHub f353711ed2 ci: build with nix on macos (and push to cachix) (#2610)
The idea is to ensure the macos nix build works, and to
build a cache in cachix to help local development on macos.
2023-01-12 11:45:10 +01:00
Robert Vollmert 0a56d6ce88 ci: authenticate GitHub API request (fixes #2607) 2023-01-10 18:45:50 +01:00
Alexander LjungbergandGitHub 43ad6d6aa0 feat: validate ?columns mutation targets based on schema cache (#2542)
This returns an error for trying to update or insert into invalid columns, without hitting the database. This change also switches from `json_populate_recordset` for these operations `json_to_recordset` which should make no functional difference except allowing future flexibility.
2023-01-07 22:08:30 -05:00
Franz-Josef FärberandSteve Chavez 8be87ed4ac Minor improvements to sql-user-management-using-postgres-users-and-passwords.rst 2023-01-04 02:22:41 -03:00
RobertandGitHub 5a0f83ecb8 bump postgresql-libpq (#2599)
For https://github.com/PostgREST/postgresql-libpq/pull/2.
2022-12-30 15:40:17 +01:00
Laurence IslaandGitHub 4ddd5b4a76 Add upsert headers for POST requests to the OpenAPI output 2022-12-27 16:16:41 -05:00
Laurence IslaandGitHub 1f69757835 fix: Add required OpenAPI items object when the paramater is an array 2022-12-20 12:30:28 -05:00
steve-chavez e0df95844b bump version to 10.1.1.20221215 2022-12-15 23:06:32 -05:00
Laurence IslaandGitHub 5a660e0cbe Add suggestions with fuzzy text search when no relationship is found (#2583) 2022-12-15 18:33:15 -05:00
steve-chavez 566c6fe53f correct bad null filter restriction
When embedding using the column name.

Also make related order similarly strict to avoid the same kind of issues.
2022-12-15 18:02:25 -05:00
steve-chavez d93bb95d83 bump version to 10.1.1.20221212 2022-12-12 17:34:46 -05:00
steve-chavez 3773cce246 feat: null filters on embedded resources 2022-12-12 17:14:54 -05:00
steve-chavez 52c011f896 refactor: remove Bool from readPlanToQuery 2022-12-12 17:14:54 -05:00
steve-chavez 81cd9d4b15 refactor: delete unused TestTypes 2022-12-12 13:31:02 -05:00
steve-chavez 781fa592ca refactor: delete QueryCost, instead use PlanSpec 2022-12-12 13:31:02 -05:00
steve-chavez 1065021348 refactor: add planCost and planHdr for tests 2022-12-12 13:31:02 -05:00
Laurence IslaandGitHub aecc53d8f9 fix: clarify error messages for functions
Move explanation on single unnamed parameters to the error details
2022-12-09 14:34:38 -05:00
steve-chavez a5465e20d0 correct spread embed to '...' 2022-12-05 19:38:32 -05:00
Laurence IslaandGitHub 9e567216e9 feat: hint function names/parameters on error 2022-12-01 17:17:45 -05:00
Steve ChavezandGitHub 5e9dba5292 feat: Allow embedding without selecting any column (#2574) 2022-11-25 18:37:57 -05:00
steve-chavez f7009635d6 test: move failed order parsing to doctest 2022-11-24 09:03:35 -05:00
steve-chavez e5c77385ae test: move failed or/and parsing to doctest 2022-11-24 09:03:35 -05:00
steve-chavez 3103060f4b test: move failed json parsing to doctest 2022-11-24 09:03:35 -05:00
steve-chavez 315b01ebf7 test: spread embed includes junction cols 2022-11-18 18:11:14 -05:00
steve-chavez 25f65065f4 test: spread embed disambiguates recursive m2m 2022-11-18 18:11:14 -05:00
steve-chavez 60c0c11c1d test: spread embed disambiguates 2 fks on junction 2022-11-18 18:11:14 -05:00
steve-chavez cb99270a8f fix: bad M2M embed on RPC 2022-11-18 17:49:24 -05:00
steve-chavez cca0b5ae66 messages: better error for related order 2022-11-16 21:26:16 -05:00
steve-chavez 2aa0e091bb feat: add spread embeds 2022-11-16 21:26:16 -05:00
fjf2002andGitHub f6e3b77643 how-to: SQL User Management using postgres’ users and passwords (#581) 2022-11-16 17:10:18 -05:00
Laurence IslaandSteve Chavez f3831427e5 Fix broken links and dictcheck 2022-11-14 22:14:04 -05:00
Laurence IslaandSteve Chavez 485e4db4d7 Fix auth page due to anonymous requests not allowed when db-anon-role is not set 2022-11-14 22:14:04 -05:00
Laurence IslaandSteve Chavez a6af266ab7 Fix step 3 of the tutorial 0 2022-11-14 22:14:04 -05:00
steve-chavez 78d45b4e32 feat: add related orders 2022-11-08 17:28:50 -05:00
steve-chavez ef42d1c87f test: add doctest for pOrderTerm 2022-11-08 17:28:50 -05:00
Andrea BernicchiaandGitHub 50eb42bf46 LICENSE file added (#579) 2022-11-02 12:23:35 -05:00
Andrea Bernicchiaandsteve-chavez 8a98b4ed26 Update heroku installation docs
* Heroku installation docs aligned with https://github.com/PostgREST/postgrest-heroku/pull/40
* Update install.rst
2022-10-27 16:38:25 -05:00
dependabot[bot]andSteve Chavez 73f759ef3a Bump cachix/install-nix-action from 17 to 18
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 17 to 18.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v17...v18)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-17 21:06:34 -05:00
Laurence IslaandGitHub fdb975c87c Add information on preflight requests in CORS subsection 2022-09-21 19:09:50 -05:00
Laurence IslaandSteve Chavez d507f2f0f4 Shorten explanations for the working with types section 2022-09-19 21:55:31 -05:00
mdr1384andSteve Chavez 022101ae3c Fix example code in computed relationships
Looks like a copy-paste error - the `films.id` column should be associated with the `premieres.film_id` column.
2022-09-15 18:33:14 -05:00
240 changed files with 20203 additions and 10367 deletions
+16
View File
@@ -0,0 +1,16 @@
freebsd_instance:
image_family: freebsd-14-0
build_task:
# Don't change this name without adjusting .github/workflows/ci.yaml
name: Build FreeBSD (Stack)
install_script: pkg install -y postgresql13-client hs-stack git
stack_cache:
folders: /.stack
fingerprint_script: cat postgrest.cabal stack.yaml.lock
reupload_on_changes: false
build_script: stack build -j 1 --local-bin-path . --copy-bins
bin_artifacts:
path: postgrest
+12
View File
@@ -3,4 +3,16 @@ When submitting a new feature or fix:
- Add a new entry to the CHANGELOG - https://github.com/PostgREST/postgrest/blob/main/CHANGELOG.md#unreleased
- If relevant, update the docs
- Use a prefix for the PR title or commits, e.g. "fix: description of the fix".
+ `fix`, bug fixes
+ `feat`, new features added
+ `perf`, performance improvements
+ `nix`, related to the Nix development environment
+ `ci`, related to the Continuous Integration modules
+ `test`, related to the testing modules
+ `refactor`, refactoring code
+ `deprecate`, deprecating a feature
+ `chore`, maintenance (changelog, build process, etc.)
+ Other prefixes may be used if necessary
- If there's a breaking change, add `BREAKING CHANGE` and an explanation to your commit message
-->
@@ -0,0 +1,119 @@
name: Artifact from Cirrus
description: Waits for a specific Cirrus CI run to complete, then downloads the artifact and uploads it to the current workflow. This will silently succeed if Cirrus CI did not schedule a task within 2 minutes.
inputs:
download:
description: Name of Artifact to download from Cirrus CI
required: true
task:
description: Name of Cirrus Task
required: true
token:
description: GitHub Token
required: true
upload:
description: Name of Artifact to upload on GitHub Actions
required: true
runs:
using: composite
steps:
- shell: bash
run: echo "GH_TOKEN=${{ inputs.token }}" >> "$GITHUB_ENV"
- name: Wait for Check Suite to be created
id: check-suite
env:
# GITHUB_SHA does weird things for pull request, so we roll our own:
COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
shell: bash
run: |
get_check_runs_url() {
gh api "repos/{owner}/{repo}/commits/${COMMIT}/check-suites" \
| jq -r '.check_suites[] | select(.app.slug == "cirrus-ci") | .check_runs_url'
}
for _ in $(seq 1 12); do
check_runs_url="$(get_check_runs_url)"
if [ -z "$check_runs_url" ]; then
echo "Cirrus CI task has not started, yet. Waiting..."
sleep 10
else
echo "check_runs_url=$check_runs_url" >> "$GITHUB_OUTPUT"
exit 0
fi
done
>&2 echo "Cirrus CI check suite not found. Is Cirrus CI enabled for this repo?"
- name: Find task by name
id: find-task
if: steps.check-suite.outputs.check_runs_url
shell: bash
run: |
get_number_of_tasks() {
gh api "${{ steps.check-suite.outputs.check_runs_url }}" \
| jq -r '.check_runs | map(select(.name == "${{ inputs.task }}")) | length'
}
tasks="$(get_number_of_tasks)"
case "$tasks" in
0)
echo "Task not found, assuming it's skipped intentionally..."
exit 0
;;
1)
echo "task_found=1" >> "$GITHUB_OUTPUT"
exit 0
;;
*)
>&2 echo "More than 1 task with the same name found. Don't know what to do..."
exit 1
;;
esac
- name: Wait for Cirrus CI to complete task
if: steps.find-task.outputs.task_found
shell: bash
run: |
get_conclusion() {
gh api "${{ steps.check-suite.outputs.check_runs_url }}" \
| jq -r '.check_runs[] | select(.name == "${{ inputs.task }}" and .status == "completed") | .conclusion'
}
while true; do
conclusion="$(get_conclusion)"
if [ -z "$conclusion" ]; then
echo "Cirrus CI task has not completed, yet. Waiting..."
sleep 30
else
if [ "$conclusion" == "success" ]; then
break
else
exit 1
fi
fi
done
- name: Download artifact from Cirrus CI
if: steps.find-task.outputs.task_found
id: download
shell: bash
run: |
get_external_id() {
gh api "${{ steps.check-suite.outputs.check_runs_url }}" \
| jq -er '.check_runs[] | select(.name == "${{ inputs.task }}") | .external_id'
}
archive="$(mktemp)"
artifacts="$(mktemp -d)"
until curl --no-progress-meter --fail -o "${archive}" \
"https://api.cirrus-ci.com/v1/artifact/task/$(get_external_id)/${{ inputs.download }}.zip"
do
# This happens when a tag is pushed on the same commit. In this case the
# job is immediately marked as "completed" for us, so we end up here after a few
# seconds - but the actual Cirrus CI task is still running and didn't produce its artifact, yet.
echo "Artifact not found on Cirrus CI, yet. Waiting..."
sleep 30
done
unzip "${archive}" -d "${artifacts}"
echo "artifacts=${artifacts}" >> "$GITHUB_OUTPUT"
- name: Save artifact to GitHub Actions
if: steps.find-task.outputs.task_found
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: ${{ inputs.upload }}
path: ${{ steps.download.outputs.artifacts }}
if-no-files-found: error
+25
View File
@@ -0,0 +1,25 @@
name: Setup Nix
description: Installs nix, sets up cachix and installs a subset of tooling.
inputs:
authToken:
description: Token to pass to cachix
tools:
description: Tools to install with nix-env -iA <tools>
runs:
using: composite
steps:
- uses: nixbuild/nix-quick-install-action@60e9c39264d4714139af3cdf15f691b19eec3530 # v28
with:
nix_conf: |-
always-allow-substitutes = true
max-jobs = auto
- uses: cachix/cachix-action@18cf96c7c98e048e10a83abd92116114cd8504be # v14
with:
name: postgrest
authToken: ${{ inputs.authToken }}
- if: ${{ inputs.tools }}
run: nix-env -f default.nix -iA ${{ inputs.tools }}
shell: bash
+18
View File
@@ -0,0 +1,18 @@
codecov:
branch: main
require_ci_to_pass: false
comment: false
coverage:
status:
project:
default:
target: auto
threshold: 1%
only_pulls: false
patch:
default:
target: auto
threshold: 1%
only_pulls: true
+11
View File
@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
- package-ecosystem: github-actions
directory: /.github/actions/setup-nix
schedule:
interval: weekly
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# Tag a release that will be built with Github Actions. The version is
# read from 'postgrest.cabal'.
version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
echo "Tagging version v$version"
git tag -f "v$version"
echo "Pushing tag..."
git push -f origin "refs/tags/v$version"
+69
View File
@@ -0,0 +1,69 @@
#!/bin/bash
set -Eeuo pipefail
# This script builds PostgREST in a remote ARM server
[ -z "$1" ] && { echo "Missing 1st argument: PostgREST github commit SHA"; exit 1; }
[ -z "$2" ] && { echo "Missing 2nd argument: Build environment directory name"; exit 1; }
[ -z "$3" ] && { echo "Missing 3rd argument: GHC version"; exit 1; }
PGRST_GITHUB_COMMIT="$1"
SCRIPT_DIR="$2"
DOCKER_BUILD_DIR="$SCRIPT_DIR/docker-env"
# latest is a shortcut documented on https://www.haskell.org/ghcup/guide/#tags-and-shortcuts
CABAL_VERSION="latest"
GHC_VERSION="$3"
install_packages() {
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git build-essential curl libffi-dev libffi7 libgmp-dev libgmp10 libncurses-dev libncurses5 libtinfo5 llvm libnuma-dev zlib1g-dev libpq-dev jq gcc
sudo apt-get clean
}
install_ghcup() {
export BOOTSTRAP_HASKELL_NONINTERACTIVE=1
export BOOTSTRAP_HASKELL_MINIMAL=1
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
source ~/.ghcup/env
}
install_cabal() {
ghcup upgrade
ghcup install cabal $CABAL_VERSION
ghcup set cabal $CABAL_VERSION
}
install_ghc() {
ghcup upgrade
ghcup install ghc $GHC_VERSION
ghcup set ghc $GHC_VERSION
}
install_packages
# Add ghcup to the PATH for this session
[ -f ~/.ghcup/env ] && source ~/.ghcup/env
ghcup --version || install_ghcup
ghcup set cabal $CABAL_VERSION || install_cabal
ghcup set ghc $GHC_VERSION || install_ghc
cd ~/$SCRIPT_DIR
# Clone the repository and build the project
git clone https://github.com/PostgREST/postgrest.git
cd postgrest
git checkout $PGRST_GITHUB_COMMIT
cabal v2-update && cabal v2-build
# Copy the built binary to the Dockerfile directory
PGRST_BIN=$(cabal exec which postgrest | tail -1)
cp $PGRST_BIN ~/$DOCKER_BUILD_DIR
# Move and compress the built binary
mkdir -p ~/$SCRIPT_DIR/result
mv $PGRST_BIN ~/$SCRIPT_DIR/result
cd ~/$SCRIPT_DIR
tar -cJf result.tar.xz result
+18
View File
@@ -0,0 +1,18 @@
# PostgREST docker hub image
FROM ubuntu:noble@sha256:3f85b7caad41a95462cf5b787d8a04604c8262cdcdf9a472b8c52ef83375fe15 AS postgrest
RUN apt-get update -y \
&& apt install -y --no-install-recommends libpq-dev zlib1g-dev jq gcc libnuma-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY postgrest /usr/bin/postgrest
EXPOSE 3000
USER 1000
# Use the array form to avoid running the command using bash, which does not handle `SIGTERM` properly.
# See https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop
CMD ["postgrest"]
+50
View File
@@ -0,0 +1,50 @@
#!/bin/bash
set -Eeuo pipefail
# This script publishes the Docker ARM images to Docker Hub.
[ -z "$1" ] && { echo "Missing 1st argument: PostgREST github commit SHA"; exit 1; }
[ -z "$2" ] && { echo "Missing 2nd argument: Docker repo"; exit 1; }
[ -z "$3" ] && { echo "Missing 3rd argument: Docker username"; exit 1; }
[ -z "$4" ] && { echo "Missing 4th argument: Docker password"; exit 1; }
[ -z "$5" ] && { echo "Missing 5th argument: Build environment directory name"; exit 1; }
[ -z "$6" ] && { echo "Missing 6th argument: PostgREST version"; exit 1; }
PGRST_GITHUB_COMMIT="$1"
DOCKER_REPO="$2"
DOCKER_USER="$3"
DOCKER_PASS="$4"
SCRIPT_DIR="$5"
PGRST_VERSION="$6"
DOCKER_BUILD_DIR="$SCRIPT_DIR/docker-env"
clean_env()
{
sudo docker logout
}
# Login to Docker
sudo docker logout
{ echo $DOCKER_PASS | sudo docker login -u $DOCKER_USER --password-stdin; } || { echo "Couldn't login to docker"; exit 1; }
trap clean_env sigint sigterm exit
# Move to the docker build environment
cd ~/$DOCKER_BUILD_DIR
# Push final images to Docker hub
# NOTE: This command publishes a separate ARM image because the builds cannot
# be added to the manifest if they are not in the registry beforehand.
# This image must be manually deleted from Docker Hub at the end of the process.
sudo docker buildx build --build-arg PGRST_GITHUB_COMMIT=$PGRST_GITHUB_COMMIT \
-t $DOCKER_REPO/postgrest:$PGRST_VERSION-arm \
--push .
# Add the arm images to the manifest
# NOTE: This assumes that there already is a `postgrest:<version>` image
# for the amd64 architecture pushed to Docker Hub
sudo docker buildx imagetools create --append -t $DOCKER_REPO/postgrest:$PGRST_VERSION $DOCKER_REPO/postgrest:$PGRST_VERSION-arm
[ "$PGRST_VERSION" != "devel" ] && sudo docker buildx imagetools create --append -t $DOCKER_REPO/postgrest:latest $DOCKER_REPO/postgrest:$PGRST_VERSION-arm
sudo docker logout
+78
View File
@@ -0,0 +1,78 @@
name: Cachix
# This workflow serves to
# - keep cachix up to date with the main branch
# - incrementally update cachix for large dependency
# updates, e.g. after running postgrest-nixpkgs-upgrade,
# which can cause the main CI workflow to time out
on:
workflow_dispatch:
push:
branches:
- main
- rel-*
tags:
- v*
jobs:
Seed-Cachix:
strategy:
fail-fast: false
matrix:
include:
- os: Linux
runs-on: ubuntu-22.04
- os: MacOS
runs-on: macos-12
name: Seed ${{ matrix.os }}
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Install cachix tooling
run: |
nix-env -f default.nix -iA devTools.pushCachix.bin
postgrest-push-cachix
- name: Seed dynamic postgrest build
run: |
nix-build -A postgrestPackage
postgrest-push-cachix
- name: Seed style tools
run: |
nix-build -A style
postgrest-push-cachix
- name: Seed test tools
run: |
nix-build -A tests
postgrest-push-cachix
- name: Seed static toolchain
if: matrix.os == 'Linux'
run: |
nix-build -A packagesStatic.haskellPackages.hello
postgrest-push-cachix
- name: Seed static postgresql build (for libpq)
if: matrix.os == 'Linux'
run: |
nix-build -A packagesStatic.pkgs.postgresql
postgrest-push-cachix
- name: Seed static postgrest build
if: matrix.os == 'Linux'
run: |
nix-build -A postgrestStatic
postgrest-push-cachix
- name: Build and push everything to Cachix
run: |
nix-build
postgrest-push-cachix
+554
View File
@@ -0,0 +1,554 @@
name: CI
on:
push:
branches:
- main
- v[0-9]+
tags:
- devel
- v*
pull_request:
branches:
- main
- v[0-9]+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
# Terminate all previous runs of the same workflow for pull requests
cancel-in-progress: "${{ github.event_name == 'pull_request' }}"
jobs:
Lint-Style:
name: Lint & check code style
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
tools: style
- name: Run linter (check locally with `nix-shell --run postgrest-lint`)
run: postgrest-lint
- name: Run style check (auto-format with `nix-shell --run postgrest-style`)
run: postgrest-style-check
Test-Nix:
name: Test (Nix)
runs-on: ubuntu-22.04
defaults:
run:
# Hack for enabling color output, see:
# https://github.com/actions/runner/issues/241#issuecomment-842566950
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
tools: tests
- name: Run coverage (IO tests and Spec tests against PostgreSQL 15)
run: postgrest-coverage
- name: Upload coverage to codecov
uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # v4.3.1
with:
files: ./coverage/codecov.json
token: ${{ secrets.CODECOV_TOKEN }}
- name: Run doctests
if: always()
run: nix-shell --run postgrest-test-doctests
- name: Check the spec tests for idempotence
if: always()
run: postgrest-test-spec-idempotence
Test-Pg-Nix:
strategy:
fail-fast: false
matrix:
pgVersion: [9.6, 10, 11, 12, 13, 14, 15, 16]
name: Test PG ${{ matrix.pgVersion }} (Nix)
runs-on: ubuntu-22.04
defaults:
run:
# Hack for enabling color output, see:
# https://github.com/actions/runner/issues/241#issuecomment-842566950
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
tools: tests withTools
- name: Run spec tests
if: always()
run: postgrest-with-postgresql-${{ matrix.pgVersion }} postgrest-test-spec
- name: Run IO tests
if: always()
run: postgrest-with-postgresql-${{ matrix.pgVersion }} -f test/io/fixtures.sql postgrest-test-io -vv
Test-Memory-Nix:
name: Test memory (Nix)
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
tools: memory
- name: Run memory tests
run: postgrest-test-memory
Build-Static-Nix:
name: Build Linux static (Nix)
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
tools: tests
- name: Build static executable
run: nix-build -A postgrestStatic
- name: Check static executable
run: postgrest-check-static result/bin/postgrest
- name: Save built executable as artifact
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: postgrest-linux-static-x64
path: result/bin/postgrest
if-no-files-found: error
- name: Build Docker image
run: nix-build -A docker.image --out-link postgrest-docker.tar.gz
- name: Save built Docker image as artifact
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: postgrest-docker-x64
path: postgrest-docker.tar.gz
if-no-files-found: error
Build-Macos-Nix:
name: Build MacOS (Nix)
runs-on: macos-12
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
- name: Build everything
run: |
nix-build
Build-Stack:
strategy:
fail-fast: false
matrix:
include:
- name: Linux
runs-on: ubuntu-22.04
cache: |
~/.stack
.stack-work
artifact: postgrest-ubuntu-x64
- name: MacOS
runs-on: macos-12
cache: |
~/.stack
.stack-work
artifact: postgrest-macos-x64
- name: Windows
runs-on: windows-2022
cache: |
~\AppData\Roaming\stack
~\AppData\Local\Programs\stack
.stack-work
deps: Add-Content $env:GITHUB_PATH $env:PGBIN
artifact: postgrest-windows-x64
name: Build ${{ matrix.name }} (Stack)
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Stack working files cache
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with:
path: ${{ matrix.cache }}
key: cache-stack-${{ runner.os }}-${{ hashFiles('stack.yaml.lock') }}
- name: Install dependencies
if: ${{ matrix.deps }}
run: ${{ matrix.deps }}
- name: Build with Stack
run: stack build --local-bin-path result --copy-bins
- name: Save built executable as artifact
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: ${{ matrix.artifact }}
path: |
result/postgrest
result/postgrest.exe
if-no-files-found: error
Get-FreeBSD-CirrusCI:
name: Get FreeBSD build from CirrusCI
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- uses: ./.github/actions/artifact-from-cirrus
with:
token: ${{ github.token }}
task: Build FreeBSD (Stack)
download: bin
upload: postgrest-freebsd-x64
Build-Cabal:
strategy:
matrix:
ghc: ['9.0.2', '9.2.4']
fail-fast: false
name: Build Linux (Cabal, GHC ${{ matrix.ghc }})
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Workaround runner image issue
# https://github.com/actions/runner-images/issues/7061
run: sudo chown -R "$USER" /usr/local/.ghcup
- name: ghcup
run: |
ghcup install ghc ${{ matrix.ghc }}
ghcup set ghc ${{ matrix.ghc }}
- name: Copy cabal.project & fix caching
run: |
mkdir ~/.cabal
cp cabal.project.non-nix cabal.project
- name: Cache
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with:
path: |
~/.cabal/packages
~/.cabal/store
dist-newstyle
key: cache-cabal-${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('**/*.cabal', '**/cabal.project') }}
restore-keys: |
cache-cabal-${{ runner.os }}-${{ matrix.ghc }}-
- name: Install dependencies
run: |
cabal update
cabal build --only-dependencies --enable-tests --enable-benchmarks
- name: Build
run: cabal build --enable-tests --enable-benchmarks all
Build-Cabal-Arm:
strategy:
matrix:
ghc: ['9.2.4']
fail-fast: false
name: Build aarch64 (Cabal, GHC ${{ matrix.ghc }})
if: "${{ github.event_name == 'push' }}"
runs-on: ubuntu-22.04
outputs:
remotepath: ${{ steps.Remote-Dir.outputs.remotepath }}
env:
GITHUB_COMMIT: ${{ github.sha }}
GHC_VERSION: ${{ matrix.ghc }}
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- id: Remote-Dir
name: Unique directory name for the remote build
run: echo "remotepath=postgrest-build-$(uuidgen)" >> "$GITHUB_OUTPUT"
- name: Copy script files to the remote server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.SSH_ARM_HOST }}
username: ubuntu
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
source: ".github/scripts/arm/*"
target: ${{ steps.Remote-Dir.outputs.remotepath }}
strip_components: 3
- name: Build ARM
uses: appleboy/ssh-action@master
env:
REMOTE_DIR: ${{ steps.Remote-Dir.outputs.remotepath }}
with:
host: ${{ secrets.SSH_ARM_HOST }}
username: ubuntu
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
command_timeout: 120m
script_stop: true
envs: GITHUB_COMMIT,REMOTE_DIR,GHC_VERSION
script: bash ~/$REMOTE_DIR/build.sh "$GITHUB_COMMIT" "$REMOTE_DIR" "$GHC_VERSION"
- name: Download binaries from remote server
uses: nicklasfrahm/scp-action@main
with:
direction: download
host: ${{ secrets.SSH_ARM_HOST }}
username: ubuntu
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
source: "${{ steps.Remote-Dir.outputs.remotepath }}/result.tar.xz"
target: "result.tar.xz"
- name: Extract downloaded binaries
run: tar -xvf result.tar.xz && rm result.tar.xz
- name: Save aarch64 executable as artifact
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: postgrest-ubuntu-aarch64
path: result/postgrest
if-no-files-found: error
Tag-Release:
name: Tag Release
if: startsWith(github.ref, 'refs/heads/')
permissions:
contents: write
runs-on: ubuntu-22.04
needs:
- Lint-Style
- Test-Nix
- Test-Pg-Nix
- Test-Memory-Nix
- Build-Static-Nix
- Build-Stack
- Get-FreeBSD-CirrusCI
- Build-Cabal-Arm
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
with:
ssh-key: ${{ secrets.POSTGREST_SSH_KEY }}
- name: Tag latest commit
run: |
cabal_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
if [[ "$cabal_version" == *.*.* ]]; then
git fetch --tags
if [ -z "$(git tag --list "v$cabal_version")" ]; then
git tag "v$cabal_version"
git push origin "v$cabal_version"
fi
else
git tag -f "devel"
git push -f origin "devel"
fi
Prepare-Release:
name: Prepare release
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-22.04
needs:
- Lint-Style
- Test-Nix
- Test-Pg-Nix
- Test-Memory-Nix
- Build-Static-Nix
- Build-Stack
- Get-FreeBSD-CirrusCI
- Build-Cabal-Arm
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Check the version to be released
run: |
cabal_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
if [ "${GITHUB_REF_NAME}" != "devel" ] && [ "${GITHUB_REF_NAME}" != "v$cabal_version" ]; then
echo "Tagged version ($GITHUB_REF_NAME) does not match the one in postgrest.cabal (v$cabal_version). Aborting release..."
exit 1
fi
- name: Identify changes from CHANGELOG.md
run: |
if [ "${GITHUB_REF_NAME}" == "devel" ]; then
echo "Getting unreleased changes..."
sed -n "1,/## Unreleased/d;/## \[/q;p" CHANGELOG.md > CHANGES.md
else
version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
echo "Propper release, getting changes for version $version ..."
sed -n "1,/## \[$version\]/d;/## \[/q;p" CHANGELOG.md > CHANGES.md
fi
echo "Relevant extract from CHANGELOG.md:"
cat CHANGES.md
- name: Save CHANGES.md as artifact
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: release-changes
path: CHANGES.md
if-no-files-found: error
Release-GitHub:
name: Release on GitHub
permissions:
contents: write
runs-on: ubuntu-22.04
needs: Prepare-Release
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Download all artifacts
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
path: artifacts
- name: Create release bundle with archives for all builds
run: |
find artifacts -type f -iname postgrest -exec chmod +x {} \;
mkdir -p release-bundle
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-linux-static-x64.tar.xz" \
-C artifacts/postgrest-linux-static-x64 postgrest
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-macos-x64.tar.xz" \
-C artifacts/postgrest-macos-x64 postgrest
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-freebsd-x64.tar.xz" \
-C artifacts/postgrest-freebsd-x64 postgrest
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-ubuntu-aarch64.tar.xz" \
-C artifacts/postgrest-ubuntu-aarch64 postgrest
zip "release-bundle/postgrest-${GITHUB_REF_NAME}-windows-x64.zip" \
artifacts/postgrest-windows-x64/postgrest.exe
- name: Save release bundle
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: release-bundle
path: release-bundle
if-no-files-found: error
- name: Publish release on GitHub
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Releasing version ${GITHUB_REF_NAME} on GitHub..."
if [ "${GITHUB_REF_NAME}" == "devel" ]; then
# To replace the existing release, we must first delete the old assets,
# then modify the release, then add the new assets.
gh release view devel --json assets \
| jq -r '.assets[] | .name' \
| xargs -rn1 \
gh release delete-asset -y devel
gh release edit devel \
-t devel \
--verify-tag \
-F artifacts/release-changes/CHANGES.md \
--prerelease
gh release upload --clobber devel release-bundle/*
else
gh release create "${GITHUB_REF_NAME}" \
-t "${GITHUB_REF_NAME}" \
--verify-tag \
-F artifacts/release-changes/CHANGES.md \
release-bundle/*
fi
Release-Docker:
name: Release on Docker Hub
runs-on: ubuntu-22.04
needs:
- Prepare-Release
env:
DOCKER_REPO: ${{ vars.DOCKER_REPO }}
DOCKER_USER: ${{ vars.DOCKER_USER }}
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Download Docker image
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: postgrest-docker-x64
- name: Publish images on Docker Hub
run: |
docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
docker load -i postgrest-docker.tar.gz
docker tag postgrest:latest "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}"
docker push "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}"
# Only tag 'latest' for full releases
if [ "${GITHUB_REF_NAME}" != "devel" ]; then
echo "Pushing to 'latest' tag for full release of ${GITHUB_REF_NAME} ..."
docker tag postgrest:latest "$DOCKER_REPO"/postgrest:latest
docker push "$DOCKER_REPO"/postgrest:latest
else
echo "Skipping push to 'latest' tag for pre-release..."
fi
# TODO: Enable dockerhub description update again, once a solution for the permission problem is found:
# https://github.com/docker/hub-feedback/issues/1927
# - name: Update descriptions on Docker Hub
# env:
# DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
# run: |
# if [[ -z "$ISPRERELEASE" ]]; then
# echo "Updating description on Docker Hub..."
# postgrest-release-dockerhub-description
# else
# echo "Skipping updating description for pre-release..."
# fi
Release-Docker-Arm:
name: Release Arm Builds on Docker Hub
runs-on: ubuntu-22.04
needs:
- Build-Cabal-Arm
- Release-Docker
env:
GITHUB_COMMIT: ${{ github.sha }}
DOCKER_REPO: ${{ vars.DOCKER_REPO }}
DOCKER_USER: ${{ vars.DOCKER_USER }}
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Publish images for ARM builds on Docker Hub
uses: appleboy/ssh-action@master
env:
REMOTE_DIR: ${{ needs.Build-Cabal-Arm.outputs.remotepath }}
with:
host: ${{ secrets.SSH_ARM_HOST }}
username: ubuntu
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
script_stop: true
envs: GITHUB_COMMIT,DOCKER_REPO,DOCKER_USER,DOCKER_PASS,REMOTE_DIR,GITHUB_REF_NAME
script: bash ~/$REMOTE_DIR/docker-publish.sh "$GITHUB_COMMIT" "$DOCKER_REPO" "$DOCKER_USER" "$DOCKER_PASS" "$REMOTE_DIR" "$GITHUB_REF_NAME"
Clean-Arm-Server:
name: Remove copied files from server
needs:
- Build-Cabal-Arm
- Release-Docker-Arm
if: success() ||
needs.Build-Cabal-Arm.result == 'failure' ||
needs.Build-Cabal-Arm.result == 'cancelled' ||
(needs.Build-Cabal-Arm.result == 'success' && !startsWith(github.ref, 'refs/tags/v'))
runs-on: ubuntu-22.04
env:
REMOTE_DIR: ${{ needs.Build-Cabal-Arm.outputs.remotepath }}
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- name: Remove uploaded files from server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_ARM_HOST }}
username: ubuntu
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
envs: REMOTE_DIR
script: rm -rf $REMOTE_DIR
+12 -12
View File
@@ -13,38 +13,38 @@ on:
jobs:
build:
name: Build docs
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26
- run: nix-env -f docs/default.nix -iA build
- run: postgrest-docs-build
spellcheck:
name: Run spellcheck
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26
- run: nix-env -f docs/default.nix -iA spellcheck
- run: postgrest-docs-spellcheck
dictcheck:
name: Run dictcheck
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26
- run: nix-env -f docs/default.nix -iA dictcheck
- run: postgrest-docs-dictcheck
linkcheck:
name: Run linkcheck
if: github.base_ref == 'main'
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26
- run: nix-env -f docs/default.nix -iA linkcheck
- run: postgrest-docs-linkcheck
+70
View File
@@ -0,0 +1,70 @@
name: Loadtest
on:
push:
branches:
- main
tags:
- v*
pull_request:
branches:
- main
jobs:
Loadtest-PR-Nix:
name: Loadtest PR (Nix)
if: ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-22.04
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
with:
fetch-depth: 0
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
tools: loadtest
- uses: WyriHaximus/github-action-get-previous-tag@04e8485ecb6487243907e330d522ff60f02283ce # v1.4.0
id: get-latest-tag
with:
prefix: v
- name: Run loadtest
run: |
postgrest-loadtest-against main ${{ steps.get-latest-tag.outputs.tag }}
postgrest-loadtest-report > loadtest/loadtest.md
- name: Upload report
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: loadtest.md
path: loadtest/loadtest.md
if-no-files-found: error
Loadtest-Merge-Nix:
name: Loadtest Merge (Nix)
if: ${{ github.event_name == 'push' }}
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
with:
fetch-depth: 0
- uses: WyriHaximus/github-action-get-previous-tag@04e8485ecb6487243907e330d522ff60f02283ce # v1.4.0
id: get-latest-tag
with:
prefix: v
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
tools: loadtest
- name: Run loadtest
run: |
postgrest-loadtest-against ${{ steps.get-latest-tag.outputs.tag }}
postgrest-loadtest-report > loadtest/loadtest.md
- name: Upload report
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: loadtest.md
path: loadtest/loadtest.md
if-no-files-found: error
+33
View File
@@ -0,0 +1,33 @@
name: Upload Reports
on:
workflow_run:
workflows: ["Loadtest"]
types:
- completed
jobs:
upload:
name: Loadtest
permissions:
checks: write
runs-on: ubuntu-22.04
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: Download from Artifacts
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
name: loadtest.md
path: artifacts
- name: Upload to GitHub Checks
uses: LouisBrunner/checks-action@6b626ffbad7cc56fd58627f774b9067e6118af23 # v2.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
sha: ${{ github.event.workflow_run.head_sha }}
name: Loadtest Results
conclusion: neutral
output: |
{"summary":""}
output_text_description_file: artifacts/loadtest.md
View File
+4 -6
View File
@@ -55,12 +55,6 @@ It builds the OpenAPI response using the schema cache.
This module provides functions to deal with JWT authorization.
### Workers.hs
This spawns threads which are used to execute concurrent jobs.
Jobs include connection recovery, a listener for the PostgreSQL LISTEN command, and an admin server.
### SchemaCache.hs
This queries the PostgreSQL system catalogs and caches the metadata into a SchemaCache type,
@@ -68,3 +62,7 @@ This queries the PostgreSQL system catalogs and caches the metadata into a Schem
### AppState.hs
The state of the App which is kept across requests.
This spawns threads which are used to execute concurrent jobs.
Jobs include connection recover and a listener for the PostgreSQL LISTEN command.
+37 -20
View File
@@ -4,40 +4,40 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
## Sponsors
<table>
<table align="center">
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest" target="_blank">
<img width="222px" src="static/cybertec-new.png">
<img width="296px" src="static/cybertec-new.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo" target="_blank">
<img width="296px" src="static/2ndquadrant.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/retool.png">
</a>
</td>
</tr>
<tr></tr>
<tr>
<td align="center" valign="middle">
<a href="https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/gnuhost.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
<img width="296px" src="static/supabase.png">
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/neon.jpg">
</a>
</td>
</tr>
<tr></tr>
<tr>
<td align="center" valign="middle">
<a href="https://code.build/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/code-build.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/oblivious.jpg">
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/supabase.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/tembo.png">
</a>
</td>
</tr>
@@ -46,12 +46,14 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
## Lead Backers
- [Roboflow](https://github.com/roboflow)
- Evans Fernandes
- [Jan Sommer](https://github.com/nerfpops)
- [Franz Gusenbauer](https://www.igutech.at/)
## Backers
- Zac Miller
- Tsingson Qin
- Michel Pelletier
- Jay Hannah
@@ -73,7 +75,22 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
<tr>
<td align="center" valign="middle">
<a href="https://www.timescale.com?utm_campaign=postgrest&utm_source=sponsor&utm_medium=referral&utm_content=github" target="_blank">
<img width="222px" src="static/timescaledb.png">
<img width="222px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/timescaledb.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img max-width="222px" height="88" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/retool.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo" target="_blank">
<img width="222px" src="static/2ndquadrant.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="222px" src="static/oblivious.jpg">
</a>
</td>
</tr>
+220
View File
@@ -3,6 +3,225 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased
## [12.0.3] - 2024-05-09
### Fixed
- #3149, Misleading "Starting PostgREST.." logs on schema cache reloading - @steve-chavez
- #3205, Fix wrong subquery error returning a status of 400 Bad Request - @steve-chavez
- #3224, Return status code 406 for non-accepted media type instead of code 415 - @wolfgangwalther
- #3160, Fix using select= query parameter for custom media type handlers - @wolfgangwalther
- #3361, Clarify PGRST204(column not found) error message - @steve-chavez
- #3373, Remove rejected mediatype `application/vnd.pgrst.object+json` from response - @taimoorzaeem
- #3418, Fix OpenAPI not tagging a FK column correctly on O2O relationships - @laurenceisla
- #3256, Fix wrong http status for pg error `42P17 infinite recursion` - @taimoorzaeem
## [12.0.2] - 2023-12-20
### Fixed
- #3124, Fix table's media type handlers not working for all schemas - @steve-chavez
- #3126, Fix empty row on media type handler function - @steve-chavez
## [12.0.1] - 2023-12-12
### Fixed
- #3054, Fix not allowing special characters in JSON keys - @laurenceisla
- #2344, Replace JSON parser error with a clearer generic message - @develop7
- #3100, Add missing in-database configuration option for `jwt-cache-max-lifetime` - @laurenceisla
- #3089, The any media type handler now sets `Content-Type: application/octet-stream` by default instead of `Content-Type: application/json` - @steve-chavez
## [12.0.0] - 2023-12-01
### Added
- #1614, Add `db-pool-automatic-recovery` configuration to disable connection retrying - @taimoorzaeem
- #2492, Allow full response control when raising exceptions - @taimoorzaeem, @laurenceisla
- #2771, #2983, #3062, #3055 Add `Server-Timing` response header - @taimoorzaeem, @develop7, @laurenceisla
- #2698, Add config `jwt-cache-max-lifetime` and implement JWT caching - @taimoorzaeem
- #2943, Add `handling=strict/lenient` for Prefer header - @taimoorzaeem
- #2441, Add config `server-cors-allowed-origins` to specify CORS origins - @taimoorzaeem
- #2825, SQL handlers for custom media types - @steve-chavez
+ Solves #1548, #2699, #2763, #2170, #1462, #1102, #1374, #2901
- #2799, Add timezone in Prefer header - @taimoorzaeem
- #3001, Add `statement_timeout` set on functions - @taimoorzaeem
- #3045, Apply superuser settings on impersonated roles if they have PostgreSQL 15 `GRANT SET ON PARAMETER` privilege - @steve-chavez
- #915, Add support for aggregate functions - @timabdulla
+ The aggregate functions SUM(), MAX(), MIN(), AVG(), and COUNT() are now supported.
+ It's disabled by default, you can enable it with `db-aggregates-enabled`.
- #3057, Log all internal database errors to stderr - @laurenceisla
### Fixed
- #3015, Fix unnecessary count() on RPC returning single - @steve-chavez
- #1070, Fix HTTP status responses for upserts - @taimoorzaeem
+ `PUT` returns `201` instead of `200` when rows are inserted
+ `POST` with `Prefer: resolution=merge-duplicates` returns `200` instead of `201` when no rows are inserted
- #3019, Transaction-Scoped Settings are now shown clearly in the Postgres logs - @laurenceisla
+ Shows `set_config('pgrst.setting_name', $1)` instead of `setconfig($1, $2)`
+ Does not apply to role settings and `app.settings.*`
- #2420, Fix bogus message when listening on port 0 - @develop7
- #3067, Fix Acquision Timeout errors logging to stderr when `log-level=crit` - @laurenceisla
### Changed
- Removed [raw-media-types config](https://postgrest.org/en/v11.1/references/configuration.html#raw-media-types) - @steve-chavez
- Removed `application/octet-stream`, `text/plain`, `text/xml` [builtin support for scalar results](https://postgrest.org/en/v11.1/references/api/resource_representation.html#scalar-function-response-format) - @steve-chavez
- Removed default `application/openapi+json` media type for [db-root-spec](https://postgrest.org/en/v11.1/references/configuration.html#db-root-spec) - @steve-chavez
- Removed [db-use-legacy-gucs](https://postgrest.org/en/v11.2/references/configuration.html#db-use-legacy-gucs) - @laurenceisla
## [11.2.2] - 2023-10-25
### Fixed
- #2824, Fix regression by reverting fix that returned 206 when first position = length in a `Range` header - @laurenceisla, @strengthless
## [11.2.1] - 2023-10-03
### Fixed
- #2899, Fix `application/vnd.pgrst.array` not accepted as a valid mediatype - @taimoorzaeem
- #2524, Fix schema cache and configuration reloading with `NOTIFY` not working on Windows - @diogob, @laurenceisla
- #2915, Fix duplicate headers in response - @taimoorzaeem
- #2824, Fix range request with first position same as length return status 206 - @taimoorzaeem
- #2939, Fix wrong `Preference-Applied` with `Prefer: tx=commit` when transaction is rollbacked - @steve-chavez
- #2939, Fix `count=exact` not being included in `Preference-Applied` - @steve-chavez
- #2800, Fix not including to-one embed resources that had a `NULL` value in any of the selected fields when doing null filtering on them - @laurenceisla
- #2846, Fix error when requesting `Prefer: count=<type>` and doing null filtering on embedded resources - @laurenceisla
- #2959, Fix setting `default_transaction_isolation` unnecessarily - @steve-chavez
- #2929, Fix arrow filtering on RPC returning dynamic TABLE with composite type - @steve-chavez
- #2963, Fix RPCs not embedding correctly when using overloaded functions for computed relationships - @laurenceisla
- #2970, Fix regression that rejects URI connection strings with certain unescaped characters in the password - @laurenceisla, @steve-chavez
## [11.2.0] - 2023-08-10
### Added
- #2523, Data representations - @aljungberg
+ Allows for flexible API output formatting and input parsing on a per-column type basis using regular SQL functions configured in the database
+ Enables greater flexibility in the form and shape of your APIs, both for output and input, making PostgREST a more versatile general-purpose API server
+ Examples include base64 encode/decode your binary data (like a `bytea` column containing an image), choose whether to present a timestamp column as seconds since the Unix epoch or as an ISO 8601 string, or represent fixed precision decimals as strings, not doubles, to preserve precision
+ ...and accept the same in `POST/PUT/PATCH` by configuring the reverse transformation(s)
+ Other use-cases include custom representation of enums, arrays, nested objects, CSS hex colour strings, gzip compressed fields, metric to imperial conversions, and much more
+ Works when using the `select` parameter to select only a subset of columns, embedding through complex joins, renaming fields, with views and computed columns
+ Works when filtering on a formatted column without extra indexes by parsing to the canonical representation
+ Works for data `RETURNING` operations, such as requesting the full body in a POST/PUT/PATCH with `Prefer: return=representation`
+ Works for batch updates and inserts
+ Completely optional, define the functions in the database and they will be used automatically everywhere
+ Data representations preserve the ability to write to the original column and require no extra storage or complex triggers (compared to using `GENERATED ALWAYS` columns)
+ Note: data representations require Postgres 10 (Postgres 11 if using `IN` predicates); data representations are not implemented for RPC
- #2647, Allow to verify the PostgREST version in SQL: `select distinct application_name from pg_stat_activity`. - @laurenceisla
- #2856, Add the `--version` CLI option that prints the version information - @laurenceisla
- #1655, Improve `details` field of the singular error response - @taimoorzaeem
- #740, Add `Preference-Applied` in response for `Prefer: return=representation/headers-only/minimal` - @taimoorzaeem
- #1601, Add optional `nulls=stripped` parameter for mediatypes `application/vnd.pgrst.array+json` and `application/vnd.pgrst.object+json` - @taimoorzaeem
### Fixed
- #2821, Fix OPTIONS not accepting all available media types - @steve-chavez
- #2834, Fix compilation on Ubuntu by being compatible with GHC 9.0.2 - @steve-chavez
- #2840, Fix `Prefer: missing=default` with DOMAIN default values - @steve-chavez
- #2849, Fix HEAD unnecessarily executing aggregates - @steve-chavez
- #2594, Fix unused index on jsonb/jsonb arrow filter and order (``/bets?data->>contractId=eq.1`` and ``/bets?order=data->>contractId``) - @steve-chavez
- #2861, Fix character and bit columns with fixed length not inserting/updating properly - @laurenceisla
+ Fixes the error "value too long for type character(1)" when the char length of the column was bigger than one.
- #2862, Fix null filtering on embedded resource when using a column name equal to the relation name - @steve-chavez
- #1586, Fix function parameters of type character and bit not ignoring length - @laurenceisla
+ Fixes the error "value too long for type character(1)" when the char length of the parameter was bigger than one.
- #2881, Fix error when a function returns `RECORD` or `SET OF RECORD` - @laurenceisla
- #2896, Fix applying superuser settings for impersonated role - @steve-chavez
### Deprecated
- #2863, Deprecate resource embedding target disambiguation - @steve-chavez
+ The `/table?select=*,other!fk(*)` must be used to disambiguate
+ The server aids in choosing the `!fk` by sending a `hint` on the error whenever an ambiguous request happens.
## [11.1.0] - 2023-06-07
### Added
- #2786, Limit idle postgresql connection lifetime - @robx
+ New option `db-pool-max-idletime` (default 30s).
+ This is equivalent to the old option `db-pool-timeout` of PostgREST 10.0.0.
+ A config alias for `db-pool-timeout` is included.
- #2703, Add pre-config function - @steve-chavez
+ New config option `db-pre-config`(empty by default)
+ Allows using the in-database configuration without SUPERUSER
- #2781, When `db-channel-enabled` is false, start automatic connection recovery on a new request when pool connections are closed with `pg_terminate_backend` - @steve-chavez
+ Mitigates the lack of LISTEN/NOTIFY for schema cache reloading on read replicas.
### Fixed
- #2791, Fix dropping schema cache reload notifications - @steve-chavez
- #2801, Stop retrying connection when "no password supplied" - @steve-chavez
## [11.0.1] - 2023-04-27
### Fixed
- #2762, Fixes "permission denied for schema" error during schema cache load - @steve-chavez
- #2756, Fix bad error message on generated columns when using `Prefer: missing=default` - @steve-chavez
- #1139, Allow a 30 second skew for JWT validation - @steve-chavez
+ It used to be 1 second, which was too strict
## [11.0.0] - 2023-04-16
### Added
- #1414, Add related orders - @steve-chavez
+ On a many-to-one or one-to-one relationship, you can order a parent by a child column `/projects?select=*,clients(*)&order=clients(name).desc.nullsfirst`
- #1233, #1907, #2566, Allow spreading embedded resources - @steve-chavez
+ On a many-to-one or one-to-one relationship, you can unnest a json object with `/projects?select=*,...clients(client_name:name)`
+ Allows including the join table columns when resource embedding
+ Allows disambiguating a recursive m2m embed
+ Allows disambiguating an embed that has a many-to-many relationship using two foreign keys on a junction
- #2340, Allow embedding without selecting any column - @steve-chavez
- #2563, Allow `is.null` or `not.is.null` on an embedded resource - @steve-chavez
+ Offers a more flexible replacement for `!inner`, e.g. `/projects?select=*,clients(*)&clients=not.is.null`
+ Allows doing an anti join, e.g. `/projects?select=*,clients(*)&clients=is.null`
+ Allows using or across related tables conditions
- #1100, Customizable OpenAPI title - @AnthonyFisi
- #2506, Add `server-trace-header` for tracing HTTP requests. - @steve-chavez
+ When the client sends the request header specified in the config it will be included in the response headers.
- #2694, Make `db-root-spec` stable. - @steve-chavez
+ This can be used to override the OpenAPI spec with a custom database function
- #1567, On bulk inserts, missing values can get the column DEFAULT by using the `Prefer: missing=default` header - @steve-chavez
- #2501, Allow filtering by`IS DISTINCT FROM` using the `isdistinct` operator, e.g. `/people?alias=isdistinct.foo`
- #1569, Allow `any/all` modifiers on the `eq,like,ilike,gt,gte,lt,lte,match,imatch` operators, e.g. `/tbl?id=eq(any).{1,2,3}` - @steve-chavez
- This converts the input into an array type
- #2561, Configurable role settings - @steve-chavez
- Database roles that are members of the connection role get their settings applied, e.g. doing
`ALTER ROLE anon SET statement_timeout TO '5s'` will result in that `statement_timeout` getting applied for that role.
- Works when switching roles when a JWT is sent
- Settings can be reloaded with `NOTIFY pgrst, 'reload config'`.
- #2468, Configurable transaction isolation level with `default_transaction_isolation` - @steve-chavez
- Can be set per function `create function .. set default_transaction_isolation = 'repeatable read'`
- Or per role `alter role .. set default_transaction_isolation = 'serializable'`
### Fixed
- #2651, Add the missing `get` path item for RPCs to the OpenAPI output - @laurenceisla
- #2648, Fix inaccurate error codes with new ones - @laurenceisla
+ `PGRST204`: Column is not found
+ `PGRST003`: Timed out when acquiring connection to db
- #1652, Fix function call with arguments not inlining - @steve-chavez
- #2705, Fix bug when using the `Range` header on `PATCH/DELETE` - @laurenceisla
+ Fix the`"message": "syntax error at or near \"RETURNING\""` error
+ Fix doing a limited update/delete when an `order` query parameter was present
- #2742, Fix db settings and pg version queries not getting prepared - @steve-chavez
- #2618, Fix `PATCH` requests not recognizing embedded filters and using the top-level resource instead - @steve-chavez
### Changed
- #2705, The `Range` header is now only considered on `GET` requests and is ignored for any other method - @laurenceisla
+ Other methods should use the `limit/offset` query parameters for sub-ranges
+ `PUT` requests no longer return an error when this header is present (using `limit/offset` still triggers the error)
- #2733, Remove bulk RPC call with the `Prefer: params=multiple-objects` header. A function with a JSON array or object parameter should be used instead.
## [10.2.0] - 2023-04-12
### Added
@@ -36,6 +255,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- #2548, Fix regression when embedding views with partial references to multi column FKs - @wolfgangwalther
- #2558, Fix regression when requesting limit=0 and `db-max-row` is set - @laurenceisla
- #2542, Return a clear error without hitting the database when trying to update or insert an unknown column with `?columns` - @aljungberg
## [10.1.0] - 2022-10-28
+26 -20
View File
@@ -1,6 +1,8 @@
![Logo](static/bigger-logo.png "Logo")
[![Donate](https://img.shields.io/badge/Donate-Patreon-orange.svg?colorB=F96854)](https://www.patreon.com/postgrest)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/postgrest)
[![Join the chat at https://gitter.im/begriffs/postgrest](https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/begriffs/postgrest)
[![Docs](https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat)](http://postgrest.org)
[![Docker Stars](https://img.shields.io/docker/pulls/postgrest/postgrest.svg)](https://hub.docker.com/r/postgrest/postgrest/)
[![Build Status](https://github.com/postgrest/postgrest/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/PostgREST/postgrest/actions?query=branch%3Amain)
@@ -13,40 +15,40 @@ API than you are likely to write from scratch.
## Sponsors
<table>
<table align="center">
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest" target="_blank">
<img width="222px" src="static/cybertec-new.png">
<img width="296px" src="static/cybertec-new.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo" target="_blank">
<img width="296px" src="static/2ndquadrant.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/retool.png">
</a>
</td>
</tr>
<tr></tr>
<tr>
<td align="center" valign="middle">
<a href="https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/gnuhost.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
<img width="296px" src="static/supabase.png">
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/neon.jpg">
</a>
</td>
</tr>
<tr></tr>
<tr>
<td align="center" valign="middle">
<a href="https://code.build/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/code-build.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/oblivious.jpg">
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/supabase.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/tembo.png">
</a>
</td>
</tr>
@@ -143,7 +145,11 @@ and the [API guide](http://postgrest.org/en/stable/api.html).
## Supporting development
You can help PostgREST ongoing maintenance and development by making a regular donation through Patreon https://www.patreon.com/postgrest
You can help PostgREST ongoing maintenance and development by:
- Making a regular donation through Patreon https://www.patreon.com/postgrest
- Alternatively, you can make a one-time donation via Paypal https://www.paypal.me/postgrest
Every donation will be spent on making PostgREST better for the whole community.
+1
View File
@@ -0,0 +1 @@
index-state: hackage.haskell.org 2023-10-13T13:54:33Z
+31 -11
View File
@@ -41,6 +41,7 @@ let
allOverlays.postgresql-legacy
allOverlays.postgresql-future
(allOverlays.haskell-packages { inherit compiler; })
allOverlays.slocat
];
# Evaluated expression of the Nixpkgs repository.
@@ -49,6 +50,19 @@ let
postgresqlVersions =
[
{
name = "postgresql-16";
postgresql = pkgs.postgresql_16.withPackages (p: [
p.postgis
(p.pg_safeupdate.overrideAttrs (old: {
installPhase = ''
mkdir -p $out/bin
cp safeupdate.dylib safeupdate.so || true
install -D safeupdate.so -t $out/lib
'';
}))
]);
}
{ name = "postgresql-15"; postgresql = pkgs.postgresql_15.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
{ name = "postgresql-14"; postgresql = pkgs.postgresql_14.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
{ name = "postgresql-13"; postgresql = pkgs.postgresql_13.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
@@ -65,11 +79,17 @@ let
postgrest =
pkgs.haskell.packages."${compiler}".callCabal2nix name src { };
# Function that derives a fully static Haskell package based on
# Functionality that derives a fully static Haskell package based on
# nh2/static-haskell-nix
staticHaskellPackage =
import nix/static-haskell-package.nix { inherit nixpkgs system compiler patches allOverlays; };
# Static executable.
postgrestStatic =
lib.justStaticExecutables (lib.dontCheck (staticHaskellPackage name src).package);
packagesStatic = (staticHaskellPackage name src).survey;
# Options passed to cabal in dev tools and tests
devCabalOptions =
"-f dev --test-show-detail=direct";
@@ -94,10 +114,6 @@ rec {
postgrestPackage =
lib.dontCheck postgrest;
# Static executable.
postgrestStatic =
lib.justStaticExecutables (lib.dontCheck (staticHaskellPackage name src));
# Profiled dynamic executable.
postgrestProfiled =
lib.enableExecutableProfiling (
@@ -119,14 +135,13 @@ rec {
cabalTools =
pkgs.callPackage nix/tools/cabalTools.nix { inherit devCabalOptions postgrest; };
withTools =
pkgs.callPackage nix/tools/withTools.nix { inherit cabalTools devCabalOptions postgresqlVersions postgrest; };
# Development tools.
devTools =
pkgs.callPackage nix/tools/devTools.nix { inherit tests style devCabalOptions hsie withTools; };
# Docker images and loading script.
docker =
pkgs.callPackage nix/tools/docker { postgrest = postgrestStatic; };
# Load testing tools.
loadtest =
pkgs.callPackage nix/tools/loadtest.nix { inherit withTools; };
@@ -155,7 +170,12 @@ rec {
inherit (pkgs.haskell.packages."${compiler}") hpc-codecov;
inherit (pkgs.haskell.packages."${compiler}") weeder;
};
} // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux rec {
# Static executable.
inherit postgrestStatic;
inherit packagesStatic;
withTools =
pkgs.callPackage nix/tools/withTools.nix { inherit devCabalOptions postgresqlVersions postgrest; };
# Docker images and loading script.
docker =
pkgs.callPackage nix/tools/docker { postgrest = postgrestStatic; };
}
+1 -1
View File
@@ -5,4 +5,4 @@ Pipfile.lock
_diagrams/db.pdf
misspellings
unuseddict
*.mo
.history
+2
View File
@@ -8,6 +8,8 @@ You can go download erd from https://github.com/BurntSushi/erd/releases and then
./erd_static-x86-64 -i film.er -o ../_static/film.png
```
The fonts used belong to the GNU FreeFont family. You can download them here: http://ftp.gnu.org/gnu/freefont/
## LaTeX
The schema structure diagram is done with LaTeX. You can use a GUI like https://www.mathcha.io/editor to create the .tex file.
+15
View File
@@ -0,0 +1,15 @@
entity {font: "FreeSans"}
relationship {font: "FreeMono"}
[Box_Office]
*bo_date
*+film_id
gross_revenue
[Films]
*id
+director_id
title
`...`
Box_Office +--1 Films
+12
View File
@@ -0,0 +1,12 @@
# Build using: -e ortho
entity {font: "FreeSans"}
relationship {font: "FreeMono"}
[Employees]
*id
first_name
last_name
+supervisor_id
Employees 1--* Employees
+11
View File
@@ -1,3 +1,6 @@
entity {font: "FreeSans"}
relationship {font: "FreeSerif"}
[Films]
*id
+director_id
@@ -31,6 +34,12 @@ year
*+film_id
rank
[Technical_Specs]
*+film_id
runtime
camera
sound
Roles *--1 Actors
Roles *--1 Films
@@ -38,3 +47,5 @@ Nominations *--1 Competitions
Nominations *--1 Films
Films *--1 Directors
Films 1--1 Technical_Specs
+5
View File
@@ -1,3 +1,8 @@
# Build using: -e ortho
entity {font: "FreeSans"}
relationship {font: "FreeMono"}
[Addresses]
*id
name
+16
View File
@@ -0,0 +1,16 @@
entity {font: "FreeSans"}
relationship {font: "FreeMono"}
[Premieres]
*id
location
date
+film_id
[Films]
*id
+director_id
title
`...`
Premieres *--1 Films
+12
View File
@@ -0,0 +1,12 @@
# Build using: -e ortho
entity {font: "FreeSans"}
relationship {font: "FreeMono"}
[Presidents]
*id
first_name
last_name
+predecessor_id
Presidents 1--? Presidents
+18
View File
@@ -0,0 +1,18 @@
# Build using: -e ortho
entity {font: "FreeSans"}
relationship {font: "FreeMono"}
[Users]
*id
first_name
last_name
username
[Subscriptions]
*+subscriber_id
*+subscribed_id
type
Users 1--* Subscriptions
Subscriptions *--1 Users
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

+28
View File
@@ -65,3 +65,31 @@ div.line-block {
.wy-table-responsive {
overflow: visible !important;
}
#tutorials span.caption-text {
display: none;
}
#references span.caption-text {
display: none;
}
#explanations span.caption-text {
display: none;
}
#how-tos span.caption-text {
display: none;
}
#ecosystem span.caption-text {
display: none;
}
#integrations span.caption-text {
display: none;
}
#api span.caption-text {
display: none;
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 16 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

-385
View File
@@ -1,385 +0,0 @@
.. _admin:
Hardening PostgREST
===================
PostgREST is a fast way to construct a RESTful API. Its default behavior is great for scaffolding in development. When it's time to go to production it works great too, as long as you take precautions. PostgREST is a small sharp tool that focuses on performing the API-to-database mapping. We rely on a reverse proxy like Nginx for additional safeguards.
The first step is to create an Nginx configuration file that proxies requests to an underlying PostgREST server.
.. code-block:: nginx
http {
# ...
# upstream configuration
upstream postgrest {
server localhost:3000;
}
# ...
server {
# ...
# expose to the outside world
location /api/ {
default_type application/json;
proxy_hide_header Content-Location;
add_header Content-Location /api/$upstream_http_content_location;
proxy_set_header Connection "";
proxy_http_version 1.1;
proxy_pass http://postgrest/;
}
# ...
}
}
.. note::
For ubuntu, if you already installed nginx through :code:`apt` you can add this to the config file in
:code:`/etc/nginx/sites-enabled/default`.
.. _block_fulltable:
Block Full-Table Operations
---------------------------
Each table in the admin-selected schema gets exposed as a top level route. Client requests are executed by certain database roles depending on their authentication. All HTTP verbs are supported that correspond to actions permitted to the role. For instance if the active role can drop rows of the table then the DELETE verb is allowed for clients. Here's an API request to delete old rows from a hypothetical logs table:
.. tabs::
.. code-tab:: http
DELETE /logs?time=lt.1991-08-06 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/logs?time=lt.1991-08-06" -X DELETE
However it's very easy to delete the **entire table** by omitting the query parameter!
.. tabs::
.. code-tab:: http
DELETE /logs HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/logs" -X DELETE
This can happen accidentally such as by switching a request from a GET to a DELETE. To protect against accidental operations use the `pg-safeupdate <https://github.com/eradman/pg-safeupdate>`_ PostgreSQL extension. It raises an error if UPDATE or DELETE are executed without specifying conditions. To install it you can use the `PGXN <https://pgxn.org/>`_ network:
.. code-block:: bash
sudo -E pgxn install safeupdate
# then add this to postgresql.conf:
# shared_preload_libraries='safeupdate';
This does not protect against malicious actions, since someone can add a url parameter that does not affect the result set. To prevent this you must turn to database permissions, forbidding the wrong people from deleting rows, and using `row-level security <https://www.postgresql.org/docs/current/ddl-rowsecurity.html>`_ if finer access control is required.
Count-Header DoS
----------------
For convenience to client-side pagination controls PostgREST supports counting and reporting total table size in its response. As described in :ref:`limits`, responses ordinarily include a range but leave the total unspecified like
.. code-block:: http
HTTP/1.1 200 OK
Range-Unit: items
Content-Range: 0-14/*
However including the request header :code:`Prefer: count=exact` calculates and includes the full count:
.. code-block:: http
HTTP/1.1 206 Partial Content
Range-Unit: items
Content-Range: 0-14/3573458
This is fine in small tables, but count performance degrades in big tables due to the MVCC architecture of PostgreSQL. For very large tables it can take a very long time to retrieve the results which allows a denial of service attack. The solution is to strip this header from all requests:
.. code-block:: postgres
-- Pending nginx config: Remove any prefer header which contains the word count
.. _https:
HTTPS
-----
PostgREST aims to do one thing well: add an HTTP interface to a PostgreSQL database. To keep the code small and focused we do not implement HTTPS. Use a reverse proxy such as NGINX to add this, `here's how <https://nginx.org/en/docs/http/configuring_https_servers.html>`_. Note that some Platforms as a Service like Heroku also add SSL automatically in their load balancer.
Rate Limiting
-------------
Nginx supports "leaky bucket" rate limiting (see `official docs <https://nginx.org/en/docs/http/ngx_http_limit_req_module.html>`_). Using standard Nginx configuration, routes can be grouped into *request zones* for rate limiting. For instance we can define a zone for login attempts:
.. code-block:: nginx
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
This creates a shared memory zone called "login" to store a log of IP addresses that access the rate limited urls. The space reserved, 10 MB (:code:`10m`) will give us enough space to store a history of 160k requests. We have chosen to allow only allow one request per second (:code:`1r/s`).
Next we apply the zone to certain routes, like a hypothetical stored procedure called :code:`login`.
.. code-block:: nginx
location /rpc/login/ {
# apply rate limiting
limit_req zone=login burst=5;
}
The burst argument tells Nginx to start dropping requests if more than five queue up from a specific IP.
Nginx rate limiting is general and indiscriminate. To rate limit each authenticated request individually you will need to add logic in a :ref:`Custom Validation <custom_validation>` function.
.. _external_connection_poolers:
Using External Connection Poolers
---------------------------------
PostgREST manages its :ref:`own pool of connections <db-pool>` and uses prepared statements by default in order to increase performance. However, this setting is incompatible with external connection poolers such as PgBouncer working in transaction pooling mode. In this case, you need to set the :ref:`db-prepared-statements` config option to ``false``. On the other hand, session pooling is fully compatible with PostgREST, while statement pooling is not compatible at all.
.. note::
If prepared statements are enabled, PostgREST will quit after detecting that transaction or statement pooling is being used.
You should also set the :ref:`db-channel-enabled` config option to ``false``, due to the ``LISTEN`` command not being compatible with transaction pooling, although it should not give any errors if it's left enabled by default.
Debugging
=========
Server Version
--------------
When debugging a problem it's important to verify the PostgREST version. At any time you can make a request to the running server and determine exactly which version is deployed. Look for the :code:`Server` HTTP response header, which contains the version number.
Errors
------
See the :doc:`Errors <errors>` reference page for detailed information on the errors that PostgREST returns.
.. _pgrst_logging:
Logging
-------
PostgREST logs basic request information to ``stdout``, including the authenticated user if available, the requesting IP address and user agent, the URL requested, and HTTP response status.
.. code::
127.0.0.1 - user [26/Jul/2021:01:56:38 -0500] "GET /clients HTTP/1.1" 200 - "" "curl/7.64.0"
127.0.0.1 - anonymous [26/Jul/2021:01:56:48 -0500] "GET /unexistent HTTP/1.1" 404 - "" "curl/7.64.0"
For diagnostic information about the server itself, PostgREST logs to ``stderr``.
.. code::
12/Jun/2021:17:47:39 -0500: Attempting to connect to the database...
12/Jun/2021:17:47:39 -0500: Listening on port 3000
12/Jun/2021:17:47:39 -0500: Connection successful
12/Jun/2021:17:47:39 -0500: Config re-loaded
12/Jun/2021:17:47:40 -0500: Schema cache loaded
.. note::
When running it in an SSH session you must detach it from stdout or it will be terminated when the session closes. The easiest technique is redirecting the output to a log file or to the syslog:
.. code-block:: bash
ssh foo@example.com \
'postgrest foo.conf </dev/null >/var/log/postgrest.log 2>&1 &'
# another option is to pipe the output into "logger -t postgrest"
PostgREST logging provides limited information for debugging server errors. It's helpful to get full information about both client requests and the corresponding SQL commands executed against the underlying database.
HTTP Requests
-------------
A great way to inspect incoming HTTP requests including headers and query parameters is to sniff the network traffic on the port where PostgREST is running. For instance on a development server bound to port 3000 on localhost, run this:
.. code:: bash
# sudo access is necessary for watching the network
sudo ngrep -d lo0 port 3000
The options to ngrep vary depending on the address and host on which you've bound the server. The binding is described in the :ref:`configuration` section. The ngrep output isn't particularly pretty, but it's legible.
.. _automatic_recovery:
Automatic Connection Recovery
-----------------------------
When PostgREST loses the connection to the database, it retries the connection using capped exponential backoff, with 32 seconds being the maximum backoff time.
This retry behavior is triggered immediately after the connection is lost if :ref:`db-channel-enabled` is set to true(the default), otherwise it will be activated once a request is made.
To notify the client when the next reconnection attempt will be, PostgREST responds with ``503 Service Unavailable`` and the ``Retry-After: x`` header, where ``x`` is the number of seconds programmed for the next retry.
Database Logs
-------------
Once you've verified that requests are as you expect, you can get more information about the server operations by watching the database logs. By default PostgreSQL does not keep these logs, so you'll need to make the configuration changes below. Find :code:`postgresql.conf` inside your PostgreSQL data directory (to find that, issue the command :code:`show data_directory;`). Either find the settings scattered throughout the file and change them to the following values, or append this block of code to the end of the configuration file.
.. code:: sql
# send logs where the collector can access them
log_destination = "stderr"
# collect stderr output to log files
logging_collector = on
# save logs in pg_log/ under the pg data directory
log_directory = "pg_log"
# (optional) new log file per day
log_filename = "postgresql-%Y-%m-%d.log"
# log every kind of SQL statement
log_statement = "all"
Restart the database and watch the log file in real-time to understand how HTTP requests are being translated into SQL commands.
.. note::
On Docker you can enable the logs by using a custom ``init.sh``:
.. code:: bash
#!/bin/sh
echo "log_statement = 'all'" >> /var/lib/postgresql/data/postgresql.conf
After that you can start the container and check the logs with ``docker logs``.
.. code:: bash
docker run -v "$(pwd)/init.sh":"/docker-entrypoint-initdb.d/init.sh" -d postgres
docker logs -f <container-id>
Schema Reloading
----------------
Changing the schema while the server is running can lead to errors due to a stale schema cache. To learn how to refresh the cache see :ref:`schema_reloading`.
.. _health_check:
Health Check
------------
You can enable a minimal health check to verify if PostgREST is available for client requests and to check the status of its internal state.
To do this, set the configuration variable :ref:`admin-server-port` to the port number of your preference. Two endpoints ``live`` and ``ready`` will then be available.
The ``live`` endpoint verifies if PostgREST is running on its configured port. A request will return ``200 OK`` if PostgREST is alive or ``503`` otherwise.
The ``ready`` endpoint also checks the state of both the Database Connection and the :ref:`schema_cache`. A request will return ``200 OK`` if it is ready or ``503`` if not.
For instance, to verify if PostgREST is running at ``localhost:3000`` while the ``admin-server-port`` is set to ``3001``:
.. tabs::
.. code-tab:: http
GET localhost:3001/live HTTP/1.1
.. code-tab:: bash Curl
curl -I "http://localhost:3001/live"
.. code-block:: http
HTTP/1.1 200 OK
If you have a machine with multiple network interfaces and multiple PostgREST instances in the same port, you need to specify a unique :ref:`hostname <server-host>` in the configuration of each PostgREST instance for the health check to work correctly. Don't use the special values(``!4``, ``*``, etc) in this case because the health check could report a false positive.
Daemonizing
===========
For Linux distributions that use **systemd** (Ubuntu, Debian, Archlinux) you can create a daemon in the following way.
First, create postgrest configuration in ``/etc/postgrest/config``
.. code-block:: ini
db-uri = "postgres://<your_user>:<your_password>@localhost:5432/<your_db>"
db-schemas = "<your_exposed_schema>"
db-anon-role = "<your_anon_role>"
jwt-secret = "<your_secret>"
Then create the systemd service file in ``/etc/systemd/system/postgrest.service``
.. code-block:: ini
[Unit]
Description=REST API for any PostgreSQL database
After=postgresql.service
[Service]
ExecStart=/bin/postgrest /etc/postgrest/config
ExecReload=/bin/kill -SIGUSR1 $MAINPID
[Install]
WantedBy=multi-user.target
After that, you can enable the service at boot time and start it with:
.. code-block:: bash
systemctl enable postgrest
systemctl start postgrest
## For reloading the service
## systemctl restart postgrest
.. _file_descriptors:
File Descriptors
----------------
File descriptors are kernel resources that are used by HTTP connections (among others). File descriptors are limited per process. The kernel default limit is 1024, which is increased in some Linux distributions.
When under heavy traffic, PostgREST can reach this limit and start showing ``No file descriptors available`` errors. To clear these errors, you can increase the process' file descriptor limit.
.. code-block:: ini
[Service]
LimitNOFILE=10000
Alternate URL Structure
=======================
As discussed in :ref:`singular_plural`, there are no special URL forms for singular resources in PostgREST, only operators for filtering. Thus there are no URLs like :code:`/people/1`. It would be specified instead as
.. tabs::
.. code-tab:: http
GET /people?id=eq.1 HTTP/1.1
Accept: application/vnd.pgrst.object+json
.. code-tab:: bash Curl
curl "http://localhost:3000/people?id=eq.1" \
-H "Accept: application/vnd.pgrst.object+json"
This allows compound primary keys and makes the intent for singular response independent of a URL convention.
Nginx rewrite rules allow you to simulate the familiar URL convention. The following example adds a rewrite rule for all table endpoints, but you'll want to restrict it to those tables that have a numeric simple primary key named "id."
.. code-block:: nginx
# support /endpoint/:id url style
location ~ ^/([a-z_]+)/([0-9]+) {
# make the response singular
proxy_set_header Accept 'application/vnd.pgrst.object+json';
# assuming an upstream named "postgrest"
proxy_pass http://postgrest/$1?id=eq.$2;
}
.. TODO
.. Administration
.. API Versioning
.. HTTP Caching
.. Upgrading
-3113
View File
File diff suppressed because it is too large Load Diff
-496
View File
@@ -1,496 +0,0 @@
.. _roles:
Overview of Role System
=======================
PostgREST is designed to keep the database at the center of API security. All authorization happens through database roles and permissions. It is PostgREST's job to **authenticate** requests -- i.e. verify that a client is who they say they are -- and then let the database **authorize** client actions.
Authentication Sequence
-----------------------
There are three types of roles used by PostgREST, the **authenticator**, **anonymous** and **user** roles. The database administrator creates these roles and configures PostgREST to use them.
.. image:: _static/security-roles.png
The authenticator should be created :code:`NOINHERIT` and configured in the database to have very limited access. It is a chameleon whose job is to "become" other users to service authenticated HTTP requests. The picture below shows how the server handles authentication. If auth succeeds, it switches into the user role specified by the request, otherwise it switches into the anonymous role (if it's set in :ref:`db-anon-role`).
.. image:: _static/security-anon-choice.png
Here are the technical details. We use `JSON Web Tokens <https://jwt.io/>`_ to authenticate API requests. As you'll recall a JWT contains a list of cryptographically signed claims. All claims are allowed but PostgREST cares specifically about a claim called role.
.. code:: json
{
"role": "user123"
}
When a request contains a valid JWT with a role claim PostgREST will switch to the database role with that name for the duration of the HTTP request.
.. code:: sql
SET LOCAL ROLE user123;
Note that the database administrator must allow the authenticator role to switch into this user by previously executing
.. code:: sql
GRANT user123 TO authenticator;
If the client included no JWT (or one without a role claim) then PostgREST switches into the anonymous role whose actual database-specific name, like that of with the authenticator role, is specified in the PostgREST server configuration file. The database administrator must set anonymous role permissions correctly to prevent anonymous users from seeing or changing things they shouldn't.
Users and Groups
----------------
PostgreSQL manages database access permissions using the concept of roles. A role can be thought of as either a database user, or a group of database users, depending on how the role is set up.
Roles for Each Web User
~~~~~~~~~~~~~~~~~~~~~~~
PostgREST can accommodate either viewpoint. If you treat a role as a single user then the JWT-based role switching described above does most of what you need. When an authenticated user makes a request PostgREST will switch into the role for that user, which in addition to restricting queries, is available to SQL through the :code:`current_user` variable.
You can use row-level security to flexibly restrict visibility and access for the current user. Here is an `example <https://www.2ndquadrant.com/en/blog/application-users-vs-row-level-security/>`_ from Tomas Vondra, a chat table storing messages sent between users. Users can insert rows into it to send messages to other users, and query it to see messages sent to them by other users.
.. code-block:: postgres
CREATE TABLE chat (
message_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
message_time TIMESTAMP NOT NULL DEFAULT now(),
message_from NAME NOT NULL DEFAULT current_user,
message_to NAME NOT NULL,
message_subject VARCHAR(64) NOT NULL,
message_body TEXT
);
ALTER TABLE chat ENABLE ROW LEVEL SECURITY;
We want to enforce a policy that ensures a user can see only those messages sent by them or intended for them. Also we want to prevent a user from forging the message_from column with another person's name.
PostgreSQL allows us to set this policy with row-level security:
.. code-block:: postgres
CREATE POLICY chat_policy ON chat
USING ((message_to = current_user) OR (message_from = current_user))
WITH CHECK (message_from = current_user)
Anyone accessing the generated API endpoint for the chat table will see exactly the rows they should, without our needing custom imperative server-side coding.
.. warning::
Roles are namespaced per-cluster rather than per-database so they may be prone to collision.
Web Users Sharing Role
~~~~~~~~~~~~~~~~~~~~~~
Alternately database roles can represent groups instead of (or in addition to) individual users. You may choose that all signed-in users for a web app share the role webuser. You can distinguish individual users by including extra claims in the JWT such as email.
.. code:: json
{
"role": "webuser",
"email": "john@doe.com"
}
SQL code can access claims through GUC variables set by PostgREST per request. For instance to get the email claim, call this function:
For PostgreSQL server version >= 14
.. code:: sql
current_setting('request.jwt.claims', true)::json->>'email';
For PostgreSQL server version < 14
.. code:: sql
current_setting('request.jwt.claim.email', true);
This allows JWT generation services to include extra information and your database code to react to it. For instance the RLS example could be modified to use this current_setting rather than current_user. The second 'true' argument tells current_setting to return NULL if the setting is missing from the current configuration.
Hybrid User-Group Roles
~~~~~~~~~~~~~~~~~~~~~~~
You can mix the group and individual role policies. For instance we could still have a webuser role and individual users which inherit from it:
.. code-block:: postgres
CREATE ROLE webuser NOLOGIN;
-- grant this role access to certain tables etc
CREATE ROLE user000 NOLOGIN;
GRANT webuser TO user000;
-- now user000 can do whatever webuser can
GRANT user000 TO authenticator;
-- allow authenticator to switch into user000 role
-- (the role itself has nologin)
.. _custom_validation:
Custom Validation
-----------------
PostgREST honors the :code:`exp` claim for token expiration, rejecting expired tokens. However it does not enforce any extra constraints. An example of an extra constraint would be to immediately revoke access for a certain user. The configuration file parameter :code:`db-pre-request` specifies a stored procedure to call immediately after the authenticator switches into a new role and before the main query itself runs.
Here's an example. In the config file specify a stored procedure:
.. code:: ini
db-pre-request = "public.check_user"
In the function you can run arbitrary code to check the request and raise an exception to block it if desired.
.. code-block:: postgres
CREATE OR REPLACE FUNCTION check_user() RETURNS void AS $$
BEGIN
IF current_user = 'evil_user' THEN
RAISE EXCEPTION 'No, you are evil'
USING HINT = 'Stop being so evil and maybe you can log in';
END IF;
END
$$ LANGUAGE plpgsql;
.. _client_auth:
Client Auth
===========
To make an authenticated request the client must include an :code:`Authorization` HTTP header with the value :code:`Bearer <jwt>`. For instance:
.. tabs::
.. code-tab:: http
GET /foo HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiamRvZSIsImV4cCI6MTQ3NTUxNjI1MH0.GYDZV3yM0gqvuEtJmfpplLBXSGYnke_Pvnl0tbKAjB4
.. code-tab:: bash Curl
curl "http://localhost:3000/foo" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiamRvZSIsImV4cCI6MTQ3NTUxNjI1MH0.GYDZV3yM0gqvuEtJmfpplLBXSGYnke_Pvnl0tbKAjB4"
The ``Bearer`` header value can be used with or without capitalization(``bearer``).
JWT Generation
--------------
You can create a valid JWT either from inside your database or via an external service. Each token is cryptographically signed with a secret key. In the case of symmetric cryptography the signer and verifier share the same secret passphrase. In asymmetric cryptography the signer uses the private key and the verifier the public key. PostgREST supports both symmetric and asymmetric cryptography.
JWT from SQL
~~~~~~~~~~~~
You can create JWT tokens in SQL using the `pgjwt extension <https://github.com/michelp/pgjwt>`_. It's simple and requires only pgcrypto. If you're on an environment like Amazon RDS which doesn't support installing new extensions, you can still manually run the `SQL inside pgjwt <https://github.com/michelp/pgjwt/blob/master/pgjwt--0.1.1.sql>`_ (you'll need to replace ``@extschema@`` with another schema or just delete it) which creates the functions you will need.
Next write a stored procedure that returns the token. The one below returns a token with a hard-coded role, which expires five minutes after it was issued. Note this function has a hard-coded secret as well.
.. code-block:: postgres
CREATE TYPE jwt_token AS (
token text
);
CREATE FUNCTION jwt_test() RETURNS public.jwt_token AS $$
SELECT public.sign(
row_to_json(r), 'reallyreallyreallyreallyverysafe'
) AS token
FROM (
SELECT
'my_role'::text as role,
extract(epoch from now())::integer + 300 AS exp
) r;
$$ LANGUAGE sql;
PostgREST exposes this function to clients via a POST request to ``/rpc/jwt_test``.
.. note::
To avoid hard-coding the secret in stored procedures, save it as a property of the database.
.. code-block:: postgres
-- run this once
ALTER DATABASE mydb SET "app.jwt_secret" TO 'reallyreallyreallyreallyverysafe';
-- then all functions can refer to app.jwt_secret
SELECT sign(
row_to_json(r), current_setting('app.jwt_secret')
) AS token
FROM ...
JWT from Auth0
~~~~~~~~~~~~~~
An external service like `Auth0 <https://auth0.com/>`_ can do the hard work transforming OAuth from Github, Twitter, Google etc into a JWT suitable for PostgREST. Auth0 can also handle email signup and password reset flows.
To use Auth0, create `an application <https://auth0.com/docs/get-started/applications>`_ for your app and `an API <https://auth0.com/docs/get-started/apis>`_ for your PostgREST server. Auth0 supports both HS256 and RS256 scheme for the issued tokens for APIs. For simplicity, you may first try HS256 scheme while creating your API on Auth0. Your application should use your PostgREST API's `API identifier <https://auth0.com/docs/get-started/apis/api-settings>`_ by setting it with the `audience parameter <https://auth0.com/docs/secure/tokens/access-tokens/get-access-tokens#control-access-token-audience>`_ during the authorization request. This will ensure that Auth0 will issue an access token for your PostgREST API. For PostgREST to verify the access token, you will need to set ``jwt-secret`` on PostgREST config file with your API's signing secret.
.. note::
Our code requires a database role in the JWT. To add it you need to save the database role in Auth0 `app metadata <https://auth0.com/docs/manage-users/user-accounts/metadata/manage-metadata-rules>`_. Then, you will need to write `a rule <https://auth0.com/docs/customize/rules>`_ that will extract the role from the user's app_metadata and set it as a `custom claim <https://auth0.com/docs/get-started/apis/scopes/sample-use-cases-scopes-and-claims#add-custom-claims-to-a-token>`_ in the access token. Note that, you may use Auth0's `core authorization feature <https://auth0.com/docs/manage-users/access-control/rbac>`_ for more complex use cases. Metadata solution is mentioned here for simplicity.
.. code:: javascript
function (user, context, callback) {
// Follow the documentations at
// https://postgrest.org/en/latest/configuration.html#db-role-claim-key
// to set a custom role claim on PostgREST
// and use it as custom claim attribute in this rule
const myRoleClaim = 'https://myapp.com/role';
user.app_metadata = user.app_metadata || {};
context.accessToken[myRoleClaim] = user.app_metadata.role;
callback(null, user, context);
}
.. _asym_keys:
Asymmetric Keys
~~~~~~~~~~~~~~~
As described in the :ref:`configuration` section, PostgREST accepts a ``jwt-secret`` config file parameter. If it is set to a simple string value like "reallyreallyreallyreallyverysafe" then PostgREST interprets it as an HMAC-SHA256 passphrase. However you can also specify a literal JSON Web Key (JWK) or set. For example, you can use an RSA-256 public key encoded as a JWK:
.. code-block:: json
{
"alg":"RS256",
"e":"AQAB",
"key_ops":["verify"],
"kty":"RSA",
"n":"9zKNYTaYGfGm1tBMpRT6FxOYrM720GhXdettc02uyakYSEHU2IJz90G_MLlEl4-WWWYoS_QKFupw3s7aPYlaAjamG22rAnvWu-rRkP5sSSkKvud_IgKL4iE6Y2WJx2Bkl1XUFkdZ8wlEUR6O1ft3TS4uA-qKifSZ43CahzAJyUezOH9shI--tirC028lNg767ldEki3WnVr3zokSujC9YJ_9XXjw2hFBfmJUrNb0-wldvxQbFU8RPXip-GQ_JPTrCTZhrzGFeWPvhA6Rqmc3b1PhM9jY7Dur1sjYWYVyXlFNCK3c-6feo5WlRfe1aCWmwZQh6O18eTmLeT4nWYkDzQ"
}
.. note::
This could also be a JSON Web Key Set (JWKS) if it was contained within an array assigned to a `keys` member, e.g. ``{ keys: [jwk1, jwk2] }``.
Just pass it in as a single line string, escaping the quotes:
.. code-block:: ini
jwt-secret = "{ \"alg\":\"RS256\", … }"
To generate such a public/private key pair use a utility like `latchset/jose <https://github.com/latchset/jose>`_.
.. code-block:: bash
jose jwk gen -i '{"alg": "RS256"}' -o rsa.jwk
jose jwk pub -i rsa.jwk -o rsa.jwk.pub
# now rsa.jwk.pub contains the desired JSON object
You can specify the literal value as we saw earlier, or reference a filename to load the JWK from a file:
.. code-block:: ini
jwt-secret = "@rsa.jwk.pub"
JWT security
~~~~~~~~~~~~
There are at least three types of common critiques against using JWT: 1) against the standard itself, 2) against using libraries with known security vulnerabilities, and 3) against using JWT for web sessions. We'll briefly explain each critique, how PostgREST deals with it, and give recommendations for appropriate user action.
The critique against the `JWT standard <https://datatracker.ietf.org/doc/html/rfc7519>`_ is voiced in detail `elsewhere on the web <https://web.archive.org/web/20230123041631/https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid>`_. The most relevant part for PostgREST is the so-called :code:`alg=none` issue. Some servers implementing JWT allow clients to choose the algorithm used to sign the JWT. In this case, an attacker could set the algorithm to :code:`none`, remove the need for any signature at all and gain unauthorized access. The current implementation of PostgREST, however, does not allow clients to set the signature algorithm in the HTTP request, making this attack irrelevant. The critique against the standard is that it requires the implementation of the :code:`alg=none` at all.
Critiques against JWT libraries are only relevant to PostgREST via the library it uses. As mentioned above, not allowing clients to choose the signature algorithm in HTTP requests removes the greatest risk. Another more subtle attack is possible where servers use asymmetric algorithms like RSA for signatures. Once again this is not relevant to PostgREST since it is not supported. Curious readers can find more information in `this article <https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/>`_. Recommendations about high quality libraries for usage in API clients can be found on `jwt.io <https://jwt.io/>`_.
The last type of critique focuses on the misuse of JWT for maintaining web sessions. The basic recommendation is to `stop using JWT for sessions <http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/>`_ because most, if not all, solutions to the problems that arise when you do, `do not work <http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/>`_. The linked articles discuss the problems in depth but the essence of the problem is that JWT is not designed to be secure and stateful units for client-side storage and therefore not suited to session management.
PostgREST uses JWT mainly for authentication and authorization purposes and encourages users to do the same. For web sessions, using cookies over HTTPS is good enough and well catered for by standard web frameworks.
Schema Isolation
================
You can isolate your api schema from internal implementation details, as explained in :ref:`schema_isolation`. For an example of wrapping a private table with a public view see the :ref:`public_ui` section below.
.. _sql_user_management:
SQL User Management
===================
Storing Users and Passwords
---------------------------
As mentioned, an external service can provide user management and coordinate with the PostgREST server using JWT. It's also possible to support logins entirely through SQL. It's a fair bit of work, so get ready.
The following table, functions, and triggers will live in a :code:`basic_auth` schema that you shouldn't expose publicly in the API. The public views and functions will live in a different schema which internally references this internal information.
First we'll need a table to keep track of our users:
.. code:: sql
-- We put things inside the basic_auth schema to hide
-- them from public view. Certain public procs/views will
-- refer to helpers and tables inside.
create schema if not exists basic_auth;
create table if not exists
basic_auth.users (
email text primary key check ( email ~* '^.+@.+\..+$' ),
pass text not null check (length(pass) < 512),
role name not null check (length(role) < 512)
);
We would like the role to be a foreign key to actual database roles, however PostgreSQL does not support these constraints against the :code:`pg_roles` table. We'll use a trigger to manually enforce it.
.. code-block:: plpgsql
create or replace function
basic_auth.check_role_exists() returns trigger as $$
begin
if not exists (select 1 from pg_roles as r where r.rolname = new.role) then
raise foreign_key_violation using message =
'unknown database role: ' || new.role;
return null;
end if;
return new;
end
$$ language plpgsql;
drop trigger if exists ensure_user_role_exists on basic_auth.users;
create constraint trigger ensure_user_role_exists
after insert or update on basic_auth.users
for each row
execute procedure basic_auth.check_role_exists();
Next we'll use the pgcrypto extension and a trigger to keep passwords safe in the :code:`users` table.
.. code-block:: plpgsql
create extension if not exists pgcrypto;
create or replace function
basic_auth.encrypt_pass() returns trigger as $$
begin
if tg_op = 'INSERT' or new.pass <> old.pass then
new.pass = crypt(new.pass, gen_salt('bf'));
end if;
return new;
end
$$ language plpgsql;
drop trigger if exists encrypt_pass on basic_auth.users;
create trigger encrypt_pass
before insert or update on basic_auth.users
for each row
execute procedure basic_auth.encrypt_pass();
With the table in place we can make a helper to check a password against the encrypted column. It returns the database role for a user if the email and password are correct.
.. code-block:: plpgsql
create or replace function
basic_auth.user_role(email text, pass text) returns name
language plpgsql
as $$
begin
return (
select role from basic_auth.users
where users.email = user_role.email
and users.pass = crypt(user_role.pass, users.pass)
);
end;
$$;
.. _public_ui:
Public User Interface
---------------------
In the previous section we created an internal table to store user information. Here we create a login function which takes an email address and password and returns JWT if the credentials match a user in the internal table.
Permissions
~~~~~~~~~~~
Your database roles need access to the schema, tables, views and functions in order to service HTTP requests.
Recall from the `Overview of Role System`_ that PostgREST uses special roles to process requests, namely the authenticator and
anonymous roles. Below is an example of permissions that allow anonymous users to create accounts and attempt to log in.
.. code-block:: postgres
-- the names "anon" and "authenticator" are configurable and not
-- sacred, we simply choose them for clarity
create role anon noinherit;
create role authenticator noinherit;
grant anon to authenticator;
Then, add ``db-anon-role`` to the configuration file to allow anonymous requests.
.. code:: ini
db-anon-role = "anon"
Logins
~~~~~~
As described in `JWT from SQL`_, we'll create a JWT inside our login function. Note that you'll need to adjust the secret key which is hard-coded in this example to a secure (at least thirty-two character) secret of your choosing.
.. code-block:: postgres
-- add type
CREATE TYPE basic_auth.jwt_token AS (
token text
);
-- login should be on your exposed schema
create or replace function
login(email text, pass text) returns basic_auth.jwt_token as $$
declare
_role name;
result basic_auth.jwt_token;
begin
-- check email and password
select basic_auth.user_role(email, pass) into _role;
if _role is null then
raise invalid_password using message = 'invalid user or password';
end if;
select sign(
row_to_json(r), 'reallyreallyreallyreallyverysafe'
) as token
from (
select _role as role, login.email as email,
extract(epoch from now())::integer + 60*60 as exp
) r
into result;
return result;
end;
$$ language plpgsql security definer;
grant execute on function login(text,text) to anon;
Since the above :code:`login` function is defined as `security definer <https://www.postgresql.org/docs/current/sql-createfunction.html#id-1.9.3.67.10.2>`_,
the anonymous user :code:`anon` doesn't need permission to read the :code:`basic_auth.users` table. It doesn't even need permission to access the :code:`basic_auth` schema.
:code:`grant execute on function` is included for clarity but it might not be needed, see :ref:`func_privs` for more details.
An API request to call this function would look like:
.. tabs::
.. code-tab:: http
POST /rpc/login HTTP/1.1
{ "email": "foo@bar.com", "pass": "foobar" }
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/login" \
-X POST -H "Content-Type: application/json" \
-d '{ "email": "foo@bar.com", "pass": "foobar" }'
The response would look like the snippet below. Try decoding the token at `jwt.io <https://jwt.io/>`_. (It was encoded with a secret of :code:`reallyreallyreallyreallyverysafe` as specified in the SQL code above. You'll want to change this secret in your app!)
.. code:: json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImZvb0BiYXIuY29tIiwicGFzcyI6ImZvb2JhciJ9.37066TTRlh-1hXhnA9oO9Pj6lgL6zFuJU0iCHhuCFno"
}
Alternatives
~~~~~~~~~~~~
See the how-to :ref:`sql-user-management-using-postgres-users-and-passwords` for a similar way that completely avoids the table :code:`basic_auth.users`.
+22 -5
View File
@@ -28,7 +28,11 @@ import os
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ["sphinx_tabs.tabs", "sphinx_copybutton"]
extensions = [
"sphinx_tabs.tabs",
"sphinx_copybutton",
"sphinxext.opengraph",
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
@@ -45,7 +49,7 @@ source_suffix = ".rst"
master_doc = "index"
# This is overriden by readthedocs with the version tag anyway
version = "10.2"
version = "12.0"
# To avoid repetition in <title> we set this to an empty string.
release = ""
@@ -59,7 +63,7 @@ copyright = "2017, " + author
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
language = "en"
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
@@ -70,7 +74,7 @@ language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "shared/*.rst"]
# The reST default role (used for this markup: `text`) to use for all
# documents.
@@ -287,7 +291,20 @@ def setup(app):
app.add_css_file("css/custom.css")
user_agent = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:135.0) Gecko/20100101 Firefox/135.0"
# taken from https://github.com/sphinx-doc/sphinx/blob/82dad44e5bd3776ecb6fd8ded656bc8151d0e63d/sphinx/util/requests.py#L42
user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:25.0) Gecko/20100101 Firefox/25.0"
# sphinx-tabs configuration
sphinx_tabs_disable_tab_closing = True
# sphinxext-opengraph configuration
ogp_image = "_images/logo.png"
ogp_use_first_image = True
ogp_enable_meta_description = True
ogp_description_length = 300
## RTD sets html_baseurl, ensures we use the correct env for canonical URLs
## Useful to generate correct meta tags for Open Graph
## Refs: https://github.com/readthedocs/readthedocs.org/issues/10226, https://github.com/urllib3/urllib3/pull/3064
html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "/")
-728
View File
@@ -1,728 +0,0 @@
.. _configuration:
Configuration
=============
Without configuration, PostgREST won't be able to serve requests. At the minimum it needs either :ref:`a role to serve anonymous requests with <db-anon-role>` - or :ref:`a secret to use for JWT authentication <jwt-secret>`. Config parameters can be provided via :ref:`file_config`, via :ref:`env_variables_config` or through :ref:`in_db_config`.
To connect to a database it uses a `libpq connection string <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>`_. The connection string can be set in the configuration file or via environment variable or can be read from an external file. See :ref:`db-uri` for details. Any parameter that is not set in the connection string is read from `libpq environment variables <https://www.postgresql.org/docs/current/libpq-envars.html>`_. The default connection string is ``postgresql://``, which reads **all** parameters from the environment.
The user with whom PostgREST connects to the database is also known as the authenticator role. For more information about the anonymous vs authenticator roles see :ref:`roles`.
Config parameters are read in the following order:
1. From the config file.
2. From environment variables, overriding values from the config file.
3. From the database, overriding values from both the config file and environment variables.
.. _file_config:
Config File
-----------
PostgREST can read a config file. There is no predefined location for this file, you must specify the file path as the one and only argument to the server:
.. code:: bash
./postgrest /path/to/postgrest.conf
.. note::
Configuration can be reloaded without restarting the server. See :ref:`config_reloading`.
The configuration file must contain a set of key value pairs:
.. code::
# postgrest.conf
# The standard connection URI format, documented at
# https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
db-uri = "postgres://user:pass@host:5432/dbname"
# The database role to use when no client authentication is provided.
# Should differ from authenticator
db-anon-role = "anon"
# The secret to verify the JWT for authenticated requests with.
# Needs to be 32 characters minimum.
jwt-secret = "reallyreallyreallyreallyverysafe"
jwt-secret-is-base64 = False
# Port the postgrest process is listening on for http requests
server-port = 80
You can run ``postgrest --example`` to display all possible configuration parameters and how to use them in a configuration file.
.. _env_variables_config:
Environment Variables
---------------------
You can also set these :ref:`configuration parameters <config_full_list>` using environment variables. They are capitalized, have a ``PGRST_`` prefix, and use underscores. For example: ``PGRST_DB_URI`` corresponds to ``db-uri`` and ``PGRST_APP_SETTINGS_*`` to ``app.settings.*``.
.. _in_db_config:
In-Database Configuration
-------------------------
By adding settings to the **authenticator** role (see :ref:`roles`), you can make the database the single source of truth for PostgREST's configuration.
This is enabled by :ref:`db-config`.
For example, you can configure :ref:`db-schemas` and :ref:`jwt-secret` like this:
.. code:: postgresql
ALTER ROLE authenticator SET pgrst.db_schemas = "tenant1, tenant2, tenant3"
ALTER ROLE authenticator IN DATABASE <your_database_name> SET pgrst.jwt_secret = "REALLYREALLYREALLYREALLYVERYSAFE"
You can use both database-specific settings with `IN DATABASE` and cluster-wide settings without it. Database-specific settings will override cluster-wide settings if both are used for the same parameter.
Note that underscores(``_``) need to be used instead of dashes(``-``) for the in-database config parameters.
.. important::
For altering a role in this way, you need a SUPERUSER. You might not be able to use this configuration mode on cloud-hosted databases.
When using both the configuration file and the in-database configuration, the latter takes precedence.
.. danger::
If direct connections to the database are allowed, then it's not safe to use the in-db configuration for storing the :ref:`jwt-secret`.
The settings of every role are PUBLIC - they can be viewed by any user that queries the ``pg_catalog.pg_db_role_setting`` table.
In this case you should keep the :ref:`jwt-secret` in the configuration file or as environment variables.
.. _config_reloading:
Configuration Reloading
=======================
It's possible to reload PostgREST's configuration without restarting the server. You can do this :ref:`via signal <config_reloading_signal>` or :ref:`via notification <config_reloading_notify>`.
It's not possible to change :ref:`env_variables_config` for a running process and reloading a Docker container configuration will not work. In these cases, you need to restart the PostgREST server or use :ref:`in_db_config` as an alternative.
.. important::
The following settings will not be reloaded. You will need to restart PostgREST to change those.
* :ref:`admin-server-port`
* :ref:`db-uri`
* :ref:`db-pool`
* :ref:`db-pool-acquisition-timeout`
* :ref:`db-pool-max-lifetime`
* :ref:`server-host`
* :ref:`server-port`
* :ref:`server-unix-socket`
* :ref:`server-unix-socket-mode`
.. _config_reloading_signal:
Reload with signal
------------------
To reload the configuration via signal, send a SIGUSR2 signal to the server process.
.. code:: bash
killall -SIGUSR2 postgrest
.. _config_reloading_notify:
Reload with NOTIFY
------------------
To reload the configuration from within the database, you can use a NOTIFY command.
.. code:: postgresql
NOTIFY pgrst, 'reload config'
The ``"pgrst"`` notification channel is enabled by default. For configuring the channel, see :ref:`db-channel` and :ref:`db-channel-enabled`.
.. _config_full_list:
List of parameters
==================
=========================== ======= ================= ==========
Name Type Default Reloadable
=========================== ======= ================= ==========
admin-server-port Int
app.settings.* String Y
db-anon-role String Y
db-channel String pgrst Y
db-channel-enabled Boolean True Y
db-config Boolean True Y
db-extra-search-path String public Y
db-max-rows Int ∞ Y
db-plan-enabled Boolean False Y
db-pool Int 10
db-pool-acquisition-timeout Int 10
db-pool-max-lifetime Int 1800
db-pre-request String Y
db-prepared-statements Boolean True Y
db-schemas String public Y
db-tx-end String commit
db-uri String postgresql://
db-use-legacy-gucs Boolean True Y
jwt-aud String Y
jwt-role-claim-key String .role Y
jwt-secret String Y
jwt-secret-is-base64 Boolean False Y
log-level String error Y
openapi-mode String follow-privileges Y
openapi-security-active Boolean False Y
openapi-server-proxy-uri String Y
raw-media-types String Y
server-host String !4
server-port Int 3000
server-unix-socket String
server-unix-socket-mode String 660
=========================== ======= ================= ==========
.. _admin-server-port:
admin-server-port
-----------------
=============== =======================
**Environment** PGRST_ADMIN_SERVER_PORT
**In-Database** `n/a`
=============== =======================
Specifies the port for the :ref:`health_check` endpoints.
.. _app.settings.*:
app.settings.*
--------------
=============== ====================
**Environment** PGRST_APP_SETTINGS_*
**In-Database** pgrst.app_settings_*
=============== ====================
Arbitrary settings that can be used to pass in secret keys directly as strings, or via OS environment variables. For instance: :code:`app.settings.jwt_secret = "$(MYAPP_JWT_SECRET)"` will take :code:`MYAPP_JWT_SECRET` from the environment and make it available to postgresql functions as :code:`current_setting('app.settings.jwt_secret')`.
.. _db-anon-role:
db-anon-role
------------
=============== ==================
**Environment** PGRST_DB_ANON_ROLE
**In-Database** `n/a`
=============== ==================
The database role to use when executing commands on behalf of unauthenticated clients. For more information, see :ref:`roles`.
When unset anonymous access will be blocked.
.. _db-channel:
db-channel
----------
=============== ================
**Environment** PGRST_DB_CHANNEL
**In-Database** `n/a`
=============== ================
The name of the notification channel that PostgREST uses for :ref:`schema_reloading` and configuration reloading.
.. _db-channel-enabled:
db-channel-enabled
------------------
=============== ========================
**Environment** PGRST_DB_CHANNEL_ENABLED
**In-Database** `n/a`
=============== ========================
When this is set to :code:`true`, the notification channel specified in :ref:`db-channel` is enabled.
You should set this to ``false`` when using PostgresSQL behind an external connection pooler such as PgBouncer working in transaction pooling mode. See :ref:`this section <external_connection_poolers>` for more information.
.. _db-config:
db-config
---------
=============== ===============
**Environment** PGRST_DB_CONFIG
**In-Database** `n/a`
=============== ===============
Enables the in-database configuration.
.. _db-extra-search-path:
db-extra-search-path
--------------------
=============== ==========================
**Environment** PGRST_DB_EXTRA_SEARCH_PATH
**In-Database** pgrst.db_extra_search_path
=============== ==========================
Extra schemas to add to the `search_path <https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH>`_ of every request. These schemas tables, views and stored procedures **don't get API endpoints**, they can only be referred from the database objects inside your :ref:`db-schemas`.
This parameter was meant to make it easier to use **PostgreSQL extensions** (like PostGIS) that are outside of the :ref:`db-schemas`.
Multiple schemas can be added in a comma-separated string, e.g. ``public, extensions``.
.. _db-max-rows:
db-max-rows
-----------
*For backwards compatibility, this config parameter is also available without prefix as "max-rows".*
=============== =================
**Environment** PGRST_DB_MAX_ROWS
**In-Database** pgrst.db_max_rows
=============== =================
A hard limit to the number of rows PostgREST will fetch from a view, table, or stored procedure. Limits payload size for accidental or malicious requests.
.. _db-plan-enabled:
db-plan-enabled
---------------
=============== =====================
**Environment** PGRST_DB_PLAN_ENABLED
**In-Database** pgrst.db_plan_enabled
=============== =====================
When this is set to :code:`true`, the execution plan of a request can be retrieved by using the :code:`Accept: application/vnd.pgrst.plan` header. See :ref:`explain_plan`.
It's recommended to use this in testing environments only since it reveals internal database details.
However, if you choose to use it in production you can add a :ref:`db-pre-request` to filter the requests that can use this feature.
For example, to only allow requests from an IP address to get the execution plans:
.. code-block:: postgresql
-- Assuming a proxy(Nginx, Cloudflare, etc) passes an "X-Forwarded-For" header(https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)
create or replace function filter_plan_requests()
returns void as $$
declare
headers json := current_setting('request.headers', true)::json;
client_ip text := coalesce(headers->>'x-forwarded-for', '');
accept text := coalesce(headers->>'accept', '');
begin
if accept like 'application/vnd.pgrst.plan%' and client_ip != '144.96.121.73' then
raise insufficient_privilege using
message = 'Not allowed to use application/vnd.pgrst.plan';
end if;
end; $$ language plpgsql;
-- set this function on your postgrest.conf
-- db-pre-request = filter_plan_requests
.. _db-pool:
db-pool
-------
=============== =================
**Environment** PGRST_DB_POOL
**In-Database** `n/a`
=============== =================
Number of connections to keep open in PostgREST's database pool. Having enough here for the maximum expected simultaneous client connections can improve performance. Note it's pointless to set this higher than the :code:`max_connections` GUC in your database.
.. _db-pool-acquisition-timeout:
db-pool-acquisition-timeout
---------------------------
=============== =================
**Environment** PGRST_DB_POOL_ACQUISITION_TIMEOUT
**In-Database** `n/a`
=============== =================
Specifies the maximum time in seconds that the request will wait for the pool to free up a connection slot to the database. If it times out without acquiring a connection, then the request is aborted and a ``504`` error is returned.
.. _db-pool-max-lifetime:
db-pool-max-lifetime
--------------------
=============== =================
**Environment** PGRST_DB_POOL_MAX_LIFETIME
**In-Database** `n/a`
=============== =================
Specifies the maximum time in seconds of an existing connection in the pool. When this lifetime is reached, then the connection will be closed and returned to the pool.
.. _db-pre-request:
db-pre-request
--------------
*For backwards compatibility, this config parameter is also available without prefix as "pre-request".*
=============== =================
**Environment** PGRST_DB_PRE_REQUEST
**In-Database** pgrst.db_pre_request
=============== =================
A schema-qualified stored procedure name to call right after switching roles for a client request. This provides an opportunity to modify SQL variables or raise an exception to prevent the request from completing.
.. _db-prepared-statements:
db-prepared-statements
----------------------
=============== =================
**Environment** PGRST_DB_PREPARED_STATEMENTS
**In-Database** pgrst.db_prepared_statements
=============== =================
Enables or disables prepared statements.
When disabled, the generated queries will be parameterized (invulnerable to SQL injection) but they will not be prepared (cached in the database session). Not using prepared statements will noticeably decrease performance, so it's recommended to always have this setting enabled.
You should only set this to ``false`` when using PostgresSQL behind an external connection pooler such as PgBouncer working in transaction pooling mode. See :ref:`this section <external_connection_poolers>` for more information.
.. _db-schemas:
db-schemas
----------
*For backwards compatibility, this config parameter is also available in singular as "db-schema".*
=============== =================
**Environment** PGRST_DB_SCHEMAS
**In-Database** pgrst.db_schemas
=============== =================
The database schema to expose to REST clients. Tables, views and stored procedures in this schema will get API endpoints.
.. code:: bash
db-schemas = "api"
This schema gets added to the `search_path <https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH>`_ of every request.
List of schemas
~~~~~~~~~~~~~~~
You can also specify a list of schemas that can be used for **schema-based multitenancy** and **api versioning** by :ref:`multiple-schemas`. Example:
.. code:: bash
db-schemas = "tenant1, tenant2"
If you don't :ref:`Switch Schemas <multiple-schemas>`, the first schema in the list(``tenant1`` in this case) is chosen as the default schema.
*Only the chosen schema* gets added to the `search_path <https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH>`_ of every request.
.. warning::
Never expose private schemas in this way. See :ref:`schema_isolation`.
.. _db-tx-end:
db-tx-end
---------
=============== =================
**Environment** PGRST_DB_TX_END
**In-Database** pgrst.db_tx_end
=============== =================
Specifies how to terminate the database transactions.
.. code:: bash
# The transaction is always committed
db-tx-end = "commit"
# The transaction is committed unless a "Prefer: tx=rollback" header is sent
db-tx-end = "commit-allow-override"
# The transaction is always rolled back
db-tx-end = "rollback"
# The transaction is rolled back unless a "Prefer: tx=commit" header is sent
db-tx-end = "rollback-allow-override"
.. _db-uri:
db-uri
------
=============== =================
**Environment** PGRST_DB_URI
**In-Database** `n/a`
=============== =================
The standard connection PostgreSQL `URI format <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>`_. Symbols and unusual characters in the password or other fields should be percent encoded to avoid a parse error. If enforcing an SSL connection to the database is required you can use `sslmode <https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS>`_ in the URI, for example ``postgres://user:pass@host:5432/dbname?sslmode=require``.
When running PostgREST on the same machine as PostgreSQL, it is also possible to connect to the database using a `Unix socket <https://en.wikipedia.org/wiki/Unix_domain_socket>`_ and the `Peer Authentication method <https://www.postgresql.org/docs/current/auth-peer.html>`_ as an alternative to TCP/IP communication and authentication with a password, this also grants higher performance. To do this you can omit the host and the password, e.g. ``postgres://user@/dbname``, see the `libpq connection string <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>`_ documentation for more details.
Choosing a value for this parameter beginning with the at sign such as ``@filename`` (e.g. ``@./configs/my-config``) loads the connection string out of an external file.
.. _db-use-legacy-gucs:
db-use-legacy-gucs
------------------
=============== =================
**Environment** PGRST_DB_USE_LEGACY_GUCS
**In-Database** pgrst.db_use_legacy_gucs
=============== =================
Determine if GUC request settings for headers, cookies and jwt claims use the :ref:`legacy names <guc_legacy_names>` (string with dashes, invalid starting from PostgreSQL v14) with text values instead of the :ref:`new names <guc_req_headers_cookies_claims>` (string without dashes, valid on all PostgreSQL versions) with json values.
On PostgreSQL versions 14 and above, this parameter is ignored.
.. _jwt-aud:
jwt-aud
-------
=============== =================
**Environment** PGRST_JWT_AUD
**In-Database** pgrst.jwt_aud
=============== =================
Specifies the `JWT audience claim <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3>`_. If this claim is present in the client provided JWT then you must set this to the same value as in the JWT, otherwise verifying the JWT will fail.
.. _jwt-role-claim-key:
jwt-role-claim-key
------------------
*For backwards compatibility, this config parameter is also available without prefix as "role-claim-key".*
=============== =================
**Environment** PGRST_JWT_ROLE_CLAIM_KEY
**In-Database** pgrst.jwt_role_claim_key
=============== =================
A JSPath DSL that specifies the location of the :code:`role` key in the JWT claims. This can be used to consume a JWT provided by a third party service like Auth0, Okta or Keycloak. Usage examples:
.. code:: bash
# {"postgrest":{"roles": ["other", "author"]}}
# the DSL accepts characters that are alphanumerical or one of "_$@" as keys
jwt-role-claim-key = ".postgrest.roles[1]"
# {"https://www.example.com/role": { "key": "author }}
# non-alphanumerical characters can go inside quotes(escaped in the config value)
jwt-role-claim-key = ".\"https://www.example.com/role\".key"
.. _jwt-secret:
jwt-secret
----------
=============== =================
**Environment** PGRST_JWT_SECRET
**In-Database** pgrst.jwt_secret
=============== =================
The secret or `JSON Web Key (JWK) (or set) <https://datatracker.ietf.org/doc/html/rfc7517>`_ used to decode JWT tokens clients provide for authentication. For security the key must be **at least 32 characters long**. If this parameter is not specified then PostgREST refuses authentication requests. Choosing a value for this parameter beginning with the at sign such as :code:`@filename` loads the secret out of an external file. This is useful for automating deployments. Note that any binary secrets must be base64 encoded. Both symmetric and asymmetric cryptography are supported. For more info see :ref:`asym_keys`.
Choosing a value for this parameter beginning with the at sign such as ``@filename`` (e.g. ``@./configs/my-config``) loads the secret out of an external file.
.. warning::
Only when using the :ref:`file_config`, if the ``jwt-secret`` contains a ``$`` character by itself it will give errors. In this case, use ``$$`` and PostgREST will interpret it as a single ``$`` character.
.. _jwt-secret-is-base64:
jwt-secret-is-base64
--------------------
=============== =================
**Environment** PGRST_JWT_SECRET_IS_BASE64
**In-Database** pgrst.jwt_secret_is_base64
=============== =================
When this is set to :code:`true`, the value derived from :code:`jwt-secret` will be treated as a base64 encoded secret.
.. _log-level:
log-level
---------
=============== =================
**Environment** PGRST_LOG_LEVEL
**In-Database** `n/a`
=============== =================
Specifies the level of information to be logged while running PostgREST.
.. code:: bash
# Only startup and db connection recovery messages are logged
log-level = "crit"
# All the "crit" level events plus server errors (status 5xx) are logged
log-level = "error"
# All the "error" level events plus request errors (status 4xx) are logged
log-level = "warn"
# All the "warn" level events plus all requests (every status code) are logged
log-level = "info"
Because currently there's no buffering for logging, the levels with minimal logging(``crit/error``) will increase throughput.
.. _openapi-mode:
openapi-mode
------------
=============== =================
**Environment** PGRST_OPENAPI_MODE
**In-Database** pgrst.openapi_mode
=============== =================
Specifies how the OpenAPI output should be displayed.
.. code:: bash
# Follows the privileges of the JWT role claim (or from db-anon-role if the JWT is not sent)
# Shows information depending on the permissions that the role making the request has
openapi-mode = "follow-privileges"
# Ignores the privileges of the JWT role claim (or from db-anon-role if the JWT is not sent)
# Shows all the exposed information, regardless of the permissions that the role making the request has
openapi-mode = "ignore-privileges"
# Disables the OpenApi output altogether.
# Throws a `404 Not Found` error when accessing the API root path
openapi-mode = "disabled"
.. _openapi-security-active:
openapi-security-active
-----------------------
=============== =============================
**Environment** PGRST_OPENAPI_SECURITY_ACTIVE
**In-Database** pgrst.openapi_security_active
=============== =============================
When this is set to :code:`true`, security options are included in the :ref:`OpenAPI output <open-api>`.
.. _openapi-server-proxy-uri:
openapi-server-proxy-uri
------------------------
=============== =================
**Environment** PGRST_OPENAPI_SERVER_PROXY_URI
**In-Database** pgrst.openapi_server_proxy_uri
=============== =================
Overrides the base URL used within the OpenAPI self-documentation hosted at the API root path. Use a complete URI syntax :code:`scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]`. Ex. :code:`https://postgrest.com`
.. code:: json
{
"swagger": "2.0",
"info": {
"version": "0.4.3.0",
"title": "PostgREST API",
"description": "This is a dynamic API generated by PostgREST"
},
"host": "postgrest.com:443",
"basePath": "/",
"schemes": [
"https"
]
}
.. _raw-media-types:
raw-media-types
---------------
=============== =================
**Environment** PGRST_RAW_MEDIA_TYPES
**In-Database** pgrst.raw_media_types
=============== =================
This serves to extend the `Media Types <https://en.wikipedia.org/wiki/Media_type>`_ that PostgREST currently accepts through an ``Accept`` header.
These media types can be requested by following the same rules as the ones defined in :ref:`scalar_return_formats`.
As an example, the below config would allow you to request an **image** and a **XML** file by doing a request with ``Accept: image/png``
or ``Accept: font/woff2``, respectively.
.. code:: bash
raw-media-types="image/png, font/woff2"
.. _server-host:
server-host
-----------
=============== =================
**Environment** PGRST_SERVER_HOST
**In-Database** `n/a`
=============== =================
Where to bind the PostgREST web server. In addition to the usual address options, PostgREST interprets these reserved addresses with special meanings:
* :code:`*` - any IPv4 or IPv6 hostname
* :code:`*4` - any IPv4 or IPv6 hostname, IPv4 preferred
* :code:`!4` - any IPv4 hostname
* :code:`*6` - any IPv4 or IPv6 hostname, IPv6 preferred
* :code:`!6` - any IPv6 hostname
.. _server-port:
server-port
-----------
=============== =================
**Environment** PGRST_SERVER_PORT
**In-Database** `n/a`
=============== =================
The TCP port to bind the web server.
.. _server-unix-socket:
server-unix-socket
------------------
=============== =================
**Environment** PGRST_SERVER_UNIX_SOCKET
**In-Database** `n/a`
=============== =================
`Unix domain socket <https://en.wikipedia.org/wiki/Unix_domain_socket>`_ where to bind the PostgREST web server.
If specified, this takes precedence over :ref:`server-port`. Example:
.. code:: bash
server-unix-socket = "/tmp/pgrst.sock"
.. _server-unix-socket-mode:
server-unix-socket-mode
-----------------------
=============== =================
**Environment** PGRST_SERVER_UNIX_SOCKET_MODE
**In-Database** `n/a`
=============== =================
`Unix file mode <https://en.wikipedia.org/wiki/File_system_permissions>`_ to be set for the socket specified in :ref:`server-unix-socket`
Needs to be a valid octal between 600 and 777.
.. code:: bash
server-unix-socket-mode = "660"
+18 -9
View File
@@ -1,9 +1,9 @@
let
# Commit of the Nixpkgs repository that we want to use.
nixpkgsVersion = {
date = "2021-06-02";
rev = "84aa23742f6c72501f9cc209f29c438766f5352d";
tarballHash = "0h7xl6q0yjrbl9vm3h6lkxw692nm8bg3wy65gm95a2mivhrdjpxp";
date = "2023-03-25";
rev = "dbf5322e93bcc6cfc52268367a8ad21c09d76fea";
tarballHash = "0lwk4v9dkvd28xpqch0b0jrac4xl9lwm6snrnzx8k5lby72kmkng";
};
# Nix files that describe the Nixpkgs repository. We evaluate the expression
@@ -15,12 +15,9 @@ let
})
{ };
sphinxTabsPkg = ps: ps.callPackage ./extensions/sphinx-tabs.nix { };
sphinxCopybuttonPkg = ps: ps.callPackage ./extensions/sphinx-copybutton.nix { };
python = pkgs.python3.withPackages (ps: [ ps.sphinx ps.sphinx_rtd_theme ps.livereload (sphinxTabsPkg ps) (sphinxCopybuttonPkg ps) ]);
python = pkgs.python3.withPackages (ps: [ ps.sphinx ps.sphinx_rtd_theme ps.livereload ps.sphinx-tabs ps.sphinx-copybutton ps.sphinxext-opengraph ]);
in
{
rec {
inherit pkgs;
build =
@@ -77,7 +74,9 @@ in
| tail -n+2 \
| tr '\n' '\0' \
| xargs -0 -n 1 -i \
sh -c "grep \"{}\" $FILES > /dev/null || echo \"{}\""
sh -c "grep \"{}\" $FILES > /dev/null || echo \"{}\"" \
| tee unuseddict
test ! -s unuseddict
'';
linkcheck =
@@ -88,4 +87,14 @@ in
${python}/bin/sphinx-build --color -b linkcheck . _build
'';
check =
pkgs.writeShellScriptBin "postgrest-docs-check"
''
set -euo pipefail
${build}/bin/postgrest-docs-build
${dictcheck}/bin/postgrest-docs-dictcheck
${linkcheck}/bin/postgrest-docs-linkcheck
${spellcheck}/bin/postgrest-docs-spellcheck
'';
}
+9 -42
View File
@@ -6,7 +6,7 @@ Community Tutorials
* `Building a Contacts List with PostgREST and Vue.js <https://www.youtube.com/watch?v=iHtsALtD5-U>`_ -
In this video series, DigitalOcean shows how to build and deploy an Nginx + PostgREST(using a managed PostgreSQL database) + Vue.js webapp in an Ubuntu server droplet.
* `PostgREST + Auth0: Create REST API in minutes, and add social login using Auth0 <https://samkhawase.com/blog/postgrest-1-introduction/>`_ - A step-by-step tutorial to show how to dockerize and integrate Auth0 to PostgREST service.
* `PostgREST + Auth0: Create REST API in mintutes, and add social login using Auth0 <https://samkhawase.com/blog/postgrest/>`_ - A step-by-step tutorial to show how to dockerize and integrate Auth0 to PostgREST service.
* `PostgREST + PostGIS API tutorial in 5 minutes <https://gis-ops.com/postgrest-postgis-api-tutorial-geospatial-api-in-5-minutes/>`_ -
In this tutorial, GIS • OPS shows how to perform PostGIS calculations through PostgREST :ref:`s_procs` interface.
@@ -16,6 +16,12 @@ Community Tutorials
* `How PostgreSQL triggers work when called with a PostgREST PATCH HTTP request <https://blog.fgribreau.com/2020/11/how-postgresql-triggers-works-when.html>`_ - A tutorial to see how the old and new values are set or not when doing a PATCH request to PostgREST.
* `REST Data Service on YugabyteDB / PostgreSQL <https://dev.to/yugabyte/rest-data-service-on-yugabytedb-postgresql-5f2h>`_
* `Build data-driven applications with Workers and PostgreSQL <https://developers.cloudflare.com/workers/tutorials/postgres/>`_ - A tutorial on how to integrate with PostgREST and PostgreSQL using Cloudflare Workers.
.. * `A poor man's API <https://blog.frankel.ch/poor-man-api>`_ - Shows how to integrate PostgREST with Apache APISIX as an alternative to Nginx.
.. _templates:
Templates
@@ -29,28 +35,10 @@ Templates
Example Apps
------------
* `chronicle <https://github.com/srid/chronicle>`_ - tracking a tree of personal memories
* `code-du-travail-backoffice <https://github.com/SocialGouv/code-du-travail-backoffice>`_ - data administration portal for the official French Labor Code and Agreements
* `delibrium-postgrest <https://gitlab.com/delibrium/delibrium-postgrest/>`_ - example school API and front-end in Vue.js
* `elm-workshop <https://github.com/diogob/elm-workshop>`_ - building a simple database query UI
* `ember-postgrest-dynamic-ui <https://github.com/benoror/ember-postgrest-dynamic-ui>`_ - generating Ember forms to edit data
* `ETH-transactions-storage <https://github.com/Adamant-im/ETH-transactions-storage>`_ - indexer for Ethereum to get transaction list by ETH address
* `ext-postgrest-crud <https://github.com/timwis/ext-postgrest-crud>`_ - browser-based spreadsheet
* `general <https://github.com/PierreRochard/general>`_ - example auth back-end
* `goodfilm <https://github.com/tyrchen/goodfilm>`_ - example film API
* `guild-operators <https://github.com/cardano-community/koios-artifacts/tree/main/files/grest>`_ - example queries and functions that the Cardano Community uses for their Guild Operators' Repository
* `handsontable-postgrest <https://github.com/timwis/handsontable-postgrest>`_ - an excel-like database table editor
* `heritage-near-me <https://github.com/CodeforAustralia/heritage-near-me>`_ - Elm and PostgREST with PostGIS
* `ng-admin-postgrest <https://github.com/marmelab/ng-admin-postgrest>`_ - automatic database admin panel
* `pgrst-dev-setup <https://github.com/Qu4tro/pgrst-dev-setup>`_ - docker-compose and tmuxp setup for experimentation.
* `postgres-postgrest-cloudflared-example <https://github.com/cloudflare/postgres-postgrest-cloudflared-example>`_ - docker-compose setup exposing PostgREST using cloudfared
* `postgrest-demo <https://github.com/SMRxT/postgrest-demo>`_ - multi-tenant logging system
* `postgrest-example <https://github.com/begriffs/postgrest-example>`_ - sqitch versioning for API
* `postgrest-sessions-example <https://github.com/monacoremo/postgrest-sessions-example>`_ - example for cookie-based sessions
* `postgrest-translation-proxy <https://github.com/NikolayS/postgrest-translation-proxy>`_ - calling to external translation service
* `postgrest-ui <https://github.com/tatut/postgrest-ui>`_ - ClojureScript UI components for PostgREST
* `postgrest-vercel <https://github.com/seveibar/postgrest-vercel>`_ - run PostgREST on Vercel (Serverless/AWS Lambda)
* `PostgrestSkeleton <https://github.com/Recmo/PostgrestSkeleton>`_ - Docker Compose, PostgREST, Nginx and Auth0
* `PostGUI <https://github.com/priyank-purohit/PostGUI>`_ - React Material UI admin panel
* `prospector <https://github.com/sfcta/prospector>`_ - data warehouse and visualization platform
@@ -63,6 +51,7 @@ DevOps
* `cloudstark/helm-charts <https://github.com/cloudstark/helm-charts/tree/master/postgrest>`_ - helm chart to deploy PostgREST to a Kubernetes cluster via a Deployment and Service
* `jbkarle/postgrest <https://github.com/jbkarle/postgrest>`_ - helm chart with a demo database for development and test purposes
* `Limezest/postgrest-cloud-run <https://github.com/Limezest/postgrest-cloud-run>`_ - expose a PostgreSQL database on Cloud SQL using Cloud Run
* `eyberg/postgrest <https://repo.ops.city/v2/packages/eyberg/postgrest/10.1.1/x86_64/show>`_ - run PostgREST as a Nanos unikernel
.. _eco_external_notification:
@@ -71,14 +60,10 @@ External Notification
These are PostgreSQL bridges that propagate LISTEN/NOTIFY to external queues for further processing. This allows stored procedures to initiate actions outside the database such as sending emails.
* `pg-bridge <https://github.com/matthewmueller/pg-bridge>`_ - Amazon SNS
* `pg-kinesis-bridge <https://github.com/daurnimator/pg-kinesis-bridge>`_ - Amazon Kinesis
* `pg-notify-webhook <https://github.com/vbalasu/pg-notify-webhook>`_ - trigger webhooks from PostgreSQL's LISTEN/NOTIFY
* `pgsql-listen-exchange <https://github.com/gmr/pgsql-listen-exchange>`_ - RabbitMQ
* `postgres-websockets <https://github.com/diogob/postgres-websockets>`_ - expose web sockets for PostgreSQL's LISTEN/NOTIFY
* `postgresql-to-amqp <https://github.com/FGRibreau/postgresql-to-amqp>`_ - AMQP
* `postgresql2websocket <https://github.com/frafra/postgresql2websocket>`_ - Websockets
* `skeeter <https://github.com/SpiderOak/skeeter>`_ - ZeroMQ
.. _eco_extensions:
@@ -88,41 +73,23 @@ Extensions
* `aiodata <https://github.com/Exahilosys/aiodata>`_ - Python, event-based proxy and caching client.
* `pg-safeupdate <https://github.com/eradman/pg-safeupdate>`_ - prevent full-table updates or deletes
* `postgrest-auth (criles25) <https://github.com/criles25/postgrest-auth>`_ - email based auth/signup
* `postgrest-node <https://github.com/seveibar/postgrest-node>`_ - Run a PostgREST server in Node.js via npm module
* `postgrest-oauth <https://github.com/nblumoe/postgrest-oauth>`_ - OAuth2 WAI middleware
* `postgrest-oauth/api <https://github.com/postgrest-oauth/api>`_ - OAuth2 server
* `PostgREST-writeAPI <https://github.com/ppKrauss/PostgREST-writeAPI>`_ - generate Nginx rewrite rules to fit an OpenAPI spec
* `spas <https://github.com/srid/spas>`_ - allow file uploads and basic auth
.. _clientside_libraries:
Client-Side Libraries
---------------------
* `aor-postgrest-client <https://github.com/tomberek/aor-postgrest-client>`_ - JS, admin-on-rest
* `elm-postgrest <https://github.com/john-kelly/elm-postgrest>`_ - Elm
* `general-angular <https://github.com/PierreRochard/general-angular>`_ - TypeScript, generate UI from API description
* `jarvus-postgrest-apikit <https://github.com/JarvusInnovations/jarvus-postgrest-apikit>`_ - JS, Sencha framework
* `mithril-postgrest <https://github.com/catarse/mithril-postgrest>`_ - JS, Mithril
* `ng-postgrest <https://github.com/team142/ng-postgrest>`_ - Angular app for browsing, editing data exposed over PostgREST.
* `postgrest-client <https://github.com/calebmer/postgrest-client>`_ - JS
* `postgrest-csharp <https://github.com/supabase-community/postgrest-csharp>`_ - C#
* `postgrest-dart <https://github.com/supabase-community/postgrest-dart>`_ - Dart
* `postgrest-ex <https://github.com/J0/postgrest-ex>`_ - Elixir
* `postgrest-go <https://github.com/supabase-community/postgrest-go>`_ - Go
* `postgrest-js <https://github.com/supabase/postgrest-js>`_ - TypeScript/JavaScript
* `postgrest-kt <https://github.com/supabase-community/postgrest-kt>`_ - Kotlin
* `postgrest-py <https://github.com/supabase/postgrest-py>`_ - Python
* `postgrest-request <https://github.com/lewisjared/postgrest-request>`_ - JS, SuperAgent
* `postgrest-py <https://github.com/supabase-community/postgrest-py>`_ - Python
* `postgrest-rs <https://github.com/supabase-community/postgrest-rs>`_ - Rust
* `postgrest-sharp-client <https://github.com/thejettdurham/postgrest-sharp-client>`_ (needs maintainer) - C#, RestSharp
* `postgrest-swift <https://github.com/supabase-community/postgrest-swift>`_ - Swift
* `postgrest-url <https://github.com/hugomrdias/postgrest-url>`_ - JS, just for generating query URLs
* `postgrest_python_requests_client <https://github.com/davidthewatson/postgrest_python_requests_client>`_ - Python
* `postgrester <https://github.com/ivangabriele/postgrester>`_ - JS + Typescript
* `postgrestR <https://github.com/clesiemo3/postgrestR>`_ - R
* `py-postgrest <https://github.com/Kong/py-postgrest>`_ - Python
* `redux-postgrest <https://github.com/andytango/redux-postgrest>`_ - TypeScript/JS, client integrated with (React) Redux.
* `vue-postgrest <https://github.com/technowledgy/vue-postgrest>`_ - Vue.js
+198
View File
@@ -0,0 +1,198 @@
.. _db_authz:
Database Authorization
######################
Database authorization is the process of granting and verifying database access permissions. PostgreSQL manages permissions using the concept of roles.
Users and Groups
================
A role can be thought of as either a database user, or a group of database users, depending on how the role is set up.
Roles for Each Web User
-----------------------
PostgREST can accommodate either viewpoint. If you treat a role as a single user then the :ref:`jwt_impersonation` does most of what you need. When an authenticated user makes a request PostgREST will switch into the database role for that user, which in addition to restricting queries, is available to SQL through the :code:`current_user` variable.
You can use row-level security to flexibly restrict visibility and access for the current user. Here is an `example <https://www.2ndquadrant.com/en/blog/application-users-vs-row-level-security/>`_ from Tomas Vondra, a chat table storing messages sent between users. Users can insert rows into it to send messages to other users, and query it to see messages sent to them by other users.
.. code-block:: postgres
CREATE TABLE chat (
message_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
message_time TIMESTAMP NOT NULL DEFAULT now(),
message_from NAME NOT NULL DEFAULT current_user,
message_to NAME NOT NULL,
message_subject VARCHAR(64) NOT NULL,
message_body TEXT
);
ALTER TABLE chat ENABLE ROW LEVEL SECURITY;
We want to enforce a policy that ensures a user can see only those messages sent by them or intended for them. Also we want to prevent a user from forging the ``message_from`` column with another person's name.
PostgreSQL allows us to set this policy with row-level security:
.. code-block:: postgres
CREATE POLICY chat_policy ON chat
USING ((message_to = current_user) OR (message_from = current_user))
WITH CHECK (message_from = current_user)
Anyone accessing the generated API endpoint for the chat table will see exactly the rows they should, without our needing custom imperative server-side coding.
.. warning::
Roles are namespaced per-cluster rather than per-database so they may be prone to collision.
Web Users Sharing Role
----------------------
Alternately database roles can represent groups instead of (or in addition to) individual users. You may choose that all signed-in users for a web app share the role ``webuser``. You can distinguish individual users by including extra claims in the JWT such as email.
.. code:: json
{
"role": "webuser",
"email": "john@doe.com"
}
SQL code can access claims through PostgREST :ref:`tx_settings`. For instance to get the email claim, call this function:
.. code:: sql
current_setting('request.jwt.claims', true)::json->>'email';
.. note::
For PostgreSQL < 14
.. code:: sql
current_setting('request.jwt.claim.email', true);
This allows JWT generation services to include extra information and your database code to react to it. For instance the RLS example could be modified to use this ``current_setting`` rather than ``current_user``. The second ``'true'`` argument tells ``current_setting`` to return NULL if the setting is missing from the current configuration.
Hybrid User-Group Roles
-----------------------
You can mix the group and individual role policies. For instance we could still have a webuser role and individual users which inherit from it:
.. code-block:: postgres
CREATE ROLE webuser NOLOGIN;
-- grant this role access to certain tables etc
CREATE ROLE user000 NOLOGIN;
GRANT webuser TO user000;
-- now user000 can do whatever webuser can
GRANT user000 TO authenticator;
-- allow authenticator to switch into user000 role
-- (the role itself has nologin)
Schemas
=======
You must explicitly allow roles to access the exposed schemas in :ref:`db-schemas`.
.. code-block:: postgres
GRANT USAGE ON SCHEMA api TO webuser;
Tables
======
To let web users access tables you must grant them privileges for the operations you want them to do.
.. code-block:: postgres
GRANT
SELECT
, INSERT
, UPDATE(message_body)
, DELETE
ON chat TO webuser;
You can also choose on which table columns the operation is valid. In the above example, the web user can only update the ``message_body`` column.
.. _func_privs:
Functions
=========
By default, when a function is created, the privilege to execute it is not restricted by role. The function access is ``PUBLIC`` — executable by all roles (more details at `PostgreSQL Privileges page <https://www.postgresql.org/docs/current/ddl-priv.html>`_). This is not ideal for an API schema. To disable this behavior, you can run the following SQL statement:
.. code-block:: postgres
ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;
This will change the privileges for all functions created in the future in all schemas. Currently there is no way to limit it to a single schema. In our opinion it's a good practice anyway.
.. note::
It is however possible to limit the effect of this clause only to functions you define. You can put the above statement at the beginning of the API schema definition, and then at the end reverse it with:
.. code-block:: postgres
ALTER DEFAULT PRIVILEGES GRANT EXECUTE ON FUNCTIONS TO PUBLIC;
This will work because the :code:`alter default privileges` statement has effect on function created *after* it is executed. See `PostgreSQL alter default privileges <https://www.postgresql.org/docs/current/sql-alterdefaultprivileges.html>`_ for more details.
After that, you'll need to grant EXECUTE privileges on functions explicitly:
.. code-block:: postgres
GRANT EXECUTE ON FUNCTION login TO anonymous;
GRANT EXECUTE ON FUNCTION signup TO anonymous;
You can also grant execute on all functions in a schema to a higher privileged role:
.. code-block:: postgres
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA api TO web_user;
Security definer
----------------
A function is executed with the privileges of the user who calls it. This means that the user has to have all permissions to do the operations the procedure performs.
If the function accesses private database objects, your :ref:`API roles <roles>` won't be able to successfully execute the function.
Another option is to define the function with the :code:`SECURITY DEFINER` option. Then only one permission check will take place, the permission to call the function, and the operations in the function will have the authority of the user who owns the function itself.
.. code-block:: postgres
-- login as a user wich has privileges on the private schemas
-- create a sample function
create or replace function login(email text, pass text) returns jwt_token as $$
begin
-- access to a private schema called 'auth'
select auth.user_role(email, pass) into _role;
-- other operations
-- ...
end;
$$ language plpgsql security definer;
Note the ``SECURITY DEFINER`` keywords at the end of the function. See `PostgreSQL documentation <https://www.postgresql.org/docs/current/sql-createfunction.html#SQL-CREATEFUNCTION-SECURITY>`_ for more details.
Views
=====
Views are invoked with the privileges of the view owner, much like stored procedures with the ``SECURITY DEFINER`` option. When created by a SUPERUSER role, all `row-level security <https://www.postgresql.org/docs/current/ddl-rowsecurity.html>`_ policies will be bypassed.
If you're on PostgreSQL >= 15, this behavior can be changed by specifying the ``security_invoker`` option.
.. code-block:: postgres
CREATE VIEW sample_view WITH (security_invoker = true) AS
SELECT * FROM sample_table;
On PostgreSQL < 15, you can create a non-SUPERUSER role and make this role the view's owner.
.. code-block:: postgres
CREATE ROLE api_views_owner NOSUPERUSER NOBYPASSRLS;
ALTER VIEW sample_view OWNER TO api_views_owner;
@@ -1,59 +1,25 @@
.. _install:
Installation
============
############
The release page has `pre-compiled binaries for Mac OS X, Windows, Linux and FreeBSD <https://github.com/PostgREST/postgrest/releases/latest>`_ .
The release page has `pre-compiled binaries for macOS, Windows, Linux and FreeBSD <https://github.com/PostgREST/postgrest/releases/latest>`_ .
The Linux binary is a static executable that can be run on any Linux distribution.
You can also use your OS package manager.
.. tabs::
.. include:: ../shared/installation.rst
.. group-tab:: Mac OSX
.. _pg-dependency:
You can install PostgREST from the `Homebrew official repo <https://formulae.brew.sh/formula/postgrest>`_.
Supported PostgreSQL versions
=============================
.. code:: bash
=============== =================================
**Supported** PostgreSQL >= 9.6
=============== =================================
brew install postgrest
.. group-tab:: FreeBSD
You can install PostgREST from the `official ports <https://www.freshports.org/www/hs-postgrest>`_.
.. code:: bash
pkg install hs-postgrest
.. group-tab:: Linux
.. tabs::
.. tab:: Arch Linux
You can install PostgREST from the `community repo <https://archlinux.org/packages/community/x86_64/postgrest>`_.
.. code:: bash
pacman -S postgrest
.. tab:: Nix
You can install PostgREST from nixpkgs.
.. code:: bash
nix-env -i haskellPackages.postgrest
.. group-tab:: Windows
You can install PostgREST using `Chocolatey <https://community.chocolatey.org/packages/postgrest>`_ or `Scoop <https://github.com/ScoopInstaller/Scoop>`_.
.. code:: bash
choco install postgrest
scoop install postgrest
PostgREST works with all PostgreSQL versions starting from 9.6.
Running PostgREST
=================
@@ -95,19 +61,12 @@ For a complete reference of the configuration file, see :ref:`configuration`.
If you see a dialog box like this on Windows, it may be that the :code:`pg_config` program is not in your system path.
.. image:: _static/win-err-dialog.png
.. image:: ../_static/win-err-dialog.png
It usually lives in :code:`C:\Program Files\PostgreSQL\<version>\bin`. See this `article <https://www.howtogeek.com/118594/how-to-edit-your-system-path-for-easy-command-line-access/>`_ about how to modify the system path.
To test that the system path is set correctly, run ``pg_config`` from the command line. You should see it output a list of paths.
.. _pg-dependency:
PostgreSQL dependency
---------------------
To use PostgREST you will need an underlying database. We require PostgreSQL 9.6 or greater. You can use something like `Amazon RDS <https://aws.amazon.com/rds/>`_ but installing your own locally is cheaper and more convenient for development. You can also run PostgreSQL in a :ref:`docker container<pg-in-docker>`.
Docker
======
@@ -228,7 +187,7 @@ When a pre-built binary does not exist for your system you can build the project
You can build PostgREST from source with `Stack <https://github.com/commercialhaskell/stack>`_. It will install any necessary Haskell dependencies on your system.
* `Install Stack <https://docs.haskellstack.org/en/stable/#how-to-install-stack>`_ for your platform
* `Install Stack <https://docs.haskellstack.org/en/stable/README/#how-to-install-stack>`_ for your platform
* Install Library Dependencies
===================== =======================================
@@ -237,7 +196,7 @@ You can build PostgREST from source with `Stack <https://github.com/commercialha
Ubuntu/Debian libpq-dev, libgmp-dev, zlib1g-dev
CentOS/Fedora/Red Hat postgresql-devel, zlib-devel, gmp-devel
BSD postgresql12-client
OS X libpq, gmp
macOS libpq, gmp
===================== =======================================
* Build and install binary
@@ -256,126 +215,3 @@ You can build PostgREST from source with `Stack <https://github.com/commercialha
- `--install-ghc` flag is only needed for the first build and can be omitted in the subsequent builds.
* Check that the server is installed: :code:`postgrest --help`.
.. _deploy_heroku:
Deploying to Heroku
===================
1. Log into Heroku using the `Heroku CLI <https://devcenter.heroku.com/articles/heroku-cli>`_:
.. code-block:: bash
# If you have multiple Heroku accounts, use flag '--interactive' to switch between them
heroku login --interactive
2. Create a new Heroku app using the PostgREST buildpack:
.. code-block:: bash
mkdir ${YOUR_APP_NAME}
cd ${YOUR_APP_NAME}
git init .
heroku apps:create ${YOUR_APP_NAME} --buildpack https://github.com/PostgREST/postgrest-heroku.git
heroku git:remote -a ${YOUR_APP_NAME}
3. Create a new Heroku PostgreSQL add-on attached to the app and keep notes of the assigned add-on name (e.g. :code:`postgresql-curly-58902`) referred later as ${HEROKU_PG_DB_NAME}
.. code-block:: bash
heroku addons:create heroku-postgresql:standard-0 -a ${YOUR_APP_NAME}
# wait until the add-on is available
heroku pg:wait -a ${YOUR_APP_NAME}
4. Create the necessary user roles according to the
`PostgREST documentation <https://postgrest.org/en/stable/auth.html>`_:
.. code-block:: bash
heroku pg:credentials:create --name api_user -a ${YOUR_APP_NAME}
# use the following command to ensure the new credential state is active before attaching it
heroku pg:credentials -a ${YOUR_APP_NAME}
heroku addons:attach ${HEROKU_PG_DB_NAME} --credential api_user -a ${YOUR_APP_NAME}
5. Connect to the PostgreSQL database and create some sample data:
.. code-block:: bash
heroku psql -a ${YOUR_APP_NAME}
.. code-block:: postgres
# from the psql command prompt execute the following commands:
create schema api;
create table api.todos (
id serial primary key,
done boolean not null default false,
task text not null,
due timestamptz
);
insert into api.todos (task) values
('finish tutorial 0'), ('pat self on back');
grant usage on schema api to api_user;
grant select on api.todos to api_user;
6. Create the :code:`Procfile`:
.. code-block:: bash
web: PGRST_SERVER_HOST=0.0.0.0 PGRST_SERVER_PORT=${PORT} PGRST_DB_URI=${PGRST_DB_URI:-${DATABASE_URL}} ./postgrest-${POSTGREST_VER}
..
Set the following environment variables on Heroku:
.. code-block:: bash
heroku config:set POSTGREST_VER=10.0.0
heroku config:set PGRST_DB_SCHEMA=api
heroku config:set PGRST_DB_ANON_ROLE=api_user
..
PGRST_DB_URI can be set if an external database is used or if it's different from the default Heroku DATABASE_URL. This latter is used if nothing is provided.
POSTGREST_VER is mandatory to select and build the required PostgREST release.
See https://postgrest.org/en/stable/configuration.html#environment-variables for the full list of environment variables.
7. Build and deploy your app:
.. code-block:: bash
git add Procfile
git commit -m "PostgREST on Heroku"
git push heroku master
..
Your Heroku app should be live at :code:`${YOUR_APP_NAME}.herokuapp.com`
8. Test your app
From a terminal display the application logs:
.. code-block:: bash
heroku logs -t
..
From a different terminal retrieve with curl the records previously created:
.. code-block:: bash
curl https://${YOUR_APP_NAME}.herokuapp.com/todos
..
and test that any attempt to modify the table via a read-only user is not allowed:
.. code-block:: bash
curl https://${YOUR_APP_NAME}.herokuapp.com/todos -X POST \
-H "Content-Type: application/json" \
-d '{"task": "do bad thing"}'
+109
View File
@@ -0,0 +1,109 @@
.. _nginx:
Nginx
=====
PostgREST is a fast way to construct a RESTful API. Its default behavior is great for scaffolding in development. When it's time to go to production it works great too, as long as you take precautions.
PostgREST is a small sharp tool that focuses on performing the API-to-database mapping. We rely on a reverse proxy like Nginx for additional safeguards.
The first step is to create an Nginx configuration file that proxies requests to an underlying PostgREST server.
.. code-block:: nginx
http {
# ...
# upstream configuration
upstream postgrest {
server localhost:3000;
}
# ...
server {
# ...
# expose to the outside world
location /api/ {
default_type application/json;
proxy_hide_header Content-Location;
add_header Content-Location /api/$upstream_http_content_location;
proxy_set_header Connection "";
proxy_http_version 1.1;
proxy_pass http://postgrest/;
}
# ...
}
}
.. note::
For ubuntu, if you already installed nginx through :code:`apt` you can add this to the config file in
:code:`/etc/nginx/sites-enabled/default`.
.. _https:
HTTPS
-----
PostgREST aims to do one thing well: add an HTTP interface to a PostgreSQL database. To keep the code small and focused we do not implement HTTPS. Use a reverse proxy such as NGINX to add this, `here's how <https://nginx.org/en/docs/http/configuring_https_servers.html>`_. Note that some Platforms as a Service like Heroku also add SSL automatically in their load balancer.
Rate Limiting
-------------
Nginx supports "leaky bucket" rate limiting (see `official docs <https://nginx.org/en/docs/http/ngx_http_limit_req_module.html>`_). Using standard Nginx configuration, routes can be grouped into *request zones* for rate limiting. For instance we can define a zone for login attempts:
.. code-block:: nginx
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
This creates a shared memory zone called "login" to store a log of IP addresses that access the rate limited urls. The space reserved, 10 MB (:code:`10m`) will give us enough space to store a history of 160k requests. We have chosen to allow only allow one request per second (:code:`1r/s`).
Next we apply the zone to certain routes, like a hypothetical stored procedure called :code:`login`.
.. code-block:: nginx
location /rpc/login/ {
# apply rate limiting
limit_req zone=login burst=5;
}
The burst argument tells Nginx to start dropping requests if more than five queue up from a specific IP.
Nginx rate limiting is general and indiscriminate. To rate limit each authenticated request individually you will need to add logic in a :ref:`Custom Validation <custom_validation>` function.
Alternate URL Structure
-----------------------
As discussed in :ref:`singular_plural`, there are no special URL forms for singular resources in PostgREST, only operators for filtering. Thus there are no URLs like :code:`/people/1`. It would be specified instead as
.. tabs::
.. code-tab:: http
GET /people?id=eq.1 HTTP/1.1
Accept: application/vnd.pgrst.object+json
.. code-tab:: bash Curl
curl "http://localhost:3000/people?id=eq.1" \
-H "Accept: application/vnd.pgrst.object+json"
This allows compound primary keys and makes the intent for singular response independent of a URL convention.
Nginx rewrite rules allow you to simulate the familiar URL convention. The following example adds a rewrite rule for all table endpoints, but you'll want to restrict it to those tables that have a numeric simple primary key named "id."
.. code-block:: nginx
# support /endpoint/:id url style
location ~ ^/([a-z_]+)/([0-9]+) {
# make the response singular
proxy_set_header Accept 'application/vnd.pgrst.object+json';
# assuming an upstream named "postgrest"
proxy_pass http://postgrest/$1?id=eq.$2;
}
.. TODO
.. Administration
.. API Versioning
.. HTTP Caching
.. Upgrading
+15
View File
@@ -0,0 +1,15 @@
.. note::
This page is a work in progress.
.. _schema_isolation:
Schema Isolation
================
A PostgREST instance exposes all the tables, views, and stored procedures of a single `PostgreSQL schema <https://www.postgresql.org/docs/current/ddl-schemas.html>`_ (a namespace of database objects). This means private data or implementation details can go inside different private schemas and be invisible to HTTP clients.
It is recommended that you don't expose tables on your API schema. Instead expose views and stored procedures which insulate the internal details from the outside world.
This allows you to change the internals of your schema and maintain backwards compatibility. It also keeps your code easier to refactor, and provides a natural way to do API versioning.
.. image:: ../_static/db.png
-33
View File
@@ -1,33 +0,0 @@
{ lib
, buildPythonPackage
, fetchFromGitHub
, sphinx
}:
buildPythonPackage rec {
pname = "sphinx-copybutton";
version = "0.4.0";
src = fetchFromGitHub {
owner = "executablebooks";
repo = "sphinx-copybutton";
rev = "v${version}";
sha256 = "sha256-vrEIvQeP7AMXSme1PBp0ox5k8Q1rz+1cbHIO+o17Jqc=";
fetchSubmodules = true;
};
propagatedBuildInputs = [
sphinx
];
doCheck = false; # no tests
pythonImportsCheck = [ "sphinx_copybutton" ];
meta = with lib; {
description = "A small sphinx extension to add a \"copy\" button to code blocks";
homepage = "https://github.com/executablebooks/sphinx-copybutton";
license = licenses.mit;
maintainers = with maintainers; [ Luflosi ];
};
}
-29
View File
@@ -1,29 +0,0 @@
{ lib
, buildPythonPackage
, fetchPypi
, sphinx
}:
buildPythonPackage rec {
pname = "sphinx-tabs";
version = "3.2.0";
src = fetchPypi {
inherit pname version;
sha256 = "sha256:1970aahi6sa7c37cpz8nwgdb2xzf21rk6ykdd1m6w9wvxla7j4rk";
};
propagatedBuildInputs = [
sphinx
];
doCheck = false;
pythonImportsCheck = [ "sphinx_tabs" ];
meta = with lib; {
description = "Create tabbed content in Sphinx documentation when building HTML";
homepage = "https://sphinx-tabs.readthedocs.io";
license = licenses.mit;
};
}
+11 -32
View File
@@ -5,22 +5,20 @@ Create a SOAP endpoint
:author: `fjf2002 <https://github.com/fjf2002>`_
PostgREST now has XML support. With a bit of work, SOAP endpoints become possible.
Please note that PostgREST supports just ``text/xml`` MIME type in request/response headers ``Content-Type`` and ``Accept``.
If you have to use other MIME types such as ``application/soap+xml``, you could manipulate the headers in your reverse proxy.
PostgREST supports :ref:`custom_media`. With a bit of work, SOAP endpoints become possible.
Minimal Example
---------------
This example will simply return the request body, inside a tag ``therequestbodywas``.
Add the following function to your PostgreSQL database:
.. code-block:: postgres
CREATE OR REPLACE FUNCTION my_soap_endpoint(xml) RETURNS xml AS $$
create domain "text/xml" as pg_catalog.xml;
CREATE OR REPLACE FUNCTION my_soap_endpoint(xml) RETURNS "text/xml" AS $$
DECLARE
nsarray CONSTANT text[][] := ARRAY[
ARRAY['soapenv', 'http://schemas.xmlsoap.org/soap/envelope/']
@@ -79,25 +77,6 @@ and should roughly look like:
</soapenv:Body>
</soapenv:Envelope>
Unfortunately the ``Accept: text/xml`` header is currently mandatory concerning PostgREST, otherwise it will respond
with a ``Content-Type: application/json`` header and enclose the response with quotes.
(You can check the returned headers by adding ``-v`` to the curl call.)
If your SOAP clients do not send the ``Accept: text/xml`` header, you can fix that in your nginx reverse proxy
by adding something like ...
.. code-block:: nginx
set $accept $http_accept;
if ($contentType ~ "^text/xml($|;)") {
set $accept "text/xml";
}
proxy_set_header Accept $accept;
to your ``location`` nginx configuration.
(The given example sets the ``Accept`` header for each request of Content-Type ``text/xml``.)
A more elaborate example
------------------------
@@ -121,7 +100,7 @@ potentially disclosing internals to the client, but instead handle the errors di
xmlelement(NAME "soapenv:Body", body)
);
$function$;
-- helper function
CREATE OR REPLACE FUNCTION _soap_exception(
faultcode text,
@@ -137,9 +116,9 @@ potentially disclosing internals to the client, but instead handle the errors di
)
);
$function$;
CREATE OR REPLACE FUNCTION fraction_to_decimal(xml)
RETURNS xml
RETURNS "text/xml"
LANGUAGE plpgsql
AS $function$
DECLARE
@@ -207,14 +186,14 @@ The output should roughly look like:
</soapenv:Body>
</soapenv:Envelope>
References
----------
For more information concerning PostgREST, cf.
- :ref:`s_proc_single_unnamed`
- :ref:`scalar_return_formats`
- :ref:`Nginx reverse proxy <admin>`
- :ref:`custom_media`. See :ref:`any_handler`, if you need to support an ``application/soap+xml`` media type or if you want to respond with XML without sending a media type.
- :ref:`Nginx reverse proxy <nginx>`
For SOAP reference, visit
@@ -0,0 +1,321 @@
.. _providing_html_htmx:
Providing HTML Content Using Htmx
=================================
:author: `Laurence Isla <https://github.com/laurenceisla>`_
This how-to shows a way to return HTML content and use the `htmx library <https://htmx.org/>`_ to handle the AJAX requests.
Htmx expects an HTML response and uses it to replace an element inside the DOM (see the `htmx introduction <https://htmx.org/docs/#introduction>`_ in the docs).
.. image:: ../_static/how-tos/htmx-demo.gif
.. warning::
This is a proof of concept showing what can be achieved using both technologies.
We are working on `plmustache <https://github.com/PostgREST/plmustache>`_ which will further improve the HTML aspect of this how-to.
Preparatory Configuration
-------------------------
We will make a to-do app based on the :ref:`tut0`, so make sure to complete it before continuing.
To simplify things, we won't be using authentication, so grant all permissions on the ``todos`` table to the ``web_anon`` user.
.. code-block:: postgres
grant all on api.todos to web_anon;
grant usage, select on sequence api.todos_id_seq to web_anon;
Next, add the ``text/html`` as a :ref:`custom_media`. With this, PostgREST can identify the request made by your web browser (with the ``Accept: text/html`` header)
and return a raw HTML document file.
.. code-block:: postgres
create domain "text/html" as text;
Creating an HTML Response
-------------------------
Let's create a function that returns a basic HTML file, using `Tailwind CSS <https://v2.tailwindcss.com/>`_ for styling.
.. code-block:: postgres
create or replace function api.index() returns "text/html" as $$
select $html$
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>PostgREST + HTMX To-Do List</title>
<!-- Tailwind for CSS styling -->
<link href="https://unpkg.com/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-900">
<div class="flex justify-center">
<div class="max-w-lg mt-5 p-6 bg-gray-800 border border-gray-800 rounded-lg shadow-xl">
<h5 class="mb-3 text-2xl font-bold tracking-tight text-white">PostgREST + HTMX To-Do List</h5>
</div>
</div>
</body>
</html>
$html$;
$$ language sql;
The web browser will open the web page at ``http://localhost:3000/rpc/index``.
.. image:: ../_static/how-tos/htmx-simple.jpg
.. _html_htmx_list_create:
Listing and Creating To-Dos
---------------------------
Now, let's show a list of the to-dos already inserted in the database.
For that, we'll also need a function to help us sanitize the HTML content that may be present in the task.
.. code-block:: postgres
create or replace function api.sanitize_html(text) returns text as $$
select replace(replace(replace(replace(replace($1, '&', '&amp;'), '"', '&quot;'),'>', '&gt;'),'<', '&lt;'), '''', '&apos;')
$$ language sql;
create or replace function api.html_todo(api.todos) returns text as $$
select format($html$
<li class="py-3">
<span class="ml-2 %2$s">
%3$s
</span>
</li>
$html$,
$1.id,
case when $1.done then 'line-through text-gray-400' else '' end,
api.sanitize_html($1.task)
);
$$ language sql stable;
create or replace function api.html_all_todos() returns text as $$
select coalesce(
'<ul id="todo-list" role="list" class="divide-y divide-gray-700 text-gray-100">'
|| string_agg(api.html_todo(t), '' order by t.id) ||
'</ul>',
'<p class="text-gray-100">There is nothing else to do.</p>'
)
from api.todos t;
$$ language sql;
These two functions are used to build the to-do list template. We won't use them as PostgREST endpoints.
- The ``api.html_todo`` function uses the table ``api.todos`` as a parameter and formats each item into a list element ``<li>``.
The PostgreSQL `format <https://www.postgresql.org/docs/current/functions-string.html#FUNCTIONS-STRING-FORMAT>`_ is useful to that end.
It replaces the values according to the position in the template, e.g. ``%1$s`` will be replaced with the value of ``$1.id`` (the first parameter).
- The ``api.html_all_todos`` function returns the ``<ul>`` wrapper for all the list elements.
It uses `string_arg <https://www.postgresql.org/docs/current/functions-aggregate.html>`_ to concatenate all the to-dos in a single text value.
It also returns an alternative message, instead of a list, when the ``api.todos`` table is empty.
Next, let's add an endpoint to register a to-do in the database and modify the ``/rpc/index`` page accordingly.
.. code-block:: postgres
create or replace function api.add_todo(_task text) returns "text/html" as $$
insert into api.todos(task) values (_task);
select api.html_all_todos();
$$ language sql;
create or replace function api.index() returns "text/html" as $$
select $html$
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>PostgREST + HTMX To-Do List</title>
<!-- Tailwind for CSS styling -->
<link href="https://unpkg.com/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<!-- htmx for AJAX requests -->
<script src="https://unpkg.com/htmx.org"></script>
</head>
<body class="bg-gray-900"
hx-headers='{"Accept": "text/html"}'>
<div class="flex justify-center">
<div class="max-w-lg mt-5 p-6 bg-gray-800 border border-gray-800 rounded-lg shadow-xl">
<h5 class="mb-3 text-2xl font-bold tracking-tight text-white">PostgREST + HTMX To-Do List</h5>
<form hx-post="/rpc/add_todo"
hx-target="#todo-list-area"
hx-trigger="submit"
hx-on="htmx:afterRequest: this.reset()">
<input class="bg-gray-50 border text-sm rounded-lg block w-full p-2.5 mb-3 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"
type="text" name="_task" placeholder="Add a todo...">
</form>
<div id="todo-list-area">
$html$
|| api.html_all_todos() ||
$html$
<div>
</div>
</div>
</body>
</html>
$html$;
$$ language sql;
- The ``/rpc/add_todo`` endpoint allows us to add a new to-do using the ``_task`` parameter and returns an ``html`` with all the to-dos in the database.
- The ``/rpc/index`` now adds the ``hx-headers='{"Accept": "text/html"}'`` tag to the ``<body>``.
This will make sure that all htmx elements inside the body send this header, otherwise PostgREST won't recognize it as HTML.
There is also a ``<form>`` element that uses the htmx library. Let's break it down:
+ ``hx-post="/rpc/add_todo"``: sends an AJAX POST request to the ``/rpc/add_todo`` endpoint, with the value of the ``_task`` from the ``<input>`` element.
+ ``hx-target="#todo-list-area"``: the HTML content returned from the request will go inside ``<div id="todo-list-area"></div>`` (which is the list of to-dos).
+ ``hx-trigger="submit"``: htmx will do this request when submitting the form (by pressing enter while inside the ``<input>``).
+ ``hx-on="htmx:afterRequest: this.reset()">``: this is a Javascript command that clears the form `after the request is done <https://htmx.org/events/#htmx:afterRequest>`_.
With this, the ``http://localhost:3000/rpc/index`` page lists all the todos and adds new ones by submitting tasks in the input element.
Don't forget to refresh the :ref:`schema cache <schema_reloading>`.
.. image:: ../_static/how-tos/htmx-insert.gif
Editing and Deleting To-Dos
---------------------------
Now, let's modify ``api.html_todo`` and make it more functional.
.. code-block:: postgres
create or replace function api.html_todo(api.todos) returns text as $$
select format($html$
<li class="py-3">
<div class="flex justify-between items-center">
<div id="todo-edit-area-%1$s" class="pr-5">
<form id="edit-task-state-%1$s"
hx-post="/rpc/change_todo_state"
hx-vals='{"_id": %1$s, "_done": %4$s}'
hx-target="#todo-list-area"
hx-trigger="click">
<span class="ml-2 %2$s cursor-pointer">
%3$s
</span>
</form>
</div>
<div>
<button class="p-1.5 rounded-full hover:bg-gray-700 focus:ring-gray-800"
hx-get="/rpc/html_editable_task"
hx-vals='{"_id": "%1$s"}'
hx-target="#todo-edit-area-%1$s"
hx-trigger="click">
<svg class="w-4 h-4 text-blue-300" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
<path d="M12.687 14.408a3.01 3.01 0 0 1-1.533.821l-3.566.713a3 3 0 0 1-3.53-3.53l.713-3.566a3.01 3.01 0 0 1 .821-1.533L10.905 2H2.167A2.169 2.169 0 0 0 0 4.167v11.666A2.169 2.169 0 0 0 2.167 18h11.666A2.169 2.169 0 0 0 16 15.833V11.1l-3.313 3.308Zm5.53-9.065.546-.546a2.518 2.518 0 0 0 0-3.56 2.576 2.576 0 0 0-3.559 0l-.547.547 3.56 3.56Z"/>
<path d="M13.243 3.2 7.359 9.081a.5.5 0 0 0-.136.256L6.51 12.9a.5.5 0 0 0 .59.59l3.566-.713a.5.5 0 0 0 .255-.136L16.8 6.757 13.243 3.2Z"/>
</svg>
</button>
<button class="p-1.5 rounded-full hover:bg-gray-700 focus:ring-gray-800"
hx-post="/rpc/delete_todo"
hx-vals='{"_id": %1$s}'
hx-target="#todo-list-area"
hx-trigger="click">
<svg class="w-4 h-4 text-red-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5h16M7 8v8m4-8v8M7 1h4a1 1 0 0 1 1 1v3H6V2a1 1 0 0 1 1-1ZM3 5h12v13a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V5Z"/>
</svg>
</button>
</div>
</div>
</li>
$html$,
$1.id,
case when $1.done then 'line-through text-gray-400' else '' end,
api.sanitize_html($1.task),
(not $1.done)::text
);
$$ language sql stable;
Let's deconstruct the new htmx features added:
- The ``<form>`` element is configured as follows:
+ ``hx-post="/rpc/change_todo_state"``: does an AJAX POST request to that endpoint. It will toggle the ``done`` state of the to-do.
+ ``hx-vals='{"_id": %1$s, "_done": %4$s}'``: adds the parameters to the request.
This is an alternative to using hidden inputs inside the ``<form>``.
+ ``hx-trigger="click"``: htmx does the request after clicking on the element.
- For the first ``<button>``:
+ ``hx-get="/rpc/html_editable_task"``: it does an AJAX GET request to that endpoint.
It returns an HTML with an input that will allow us to edit the task.
+ ``hx-target="#todo-edit-area"``: the returned HTML will replace the element with this id.
In this case, this replaces an individual task, not the whole list.
+ ``hx-vals='{"id": "eq.%1$s"}'``: adds the query parameters to the GET request.
Note that this needs the ``eq.`` operator because it represents a table column not a function parameter.
- For the second ``<button>``:
+ ``hx-post="/rpc/delete_todo"``: this post request will delete the corresponding to-do.
Clicking on the first button will enable the task editing.
That's why we create the ``api.html_editable_task`` function as an endpoint:
.. code-block:: postgres
create or replace function api.html_editable_task(_id int) returns "text/html" as $$
select format ($html$
<form id="edit-task-%1$s"
hx-post="/rpc/change_todo_task"
hx-headers='{"Accept": "text/html"}'
hx-vals='{"_id": %1$s}'
hx-target="#todo-list-area"
hx-trigger="submit,focusout">
<input class="bg-gray-50 border text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 text-white focus:ring-blue-500 focus:border-blue-500"
id="task-%1$s" type="text" name="_task" value="%2$s" autofocus>
</form>
$html$,
id,
api.sanitize_html(task)
)
from api.todos
where id = _id;
$$ language sql;
In this example, this will return an input field that allows us to edit the corresponding to-do task.
Finally, let's add the endpoints that will modify and delete the to-dos in the database.
.. code-block:: postgres
create or replace function api.change_todo_state(_id int, _done boolean) returns "text/html" as $$
update api.todos set done = _done where id = _id;
select api.html_all_todos();
$$ language sql;
create or replace function api.change_todo_task(_id int, _task text) returns "text/html" as $$
update api.todos set task = _task where id = _id;
select api.html_all_todos();
$$ language sql;
create or replace function api.delete_todo(_id int) returns "text/html" as $$
delete from api.todos where id = _id;
select api.html_all_todos();
$$ language sql;
All of those functions return an HTML list of to-dos that will replace the outdated one:
- The ``api.change_todo_state`` function updates the ``done`` column using the ``_id`` and the ``_done`` values from the request.
- The ``api.delete_todo`` function deletes a to-do using the ``_id`` value from the request.
- The ``api.change_todo_task`` function modifies the ``task`` column using the ``_id`` and the ``_task`` value from the request.
After refreshing the :ref:`schema cache <schema_reloading>`, the page at ``http://localhost:3000/rpc/index`` will allow us to edit, delete and complete any to-do.
.. image:: ../_static/how-tos/htmx-edit-delete.gif
With that, we completed the to-do list functionality.
+36 -10
View File
@@ -26,18 +26,42 @@ First, we need a public table for storing the files.
, blob bytea
);
Let's assume this table contains an image of two cute kittens with id 42.
We can retrieve this image in binary format from our PostgREST API by requesting :code:`/files?select=blob&id=eq.42` with the :code:`Accept: application/octet-stream` header.
Unfortunately, putting the URL into the :code:`src` of an :code:`<img>` tag will not work.
That's because browsers do not send the required :code:`Accept: application/octet-stream` header.
Let's assume this table contains an image of two cute kittens with id 42. We can retrieve this image in binary format from our PostgREST API by using :ref:`custom_media`:
.. code-block:: postgres
create domain "application/octet-stream" as bytea;
create or replace function file(id int) returns "application/octet-stream" as $$
select blob from files where id = file.id;
$$ language sql;
Now we can request the RPC endpoint :code:`/rpc/file?id=42` with the :code:`Accept: application/octet-stream` header.
.. code-block:: bash
curl "localhost:3000/rpc/file?id=42" -H "Accept: application/octet-stream"
Unfortunately, putting the URL into the :code:`src` of an :code:`<img>` tag will not work. That's because browsers do not send the required :code:`Accept: application/octet-stream` header.
Instead, the :code:`Accept: image/webp` header is sent by many web browsers by default.
Luckily we can change the accepted media type in the function like so:
.. code-block:: postgres
create domain "image/webp" as bytea;
create or replace function file(id int) returns "image/webp" as $$
select blob from files where id = file.id;
$$ language sql;
Luckily we can specify the accepted media types in the :ref:`raw-media-types` configuration variable.
In this case, the :code:`Accept: image/webp` header is sent by many web browsers by default, so let's add it to the configuration variable, like this: :code:`raw-media-types="image/webp"`.
Now, the image will be displayed in the HTML page:
.. code-block:: html
<img src="http://localhost:3000/files?select=blob&id=eq.42" alt="Cute Kittens"/>
<img src="http://localhost:3000/file?id=42" alt="Cute Kittens"/>
Improved Version
----------------
@@ -60,13 +84,15 @@ First, in addition to the minimal example, we need to store the media types and
add column type text,
add column name text;
Next, we set up an RPC endpoint that sets the content type and filename.
Next, we set modify the function to set the content type and filename.
We use this opportunity to configure some basic, client-side caching.
For production, you probably want to configure additional caches, e.g. on the :ref:`reverse proxy <admin>`.
.. code-block:: postgres
create function file(id int) returns bytea as
create domain "*/*" as bytea;
create function file(id int) returns "*/*" as
$$
declare headers text;
declare blob bytea;
@@ -79,7 +105,7 @@ For production, you probably want to configure additional caches, e.g. on the :r
from files where files.id = file.id into headers;
perform set_config('response.headers', headers, true);
select files.blob from files where files.id = file.id into blob;
if found
if FOUND -- special var, see https://www.postgresql.org/docs/current/plpgsql-statements.html#PLPGSQL-STATEMENTS-DIAGNOSTICS
then return(blob);
else raise sqlstate 'PT404' using
message = 'NOT FOUND',
@@ -59,7 +59,7 @@ In order to be able to work with postgres' SCRAM-SHA-256 password hashes, we als
CREATE FUNCTION basic_auth.pbkdf2(salt bytea, pw text, count integer, desired_length integer, algorithm text) RETURNS bytea
LANGUAGE plpgsql IMMUTABLE
AS $$
DECLARE
DECLARE
hash_length integer;
block_count integer;
output bytea;
@@ -97,7 +97,7 @@ In order to be able to work with postgres' SCRAM-SHA-256 password hashes, we als
--
FOR j IN 2 .. count LOOP
the_last := ext_pgcrypto.HMAC(the_last, pw::bytea, algorithm);
-- xor the two
FOR k IN 1 .. length(xorsum) LOOP
xorsum := set_byte(xorsum, k - 1, get_byte(xorsum, k - 1) # get_byte(the_last, k - 1));
@@ -210,8 +210,6 @@ anonymous roles. Below is an example of permissions that allow anonymous users t
.. code-block:: postgres
-- the names "anon" and "authenticator" are configurable and not
-- sacred, we simply choose them for clarity
CREATE ROLE anon NOINHERIT;
CREATE role authenticator NOINHERIT LOGIN PASSWORD 'secret';
GRANT anon TO authenticator;
@@ -299,7 +297,7 @@ Let's add a table, intended for the :code:`foo` user:
Now try to get the table's contents with:
.. tabs::
.. code-tab:: http
GET /foobar HTTP/1.1
+228
View File
@@ -0,0 +1,228 @@
.. _sql_user_management:
SQL User Management
===================
As mentioned on :ref:`jwt_generation`, an external service can provide user management and coordinate with the PostgREST server using JWT. Its also possible to support logins entirely through SQL. Its a fair bit of work, so get ready.
Storing Users and Passwords
---------------------------
The following table, functions, and triggers will live in a :code:`basic_auth` schema that you shouldn't expose publicly in the API. The public views and functions will live in a different schema which internally references this internal information.
First we'll need a table to keep track of our users:
.. code:: sql
-- We put things inside the basic_auth schema to hide
-- them from public view. Certain public procs/views will
-- refer to helpers and tables inside.
create schema if not exists basic_auth;
create table if not exists
basic_auth.users (
email text primary key check ( email ~* '^.+@.+\..+$' ),
pass text not null check (length(pass) < 512),
role name not null check (length(role) < 512)
);
We would like the role to be a foreign key to actual database roles, however PostgreSQL does not support these constraints against the :code:`pg_roles` table. We'll use a trigger to manually enforce it.
.. code-block:: plpgsql
create or replace function
basic_auth.check_role_exists() returns trigger as $$
begin
if not exists (select 1 from pg_roles as r where r.rolname = new.role) then
raise foreign_key_violation using message =
'unknown database role: ' || new.role;
return null;
end if;
return new;
end
$$ language plpgsql;
drop trigger if exists ensure_user_role_exists on basic_auth.users;
create constraint trigger ensure_user_role_exists
after insert or update on basic_auth.users
for each row
execute procedure basic_auth.check_role_exists();
Next we'll use the pgcrypto extension and a trigger to keep passwords safe in the :code:`users` table.
.. code-block:: plpgsql
create extension if not exists pgcrypto;
create or replace function
basic_auth.encrypt_pass() returns trigger as $$
begin
if tg_op = 'INSERT' or new.pass <> old.pass then
new.pass = crypt(new.pass, gen_salt('bf'));
end if;
return new;
end
$$ language plpgsql;
drop trigger if exists encrypt_pass on basic_auth.users;
create trigger encrypt_pass
before insert or update on basic_auth.users
for each row
execute procedure basic_auth.encrypt_pass();
With the table in place we can make a helper to check a password against the encrypted column. It returns the database role for a user if the email and password are correct.
.. code-block:: plpgsql
create or replace function
basic_auth.user_role(email text, pass text) returns name
language plpgsql
as $$
begin
return (
select role from basic_auth.users
where users.email = user_role.email
and users.pass = crypt(user_role.pass, users.pass)
);
end;
$$;
.. _public_ui:
Public User Interface
---------------------
In the previous section we created an internal table to store user information. Here we create a login function which takes an email address and password and returns JWT if the credentials match a user in the internal table.
Permissions
~~~~~~~~~~~
Your database roles need access to the schema, tables, views and functions in order to service HTTP requests.
Recall from the :ref:`roles` that PostgREST uses special roles to process requests, namely the authenticator and
anonymous roles. Below is an example of permissions that allow anonymous users to create accounts and attempt to log in.
.. code-block:: postgres
create role anon noinherit;
create role authenticator noinherit;
grant anon to authenticator;
Then, add ``db-anon-role`` to the configuration file to allow anonymous requests.
.. code:: ini
db-anon-role = "anon"
JWT from SQL
~~~~~~~~~~~~
You can create JWT tokens in SQL using the `pgjwt extension <https://github.com/michelp/pgjwt>`_. It's simple and requires only pgcrypto. If you're on an environment like Amazon RDS which doesn't support installing new extensions, you can still manually run the `SQL inside pgjwt <https://github.com/michelp/pgjwt/blob/master/pgjwt--0.1.1.sql>`_ (you'll need to replace ``@extschema@`` with another schema or just delete it) which creates the functions you will need.
Next write a stored procedure that returns the token. The one below returns a token with a hard-coded role, which expires five minutes after it was issued. Note this function has a hard-coded secret as well.
.. code-block:: postgres
CREATE TYPE jwt_token AS (
token text
);
CREATE FUNCTION jwt_test() RETURNS public.jwt_token AS $$
SELECT public.sign(
row_to_json(r), 'reallyreallyreallyreallyverysafe'
) AS token
FROM (
SELECT
'my_role'::text as role,
extract(epoch from now())::integer + 300 AS exp
) r;
$$ LANGUAGE sql;
PostgREST exposes this function to clients via a POST request to ``/rpc/jwt_test``.
.. note::
To avoid hard-coding the secret in stored procedures, save it as a property of the database.
.. code-block:: postgres
-- run this once
ALTER DATABASE mydb SET "app.jwt_secret" TO 'reallyreallyreallyreallyverysafe';
-- then all functions can refer to app.jwt_secret
SELECT sign(
row_to_json(r), current_setting('app.jwt_secret')
) AS token
FROM ...
Logins
~~~~~~
As described in `JWT from SQL`_, we'll create a JWT inside our login function. Note that you'll need to adjust the secret key which is hard-coded in this example to a secure (at least thirty-two character) secret of your choosing.
.. code-block:: postgres
-- add type
CREATE TYPE basic_auth.jwt_token AS (
token text
);
-- login should be on your exposed schema
create or replace function
login(email text, pass text) returns basic_auth.jwt_token as $$
declare
_role name;
result basic_auth.jwt_token;
begin
-- check email and password
select basic_auth.user_role(email, pass) into _role;
if _role is null then
raise invalid_password using message = 'invalid user or password';
end if;
select sign(
row_to_json(r), 'reallyreallyreallyreallyverysafe'
) as token
from (
select _role as role, login.email as email,
extract(epoch from now())::integer + 60*60 as exp
) r
into result;
return result;
end;
$$ language plpgsql security definer;
grant execute on function login(text,text) to anon;
Since the above :code:`login` function is defined as `security definer <https://www.postgresql.org/docs/current/sql-createfunction.html#id-1.9.3.67.10.2>`_,
the anonymous user :code:`anon` doesn't need permission to read the :code:`basic_auth.users` table. It doesn't even need permission to access the :code:`basic_auth` schema.
:code:`grant execute on function` is included for clarity but it might not be needed, see :ref:`func_privs` for more details.
An API request to call this function would look like:
.. tabs::
.. code-tab:: http
POST /rpc/login HTTP/1.1
{ "email": "foo@bar.com", "pass": "foobar" }
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/login" \
-X POST -H "Content-Type: application/json" \
-d '{ "email": "foo@bar.com", "pass": "foobar" }'
The response would look like the snippet below. Try decoding the token at `jwt.io <https://jwt.io/>`_. (It was encoded with a secret of :code:`reallyreallyreallyreallyverysafe` as specified in the SQL code above. You'll want to change this secret in your app!)
.. code:: json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImZvb0BiYXIuY29tIiwicGFzcyI6ImZvb2JhciJ9.37066TTRlh-1hXhnA9oO9Pj6lgL6zFuJU0iCHhuCFno"
}
Alternatives
~~~~~~~~~~~~
See the how-to :ref:`sql-user-management-using-postgres-users-and-passwords` for a similar way that completely avoids the table :code:`basic_auth.users`.
@@ -62,7 +62,7 @@ Someone located in Cairo can retrieve the data using their local time, too:
}
]
The response has the date in the time zone configured by the server: ``UTC -05:00``.
The response has the date in the time zone configured by the server: ``UTC -05:00`` (see :ref:`prefer_timezone`).
You can use other comparative filters and also all the `PostgreSQL special date/time input values <https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-DATETIME-SPECIAL-TABLE>`_ as illustrated in this example:
@@ -506,33 +506,26 @@ Now, to send the file ``postgrest-logo.png`` we need to set the ``Content-Type:
-X POST -H "Content-Type: application/octet-stream" \
--data-binary "@postgrest-logo.png"
To get the image from the database, set the ``Accept: application/octet-stream`` header and select only the
``bytea`` type column.
To get the image from the database, use :ref:`custom_media` like so:
.. code-block:: postgres
create domain "image/png" as bytea;
create or replace get_image(id int) returns "image/png" as $$
select file from files where id = $1;
$$ language sql;
.. tabs::
.. code-tab:: http
GET /files?select=file&id=eq.1 HTTP/1.1
Accept: application/octet-stream
.. code-tab:: bash Curl
curl "http://localhost:3000/files?select=file&id=eq.1" \
-H "Accept: application/octet-stream"
Use more accurate headers according to the type of the files by using the :ref:`raw-media-types` configuration. For example, adding the ``raw-media-types="image/png"`` setting to the configuration file will allow you to use the ``Accept: image/png`` header:
.. tabs::
.. code-tab:: http
GET /files?select=file&id=eq.1 HTTP/1.1
GET /get_image?id=1 HTTP/1.1
Accept: image/png
.. code-tab:: bash Curl
curl "http://localhost:3000/files?select=file&id=eq.1" \
curl "http://localhost:3000/get_image?id=1" \
-H "Accept: image/png"
See :ref:`providing_img` for a step-by-step example on how to handle images in HTML.
+78 -122
View File
@@ -16,9 +16,15 @@ PostgREST Documentation
.. image:: https://img.shields.io/docker/pulls/postgrest/postgrest.svg
:target: https://hub.docker.com/r/postgrest/postgrest/
.. image:: https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg
:target: https://gitter.im/begriffs/postgrest
.. image:: https://img.shields.io/badge/Donate-Patreon-orange.svg?colorB=F96854
:target: https://www.patreon.com/postgrest
.. image:: https://img.shields.io/badge/Donate-PayPal-green.svg
:target: https://www.paypal.com/paypalme/postgrest
|
PostgREST is a standalone web server that turns your PostgreSQL database directly into a RESTful API. The structural constraints and permissions in the database determine the API endpoints and operations.
@@ -32,24 +38,26 @@ Sponsors
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
:width: 13em
.. image:: _static/2ndquadrant.png
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
:width: 13em
.. image:: _static/retool.png
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
.. image:: _static/gnuhost.png
:target: https://euronodes.com/?utm_source=sponsor&utm_campaign=postgrest
:target: https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
.. image:: _static/neon.jpg
:target: https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
|
.. image:: _static/code-build.webp
:target: https://code.build/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
.. image:: _static/supabase.png
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
:width: 13em
.. image:: _static/oblivious.jpg
:target: https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest
.. image:: _static/tembo.png
:target: https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
.. The static/empty.png(created with `convert -size 320x95 xc:#fcfcfc empty.png`) is an ugly workaround
@@ -61,15 +69,15 @@ Sponsors
|
Motivation
----------
Database as Single Source of Truth
----------------------------------
Using PostgREST is an alternative to manual CRUD programming. Custom API servers suffer problems. Writing business logic often duplicates, ignores or hobbles database structure. Object-relational mapping is a leaky abstraction leading to slow imperative code. The PostgREST philosophy establishes a single declarative source of truth: the data itself.
Declarative Programming
-----------------------
It's easier to ask PostgreSQL to join data for you and let its query planner figure out the details than to loop through rows yourself. It's easier to assign permissions to db objects than to add guards in controllers. (This is especially true for cascading permissions in data dependencies.) It's easier to set constraints than to litter code with sanity checks.
It's easier to ask PostgreSQL to join data for you and let its query planner figure out the details than to loop through rows yourself. It's easier to assign permissions to database objects than to add guards in controllers. (This is especially true for cascading permissions in data dependencies.) It's easier to set constraints than to litter code with sanity checks.
Leak-proof Abstraction
----------------------
@@ -84,23 +92,12 @@ PostgREST has a focused scope. It works well with other tools like Nginx. This f
Getting Support
----------------
The project has a friendly and growing community. For discussions, use the Github `discussions page <https://github.com/PostgREST/postgrest/discussions>`_. You can also report or search for bugs/features on the Github `issues <https://github.com/PostgREST/postgrest/issues>`_ page.
The project has a friendly and growing community. For discussions, use the Github `discussions page <https://github.com/PostgREST/postgrest/discussions>`_ or join our `chat room <https://gitter.im/begriffs/postgrest>`_. You can also report or search for bugs/features on the Github `issues <https://github.com/PostgREST/postgrest/issues>`_ page.
.. toctree::
:glob:
:caption: Release Notes
:titlesonly:
:hidden:
Release Notes
-------------
v10.2.0 <releases/v10.2.0>
v10.0.0 <releases/v10.0.0>
v9.0.1 <releases/v9.0.1>
v9.0.0 <releases/v9.0.0>
releases/v8.0.0
releases/v7.0.1
releases/v7.0.0
releases/v6.0.2
releases/v5.2.0
The release notes are published on `PostgREST's GitHub release page <https://github.com/PostgREST/postgrest/releases>`_.
Tutorials
---------
@@ -110,104 +107,72 @@ Are you new to PostgREST? This is the place to start!
.. toctree::
:glob:
:caption: Tutorials
:hidden:
:maxdepth: 1
tutorials/*
- :doc:`tutorials/tut0`
- :doc:`tutorials/tut1`
Also have a look at :ref:`install` and :ref:`community_tutorials`.
Also have a look at :doc:`Installation <install>` and :ref:`community_tutorials`.
Reference guides
----------------
References
----------
Technical references for PostgREST's functionality.
.. toctree::
:caption: API
:hidden:
:glob:
:caption: References
:name: references
:maxdepth: 1
api.rst
references/auth.rst
references/api.rst
references/transactions.rst
references/connection_pool.rst
references/schema_cache.rst
references/errors.rst
references/configuration.rst
references/*
.. toctree::
:caption: Configuration
:hidden:
configuration.rst
.. toctree::
:caption: Schema Cache
:hidden:
schema_cache.rst
.. toctree::
:caption: Errors
:hidden:
errors.rst
- :doc:`API <api>`
- :doc:`configuration`
- :doc:`Schema Cache <schema_cache>`
- :doc:`Errors <errors>`
Topic guides
Explanations
------------
Explanations of some key concepts in PostgREST.
Key concepts in PostgREST.
.. toctree::
:caption: Authentication
:hidden:
:glob:
:caption: Explanations
:name: explanations
:maxdepth: 1
auth.rst
explanations/*
.. toctree::
:caption: Schema Structure
:hidden:
How-tos
-------
schema_structure.rst
.. toctree::
:caption: Administration
:hidden:
admin.rst
.. toctree::
:caption: Installation
:hidden:
install.rst
- :doc:`Authentication <auth>`
- :doc:`Schema Structure <schema_structure>`
- :doc:`Administration <admin>`
- :doc:`Installation <install>`
.. _how_tos:
How-to guides
-------------
These are recipes that'll help you address specific use-cases.
Recipes that'll help you address specific use-cases.
.. toctree::
:glob:
:caption: How-to guides
:hidden:
:name: how-tos
:maxdepth: 1
how-tos/working-with-postgresql-data-types
how-tos/providing-images-for-img
how-tos/create-soap-endpoint
how-tos/sql-user-management-using-postgres-users-and-passwords
how-tos/sql-user-*
how-tos/working-*
how-tos/*
- :doc:`how-tos/providing-images-for-img`
- :doc:`how-tos/working-with-postgresql-data-types`
- :doc:`how-tos/create-soap-endpoint`
- :doc:`how-tos/sql-user-management-using-postgres-users-and-passwords`
.. _intgrs:
Integrations
------------
.. toctree::
:glob:
:caption: Integrations
:name: integrations
:maxdepth: 1
integrations/*
Ecosystem
---------
@@ -216,27 +181,11 @@ PostgREST has a growing ecosystem of examples, libraries, and experiments. Here
.. toctree::
:caption: Ecosystem
:hidden:
:name: ecosystem
:maxdepth: 1
ecosystem.rst
* :ref:`community_tutorials`
* :ref:`templates`
* :ref:`eco_example_apps`
* :ref:`devops`
* :ref:`eco_external_notification`
* :ref:`eco_extensions`
* :ref:`clientside_libraries`
Release Notes
-------------
Changes among versions.
- :doc:`releases/v9.0.0`
- :doc:`releases/v8.0.0`
In Production
-------------
@@ -246,12 +195,19 @@ Here are some companies that use PostgREST in production.
* `Datrium <https://www.datrium.com>`_
* `Drip Depot <https://www.dripdepot.com>`_
* `Image-charts <https://www.image-charts.com>`_
* `Moat <https://www.moat.com>`_
* `Netwo <https://www.netwo.io>`_
* `Nimbus <https://www.nimbusforwork.com>`_
- See how Nimbus uses PostgREST in `Paul Copplestone's blog post <https://paul.copplest.one/blog/nimbus-tech-2019-04.html>`_.
* `OpenBooking <https://www.openbooking.ch>`_
* `Redsmin <https://www.redsmin.com>`_
* `Sompani <https://www.sompani.com>`_
* `Supabase <https://supabase.com>`_
.. Failing links
* `eGull <http://www.egull.co>`_
* `MotionDynamic - Fast highly dynamic video generation at scale <https://motiondynamic.tech>`_
Testimonials
------------
+6
View File
@@ -0,0 +1,6 @@
Greenplum
#########
`Greenplum <https://greenplum.org/>`_ has been reported to work by adding ``LOGIN`` to the :ref:`anonymous and user roles <roles>`.
For more details, see https://github.com/PostgREST/postgrest/issues/2021.
+122
View File
@@ -0,0 +1,122 @@
.. _deploy_heroku:
Heroku
======
1. Log into Heroku using the `Heroku CLI <https://devcenter.heroku.com/articles/heroku-cli>`_:
.. code-block:: bash
# If you have multiple Heroku accounts, use flag '--interactive' to switch between them
heroku login --interactive
2. Create a new Heroku app using the PostgREST buildpack:
.. code-block:: bash
mkdir ${YOUR_APP_NAME}
cd ${YOUR_APP_NAME}
git init .
heroku apps:create ${YOUR_APP_NAME} --buildpack https://github.com/PostgREST/postgrest-heroku.git
heroku git:remote -a ${YOUR_APP_NAME}
3. Create a new Heroku PostgreSQL add-on attached to the app and keep notes of the assigned add-on name (e.g. :code:`postgresql-curly-58902`) referred later as ${HEROKU_PG_DB_NAME}
.. code-block:: bash
heroku addons:create heroku-postgresql:standard-0 -a ${YOUR_APP_NAME}
# wait until the add-on is available
heroku pg:wait -a ${YOUR_APP_NAME}
4. Create the necessary user roles according to the
`PostgREST documentation <https://postgrest.org/en/stable/auth.html>`_:
.. code-block:: bash
heroku pg:credentials:create --name api_user -a ${YOUR_APP_NAME}
# use the following command to ensure the new credential state is active before attaching it
heroku pg:credentials -a ${YOUR_APP_NAME}
heroku addons:attach ${HEROKU_PG_DB_NAME} --credential api_user -a ${YOUR_APP_NAME}
5. Connect to the PostgreSQL database and create some sample data:
.. code-block:: bash
heroku psql -a ${YOUR_APP_NAME}
.. code-block:: postgres
# from the psql command prompt execute the following commands:
create schema api;
create table api.todos (
id serial primary key,
done boolean not null default false,
task text not null,
due timestamptz
);
insert into api.todos (task) values
('finish tutorial 0'), ('pat self on back');
grant usage on schema api to api_user;
grant select on api.todos to api_user;
6. Create the :code:`Procfile`:
.. code-block:: bash
web: PGRST_SERVER_HOST=0.0.0.0 PGRST_SERVER_PORT=${PORT} PGRST_DB_URI=${PGRST_DB_URI:-${DATABASE_URL}} ./postgrest-${POSTGREST_VER}
..
Set the following environment variables on Heroku:
.. code-block:: bash
heroku config:set POSTGREST_VER=10.0.0
heroku config:set PGRST_DB_SCHEMA=api
heroku config:set PGRST_DB_ANON_ROLE=api_user
..
PGRST_DB_URI can be set if an external database is used or if it's different from the default Heroku DATABASE_URL. This latter is used if nothing is provided.
POSTGREST_VER is mandatory to select and build the required PostgREST release.
See https://postgrest.org/en/stable/configuration.html#environment-variables for the full list of environment variables.
7. Build and deploy your app:
.. code-block:: bash
git add Procfile
git commit -m "PostgREST on Heroku"
git push heroku master
..
Your Heroku app should be live at :code:`${YOUR_APP_NAME}.herokuapp.com`
8. Test your app
From a terminal display the application logs:
.. code-block:: bash
heroku logs -t
..
From a different terminal retrieve with curl the records previously created:
.. code-block:: bash
curl https://${YOUR_APP_NAME}.herokuapp.com/todos
..
and test that any attempt to modify the table via a read-only user is not allowed:
.. code-block:: bash
curl https://${YOUR_APP_NAME}.herokuapp.com/todos -X POST \
-H "Content-Type: application/json" \
-d '{"task": "do bad thing"}'
+31
View File
@@ -0,0 +1,31 @@
.. _external_jwt:
External JWT Generation
-----------------------
JWT from Auth0
~~~~~~~~~~~~~~
An external service like `Auth0 <https://auth0.com/>`_ can do the hard work transforming OAuth from Github, Twitter, Google etc into a JWT suitable for PostgREST. Auth0 can also handle email signup and password reset flows.
To use Auth0, create `an application <https://auth0.com/docs/get-started/applications>`_ for your app and `an API <https://auth0.com/docs/get-started/apis>`_ for your PostgREST server. Auth0 supports both HS256 and RS256 scheme for the issued tokens for APIs. For simplicity, you may first try HS256 scheme while creating your API on Auth0. Your application should use your PostgREST API's `API identifier <https://auth0.com/docs/get-started/apis/api-settings>`_ by setting it with the `audience parameter <https://auth0.com/docs/secure/tokens/access-tokens/get-access-tokens#control-access-token-audience>`_ during the authorization request. This will ensure that Auth0 will issue an access token for your PostgREST API. For PostgREST to verify the access token, you will need to set ``jwt-secret`` on PostgREST config file with your API's signing secret.
.. note::
Our code requires a database role in the JWT. To add it you need to save the database role in Auth0 `app metadata <https://auth0.com/docs/manage-users/user-accounts/metadata/manage-metadata-rules>`_. Then, you will need to write `a rule <https://auth0.com/docs/customize/rules>`_ that will extract the role from the user's app_metadata and set it as a `custom claim <https://auth0.com/docs/get-started/apis/scopes/sample-use-cases-scopes-and-claims#add-custom-claims-to-a-token>`_ in the access token. Note that, you may use Auth0's `core authorization feature <https://auth0.com/docs/manage-users/access-control/rbac>`_ for more complex use cases. Metadata solution is mentioned here for simplicity.
.. code:: javascript
function (user, context, callback) {
// Follow the documentations at
// https://postgrest.org/en/latest/configuration.html#db-role-claim-key
// to set a custom role claim on PostgREST
// and use it as custom claim attribute in this rule
const myRoleClaim = 'https://myapp.com/role';
user.app_metadata = user.app_metadata || {};
context.accessToken[myRoleClaim] = user.app_metadata.role;
callback(null, user, context);
}
+42
View File
@@ -0,0 +1,42 @@
pg-safeupdate
#############
.. _block_fulltable:
Block Full-Table Operations
---------------------------
If the :ref:`active role <user_impersonation>` can delete table rows then the DELETE verb is allowed for clients. Here's an API request to delete old rows from a hypothetical logs table:
.. tabs::
.. code-tab:: http
DELETE /logs?time=lt.1991-08-06 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/logs?time=lt.1991-08-06" -X DELETE
Note that it's very easy to delete the **entire table** by omitting the query parameter!
.. tabs::
.. code-tab:: http
DELETE /logs HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/logs" -X DELETE
This can happen accidentally such as by switching a request from a GET to a DELETE. To protect against accidental operations use the `pg-safeupdate <https://github.com/eradman/pg-safeupdate>`_ PostgreSQL extension. It raises an error if UPDATE or DELETE are executed without specifying conditions. To install it you can use the `PGXN <https://pgxn.org/>`_ network:
.. code-block:: bash
sudo -E pgxn install safeupdate
# then add this to postgresql.conf:
# shared_preload_libraries='safeupdate';
This does not protect against malicious actions, since someone can add a url parameter that does not affect the result set. To prevent this you must turn to database permissions, forbidding the wrong people from deleting rows, and using `row-level security <https://www.postgresql.org/docs/current/ddl-rowsecurity.html>`_ if finer access control is required.
+59
View File
@@ -0,0 +1,59 @@
systemd
=======
For Linux distributions that use **systemd** (Ubuntu, Debian, Archlinux) you can create a daemon in the following way.
First, create postgrest configuration in ``/etc/postgrest/config``
.. code-block:: ini
db-uri = "postgres://<your_user>:<your_password>@localhost:5432/<your_db>"
db-schemas = "<your_exposed_schema>"
db-anon-role = "<your_anon_role>"
jwt-secret = "<your_secret>"
Create a dedicated ``postgrest`` user with:
.. code-block:: ini
sudo useradd -M -U -d /nonexistent -s /usr/sbin/nologin postgrest
Then create the systemd service file in ``/etc/systemd/system/postgrest.service``
.. code-block:: ini
[Unit]
Description=REST API for any PostgreSQL database
After=postgresql.service
[Service]
User=postgrest
Group=postgrest
ExecStart=/bin/postgrest /etc/postgrest/config
ExecReload=/bin/kill -SIGUSR1 $MAINPID
[Install]
WantedBy=multi-user.target
After that, you can enable the service at boot time and start it with:
.. code-block:: bash
systemctl enable postgrest
systemctl start postgrest
## For reloading the service
## systemctl restart postgrest
.. _file_descriptors:
File Descriptors
----------------
File descriptors are kernel resources that are used by HTTP connections (among others). File descriptors are limited per process. The kernel default limit is 1024, which is increased in some Linux distributions.
When under heavy traffic, PostgREST can reach this limit and start showing ``No file descriptors available`` errors. To clear these errors, you can increase the process' file descriptor limit.
.. code-block:: ini
[Service]
LimitNOFILE=10000
+35 -53
View File
@@ -1,8 +1,7 @@
personal_ws-1.1 en 0 utf-8
Adossi
AMQP
api
API's
APISIX
Archlinux
aud
Auth
@@ -10,104 +9,98 @@ auth
authenticator
backoff
balancer
Beles
booleans
Bouscal
buildpack
BOM
Bytea
Cardano
casted
cd
centric
changelog
ClojureScript
cloudfared
coercible
conf
Cloudflare
config
cors
CORS
CPUs
cryptographically
CSV
Daemonizing
durations
DDL
DOM
DevOps
DiBiase
dockerize
DoS
eq
ETH
Ethereum
EveryLayout
Fenko
Fernandes
filename
FreeBSD
fts
GC
GeoJSON
GHC
Github
Google
grantor
GraphQL
Greenplum
gte
GUC
GUCs
gucs
Gumbs
Haskell
Heroku
HMAC
htmx
Htmx
Homebrew
hstore
HTTP
HTTPS
HV
Ibarluzea
Inlining
inlined
Integrations
idletime
IDLETIME
ilike
imatch
io
IP
isdistinct
JS
js
JSON
JWK
JWT
jwt
JWTs
Kinesis
Kofi
Kubernetes
localhost
login
lookups
Logins
LIBPQ
logins
lon
lt
lte
middleware
macOS
misprediction
Mithril
multi
MVCC
namespace
namespaced
Nanos
neq
nginx
ngrep
nixpkgs
npm
nxl
nxr
OAuth
onwards
OpenAPI
openapi
ORM
ov
passphrase
Pawel
PBKDF
Pelletier
Petr
PgBouncer
pgcrypto
pgjwt
@@ -121,7 +114,6 @@ phraseto
plainto
plfts
poolers
POSIX
PostGIS
PostgreSQL
PostgreSQL's
@@ -131,79 +123,69 @@ postgrest
PostgREST's
pre
preflight
plpgsql
psql
Qin
RabbitMQ
Rafaj
RDS
reallyreallyreallyreallyverysafe
Rechkemmer
reconnection
Redux
refactor
reloadable
Reloadable
Remo
requester's
RESTful
RestSharp
RLS
RPC
RSA
Saleeba
safeupdate
savepoint
schemas
Sencha
Serverless
Severin
schema's
SHA
signup
SIGUSR
sl
SNS
sqitch
spreaded
Spreaded
SQL
sql
sr
SSL
stateful
stdout
Stolarz
subselect
SuperAgent
supervisees
SvelteKit
SwaggerUI
syslog
systemd
Tcl
tmuxp
todo
todos
Tsingson
tos
tsquery
tx
Tyll
TypeScript
UI
ui
unicode
unikernel
unix
updatable
unfulfillable
Untyped
UPSERT
Upsert
upsert
uri
url
urlencoded
urls
variadic
Vercel
verifier
versioning
Vondra
Vue
WAI
webhooks
websearch
Websockets
webuser
wfts
ZeroMQ
www
+317
View File
@@ -0,0 +1,317 @@
.. _admin:
Admin
#####
.. _pgrst_logging:
Logging
-------
PostgREST logs basic request information to ``stdout``, including the authenticated user if available, the requesting IP address and user agent, the URL requested, and HTTP response status.
.. code::
127.0.0.1 - user [26/Jul/2021:01:56:38 -0500] "GET /clients HTTP/1.1" 200 - "" "curl/7.64.0"
127.0.0.1 - anonymous [26/Jul/2021:01:56:48 -0500] "GET /unexistent HTTP/1.1" 404 - "" "curl/7.64.0"
For diagnostic information about the server itself, PostgREST logs to ``stderr``.
.. code::
12/Jun/2021:17:47:39 -0500: Starting PostgREST 11.1.0...
12/Jun/2021:17:47:39 -0500: Attempting to connect to the database...
12/Jun/2021:17:47:39 -0500: Listening on port 3000
12/Jun/2021:17:47:39 -0500: Connection successful
12/Jun/2021:17:47:39 -0500: Config re-loaded
12/Jun/2021:17:47:40 -0500: Schema cache loaded
.. note::
When running it in an SSH session you must detach it from stdout or it will be terminated when the session closes. The easiest technique is redirecting the output to a log file or to the syslog:
.. code-block:: bash
ssh foo@example.com \
'postgrest foo.conf </dev/null >/var/log/postgrest.log 2>&1 &'
# another option is to pipe the output into "logger -t postgrest"
Currently PostgREST doesn't log the SQL commands executed against the underlying database.
Database Logs
~~~~~~~~~~~~~
To find the SQL operations, you can watch the database logs. By default PostgreSQL does not keep these logs, so you'll need to make the configuration changes below.
Find :code:`postgresql.conf` inside your PostgreSQL data directory (to find that, issue the command :code:`show data_directory;`). Either find the settings scattered throughout the file and change them to the following values, or append this block of code to the end of the configuration file.
.. code:: sql
# send logs where the collector can access them
log_destination = "stderr"
# collect stderr output to log files
logging_collector = on
# save logs in pg_log/ under the pg data directory
log_directory = "pg_log"
# (optional) new log file per day
log_filename = "postgresql-%Y-%m-%d.log"
# log every kind of SQL statement
log_statement = "all"
Restart the database and watch the log file in real-time to understand how HTTP requests are being translated into SQL commands.
.. note::
On Docker you can enable the logs by using a custom ``init.sh``:
.. code:: bash
#!/bin/sh
echo "log_statement = 'all'" >> /var/lib/postgresql/data/postgresql.conf
After that you can start the container and check the logs with ``docker logs``.
.. code:: bash
docker run -v "$(pwd)/init.sh":"/docker-entrypoint-initdb.d/init.sh" -d postgres
docker logs -f <container-id>
Server Version
--------------
When debugging a problem it's important to verify the running PostgREST version. There are three ways to do this:
- Look for the :code:`Server` HTTP response header that is returned on every request.
.. code::
HEAD /users HTTP/1.1
Server: postgrest/11.0.1
- Query ``application_name`` on `pg_stat_activity <https://www.postgresql.org/docs/current/monitoring-stats.html#MONITORING-PG-STAT-ACTIVITY-VIEW>`_.
.. code-block:: psql
select distinct application_name
from pg_stat_activity
where application_name ilike '%postgrest%';
application_name
------------------------------
PostgREST 11.1.0
.. important::
- The server sets the `fallback_application_name <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-FALLBACK-APPLICATION-NAME>`_ to the connection URI for this query to work. To override the value set ``application_name`` on the connection string.
- The version will only be set if it's a valid URI (`RFC 3986 <https://datatracker.ietf.org/doc/html/rfc3986>`_). This means any special characters must be urlencoded.
- The version will not be set if the connection string is in `keyword/value format <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING-KEYWORD-VALUE>`_.
- The ``stderr`` logs also contain the version, as noted on :ref:`pgrst_logging`.
.. _trace_header:
Trace Header
------------
You can enable tracing HTTP requests by setting :ref:`server-trace-header`. Specify the set header in the request, and the server will include it in the response.
.. code:: bash
server-trace-header = "X-Request-Id"
.. tabs::
.. code-tab:: http
GET /users HTTP/1.1
X-Request-Id: 123
.. code-tab:: bash Curl
curl "http://localhost:3000/users" \
-H "X-Request-Id: 123"
.. code::
HTTP/1.1 200 OK
X-Request-Id: 123
.. _server-timing_header:
Server-Timing Header
--------------------
You can enable the `Server-Timing <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing>`_ header by setting :ref:`server-timing-enabled` on.
This header communicates metrics of the different phases in the request-response cycle.
.. tabs::
.. code-tab:: http
GET /users HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/users" -i
.. code::
HTTP/1.1 200 OK
Server-Timing: jwt;dur=14.9, parse;dur=71.1, plan;dur=109.0, transaction;dur=353.2, response;dur=4.4
- All the durations (``dur``) are in milliseconds.
- The ``jwt`` stage is when :ref:`jwt_impersonation` is done. This duration can be lowered with :ref:`jwt_caching`.
- On the ``parse`` stage, the :ref:`url_grammar` is parsed.
- On the ``plan`` stage, the :ref:`schema_cache` is used to generate the :ref:`main_query` of the transaction.
- The ``transaction`` stage corresponds to the database transaction. See :ref:`transactions`.
- The ``response`` stage is where the response status and headers are computed.
.. note::
We're working on lowering the duration of the ``parse`` and ``plan`` stages on https://github.com/PostgREST/postgrest/issues/2816.
.. _explain_plan:
Execution plan
--------------
You can get the `EXPLAIN execution plan <https://www.postgresql.org/docs/current/sql-explain.html>`_ of a request by adding the ``Accept: application/vnd.pgrst.plan`` header.
This is enabled by :ref:`db-plan-enabled` (false by default).
.. tabs::
.. code-tab:: http
GET /users?select=name&order=id HTTP/1.1
Accept: application/vnd.pgrst.plan
.. code-tab:: bash Curl
curl "http://localhost:3000/users?select=name&order=id" \
-H "Accept: application/vnd.pgrst.plan"
.. code-block:: psql
Aggregate (cost=73.65..73.68 rows=1 width=112)
-> Index Scan using users_pkey on users (cost=0.15..60.90 rows=850 width=36)
The output of the plan is generated in ``text`` format by default but you can change it to JSON by using the ``+json`` suffix.
.. tabs::
.. code-tab:: http
GET /users?select=name&order=id HTTP/1.1
Accept: application/vnd.pgrst.plan+json
.. code-tab:: bash Curl
curl "http://localhost:3000/users?select=name&order=id" \
-H "Accept: application/vnd.pgrst.plan+json"
.. code-block:: json
[
{
"Plan": {
"Node Type": "Aggregate",
"Strategy": "Plain",
"Partial Mode": "Simple",
"Parallel Aware": false,
"Async Capable": false,
"Startup Cost": 73.65,
"Total Cost": 73.68,
"Plan Rows": 1,
"Plan Width": 112,
"Plans": [
{
"Node Type": "Index Scan",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Async Capable": false,
"Scan Direction": "Forward",
"Index Name": "users_pkey",
"Relation Name": "users",
"Alias": "users",
"Startup Cost": 0.15,
"Total Cost": 60.90,
"Plan Rows": 850,
"Plan Width": 36
}
]
}
}
]
By default the plan is assumed to generate the JSON representation of a resource(``application/json``), but you can obtain the plan for the :ref:`different representations that PostgREST supports <res_format>` by adding them to the ``for`` parameter. For instance, to obtain the plan for a ``text/xml``, you would use ``Accept: application/vnd.pgrst.plan; for="text/xml``.
The other available parameters are ``analyze``, ``verbose``, ``settings``, ``buffers`` and ``wal``, which correspond to the `EXPLAIN command options <https://www.postgresql.org/docs/current/sql-explain.html>`_. To use the ``analyze`` and ``wal`` parameters for example, you would add them like ``Accept: application/vnd.pgrst.plan; options=analyze|wal``.
Note that akin to the EXPLAIN command, the changes will be committed when using the ``analyze`` option. To avoid this, you can use the :ref:`db-tx-end` and the ``Prefer: tx=rollback`` header.
Securing the Execution Plan
~~~~~~~~~~~~~~~~~~~~~~~~~~~
It's recommended to only activate :ref:`db-plan-enabled` on testing environments since it reveals internal database details.
However, if you choose to use it in production you can add a :ref:`db-pre-request` to filter the requests that can use this feature.
For example, to only allow requests from an IP address to get the execution plans:
.. code-block:: postgresql
-- Assuming a proxy(Nginx, Cloudflare, etc) passes an "X-Forwarded-For" header(https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)
create or replace function filter_plan_requests()
returns void as $$
declare
headers json := current_setting('request.headers', true)::json;
client_ip text := coalesce(headers->>'x-forwarded-for', '');
accept text := coalesce(headers->>'accept', '');
begin
if accept like 'application/vnd.pgrst.plan%' and client_ip != '144.96.121.73' then
raise insufficient_privilege using
message = 'Not allowed to use application/vnd.pgrst.plan';
end if;
end; $$ language plpgsql;
-- set this function on your postgrest.conf
-- db-pre-request = filter_plan_requests
.. _health_check:
Health Check
------------
You can enable a health check to verify if PostgREST is available for client requests. Also to check the status of its internal state.
To do this, set the configuration variable :ref:`admin-server-port` to the port number of your preference. Two endpoints ``live`` and ``ready`` will then be available.
The ``live`` endpoint verifies if PostgREST is running on its configured port. A request will return ``200 OK`` if PostgREST is alive or ``503`` otherwise.
The ``ready`` endpoint also checks the state of both the Database Connection and the :ref:`schema_cache`. A request will return ``200 OK`` if it is ready or ``503`` if not.
For instance, to verify if PostgREST is running at ``localhost:3000`` while the ``admin-server-port`` is set to ``3001``:
.. tabs::
.. code-tab:: http
GET localhost:3001/live HTTP/1.1
.. code-tab:: bash Curl
curl -I "http://localhost:3001/live"
.. code-block:: http
HTTP/1.1 200 OK
If you have a machine with multiple network interfaces and multiple PostgREST instances in the same port, you need to specify a unique :ref:`hostname <server-host>` in the configuration of each PostgREST instance for the health check to work correctly. Don't use the special values(``!4``, ``*``, etc) in this case because the health check could report a false positive.
+124
View File
@@ -0,0 +1,124 @@
.. _api:
API
###
PostgREST exposes three database objects of a schema as resources: tables, views and stored procedures.
.. toctree::
:glob:
:maxdepth: 1
api/tables_views.rst
api/stored_procedures.rst
api/schemas.rst
api/computed_fields.rst
api/domain_representations.rst
api/pagination_count.rst
api/resource_embedding.rst
api/resource_representation.rst
api/media_type_handlers.rst
api/aggregate_functions.rst
api/openapi.rst
api/preferences.rst
api/*
.. raw:: html
<script type="text/javascript">
let hash = window.location.hash;
const redirects = {
// Tables and Views
'#horizontal-filtering-rows': 'api/tables_views.html#horizontal-filtering-rows',
'#operators': 'api/tables_views.html#operators',
'#logical-operators': 'api/tables_views.html#logical-operators',
'#pattern-matching': 'api/tables_views.html#pattern-matching',
'#full-text-search': 'api/tables_views.html#full-text-search',
'#vertical-filtering-columns': 'api/tables_views.html#vertical-filtering-columns',
'#renaming-columns': 'api/tables_views.html#renaming-columns',
'#casting-columns': 'api/tables_views.html#casting-columns',
'#json-columns': 'api/tables_views.html#json-columns',
'#composite-array-columns': 'api/tables_views.html#composite-array-columns',
'#computed-virtual-columns': 'api/computed_fields.html#computed-fields',
'#ordering': 'api/tables_views.html#ordering',
'#limits-and-pagination': 'api/tables_views.html#limits-and-pagination',
'#exact-count': 'api/tables_views.html#exact-count',
'#planned-count': 'api/tables_views.html#planned-count',
'#estimated-count': 'api/tables_views.html#estimated-count',
'#updates': 'api/tables_views.html#update',
'#insertions': 'api/tables_views.html#insert',
'#bulk-insert': 'api/tables_views.html#bulk-insert',
'#specifying-columns': 'api/tables_views.html#specifying-columns',
'#upsert': 'api/tables_views.html#upsert',
'#on-conflict': 'api/tables_views.html#on-conflict',
'#put': 'api/tables_views.html#put',
'#deletions': 'api/tables_views.html#delete',
'#limited-updates-deletions': 'api/tables_views.html#limited-update-delete',
// Stored procedures
'#stored-procedures': 'api/stored_procedures.html#stored-procedures',
'#calling-functions-with-a-single-json-parameter': 'api/stored_procedures.html#functions-with-a-single-json-parameter',
'#calling-functions-with-a-single-unnamed-parameter': 'api/stored_procedures.html#functions-with-a-single-unnamed-parameter',
'#calling-functions-with-array-parameters': 'api/stored_procedures.html#functions-with-array-parameters',
'#calling-variadic-functions': 'api/stored_procedures.html#variadic-functions',
'#scalar-functions': 'api/stored_procedures.html#scalar-functions',
'#function-filters': 'api/stored_procedures.html#table-valued-functions',
'#overloaded-functions': 'api/stored_procedures.html#overloaded-functions',
// Schemas
'#switching-schemas': 'api/schemas.html',
// Resource Embedding
'#resource-embedding': 'api/resource_embedding.html#resource-embedding',
'#many-to-one-relationships': 'api/resource_embedding.html#many-to-one-relationships',
'#one-to-many-relationships': 'api/resource_embedding.html#one-to-many-relationships',
'#many-to-many-relationships': 'api/resource_embedding.html#many-to-many-relationships',
'#one-to-one-relationships': 'api/resource_embedding.html#one-to-one-relationships',
'#computed-relationships': 'api/resource_embedding.html#computed-relationships',
'#nested-embedding': 'api/resource_embedding.html#nested-embedding',
'#embedded-filters': 'api/resource_embedding.html#embedded-filters',
'#embedding-with-top-level-filtering': 'api/resource_embedding.html#top-level-filtering',
'#embedding-partitioned-tables': 'api/resource_embedding.html#embedding-partitioned-tables',
'#embedding-views': 'api/resource_embedding.html#embedding-views',
'#embedding-chains-of-views': 'api/resource_embedding.html#embedding-chains-of-views',
'#embedding-on-stored-procedures': 'api/resource_embedding.html#embedding-on-stored-procedures',
'#embedding-after-insertions-updates-deletions': 'api/resource_embedding.html#embedding-after-insertions-updates-deletions',
'#embedding-disambiguation': 'api/resource_embedding.html#embedding-disambiguation',
'#target-disambiguation': 'api/resource_embedding.html#target-disambiguation',
'#hint-disambiguation': 'api/resource_embedding.html#hint-disambiguation',
"#embedding-through-join-tables": "api/resource_embedding.html#many-to-many-relationships",
// OpenAPI
'#openapi-support': 'api/openapi.html',
// Resource Representation
'#response-format': 'api/resource_representation.html#response-format',
'#singular-or-plural': 'api/resource_representation.html#singular-or-plural',
'#response-formats-for-scalar-responses': 'api/resource_representation.html#scalar-function-response-format',
// CORS
'#cors': 'api/cors.html',
// OPTIONS
'#options': 'api/options.html',
// URL Grammar
'#custom-queries': 'api/url_grammar.html#custom-queries',
'#unicode-support': 'api/url_grammar.html#unicode-support',
'#table-columns-with-spaces': 'api/url_grammar.html#table-columns-with-spaces',
'#reserved-characters': 'api/url_grammar.html#reserved-characters',
// Transactions
'#immutable-and-stable-functions': 'transactions.html#access-mode',
'#http-context': 'transactions.html#transaction-scoped-settings',
'#accessing-request-headers-cookies-and-jwt-claims': 'transactions.html#request-headers-cookies-and-jwt-claims',
'#legacy-guc-variable-names': 'transactions.html#transaction-scoped-settings',
'#accessing-request-path-and-method': 'transactions.html#request-path-and-method',
'#setting-response-headers': 'transactions.html#response-headers',
'#setting-headers-via-pre-request': 'transactions.html#setting-headers-via-pre-request',
'#setting-response-status-code': 'transactions.html#response-status-code',
'#raise-errors-with-http-status-codes': 'transactions.html#raise-errors-with-http-status-codes',
// Admin
'#execution-plan': 'admin.html#execution-plan',
// Deprecated
'#bulk-call': '../releases/v11.0.1.html#breaking-changes',
};
let willRedirectTo = redirects[hash];
if (willRedirectTo) {
window.location.href = willRedirectTo;
}
</script>
+342
View File
@@ -0,0 +1,342 @@
.. _aggregate_functions:
Aggregate Functions
###################
Aggregate functions allow you to summarize data by performing calculations across groups of rows. For instance, if you have an ``orders`` table that has an ``amount`` column, you could use an aggregate function to get the sum of the ``amount`` column, either for all rows, or for each group of rows that share specific values, for instance all rows that share the same ``order_date``.
.. note::
Aggregate functions are *disabled* by default in PostgREST, as without appropriate safeguards, aggregate functions can create performance problems. See :ref:`db-aggregates-enabled` for further details.
PostgREST supports the following aggregate functions: ``avg()``, ``count()``, ``max()``, ``min()``, and ``sum()``. Please refer to the `section on aggregate functions in the PostgreSQL documentation <https://www.postgresql.org/docs/current/functions-aggregate.html>`_ for a detailed explanation of these functions.
To use an aggregate function, you append the function to a value in the ``select`` parameter, like so:
.. tabs::
.. code-tab:: http
GET /orders?select=amount.sum() HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/orders?select=amount.sum()"
With the above query, PostgREST will return a single row with a single column named ``sum`` that contains the sum of all the values in the ``amount`` column:
.. code-block:: json
[
{
"sum": 1234.56
}
]
You can use multiple aggregate functions by just adding more columns with aggregate functions to the ``select`` parameter.
To group by other columns, you simply add those columns to the ``select`` parameter. For instance:
.. tabs::
.. code-tab:: http
GET /orders?select=amount.sum(),amount.avg(),order_date HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/orders?select=amount.sum(),amount.avg(),order_date"
This will return a row for each unique value in the ``order_date`` column, with the sum and average of the ``amount`` column for all rows that share the same ``order_date``:
.. code-block:: json
[
{
"sum": 1234.56,
"avg": 123.45,
"order_date": "2023-01-01"
},
{
"sum": 2345.67,
"avg": 234.56,
"order_date": "2023-01-02"
}
]
.. note::
Aggregate functions work alongside other PostgREST features, like :ref:`h_filter`, :ref:`json_columns`, and :ref:`ordering`. Please note at this time aggregate functions are not compatible with :ref:`domain_reps`. Additionally, PostgreSQL's ``HAVING`` clause and ordering by aggregated columns are not yet supported.
The Case of ``count()``
===========================
.. note::
Before the addition of aggregate functions, it was possible to count by adding ``count`` (without parentheses) to the ``select`` parameter. While this is still supported, it may be deprecated in the future, and thus use of this legacy feature is **not recommended.** Please use ``count()`` (with parentheses) instead.
``count()`` is treated specially, as it can be used without an associated column. Take for example the following query:
.. tabs::
.. code-tab:: http
GET /orders?select=count(),order_date HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/orders?select=count(),order_date"
This would return a row for each unique value in the ``order_date`` column, with the count of all rows that share the same ``order_date``:
.. code-block:: json
[
{
"count": 4,
"order_date": "2023-01-01"
},
{
"count": 2,
"order_date": "2023-01-02"
}
]
When ``count()`` is used with an associated column, its behaviour is slightly different: It will return the count of all values that are not ``NULL``. This is due to how PostgreSQL itself implements the ``count()`` function.
Renaming and Casting
====================
Renaming Aggregates
-------------------
Just like with other columns, you can rename aggregated columns too. See :ref:`renaming_columns` for details.
Renaming columns is especially helpful in the context of aggregate functions, as by default a column with an aggregate function applied will take on the name of the applied aggregate function. You may want to provide a more semantically meaningful name or prevent collisions when using multiple aggregate functions of the same type.
Casting Aggregates
------------------
When applying an aggregate function to a column, you are able to cast both the value of the input to the aggregate function *and* the value of the output from the aggregate function. In both cases, the syntax works as described in :ref:`casting_columns`, with the only difference being the placement of the cast.
Casting the Value of the Input
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For instance, imagine that the ``orders`` table has a JSON column, ``order_details``, and this column contains a JSON object that has a key, ``tax_amount``. Let's say you want to get the sum of the tax amount for every order. You can use the ``->`` or ``->>`` operators to extract the value with this key (see :ref:`json_columns`), but these operators will return values of the types JSON and ``text`` respectively, and neither of these types can be used with ``sum()``.
Therefore, you will need to first cast the input value to a type that is compatible with ``sum()`` (e.g. ``numeric``). Casting the input value is done in exactly the same way as casting any other value:
.. tabs::
.. code-tab:: http
GET /orders?select=order_details->tax_amount::numeric.sum() HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/orders?select=order_details->tax_amount::numeric.sum()"
With this, you will receive the sum of the casted ``tax_amount`` value:
.. code-block:: json
[
{
"sum": 1234.56
}
]
Casting the Value of the Output
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now let's return to an example involving the ``amount`` column of the ``orders`` table. Imagine that we want to get the rounded average of the ``amount`` column. One way to do this is to use the ``avg()`` aggregate function and then to cast the output value of the function to ``int``. To cast the value of the output of the function, we simply place the cast *after* the aggregate function:
.. tabs::
.. code-tab:: http
GET /orders?select=amount.avg()::int HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/orders?select=amount.avg()::int"
You will then receive the rounded average as the result:
.. code-block:: json
[
{
"avg": 201
}
]
Of course, you can use both input and output casts at the same time, if you so desire.
Using Aggregate Functions with Resource Embedding
=================================================
Aggregate functions can be used in conjunction with :ref:`resource_embedding`. You can use embedded resources as grouping columns, use aggregate functions within the context of an embedded resource, or use columns from a spreaded resource as grouping columns or as inputs to aggregate functions.
Using Embedded Resources as Grouping Columns
--------------------------------------------
Using an embedded resource as a grouping column allows you to use data from an association to group the results of an aggregation.
For example, imagine that the ``orders`` table from the examples above is related to a ``customers`` table. If you want to get the sum of the ``amount`` column grouped by the ``name`` column from the ``customers`` table, you can include the customer name, using the standard :ref:`resource_embedding` syntax, and perform a sum on the ``amount`` column.
.. tabs::
.. code-tab:: http
GET /orders?select=amount.sum(),customers(name) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/orders?select=amount.sum(),customers(name)"
You will then get the summed amount, along with the embedded customer resource:
.. code-block:: json
[
{
"sum": 100,
"customers": {
"name": "Customer A"
}
},
{
"sum": 200,
"customers": {
"name": "Customer B"
}
}
]
.. note::
The previous example uses a has-one association to demonstrate this functionality, but you may also use has-many associations as grouping columns, although there are few obvious use cases for this.
Using Aggregate Functions Within the Context of an Embedded Resource
--------------------------------------------------------------------
When embedding a resource, you can apply aggregate functions to columns from the associated resource to perform aggregations within the context of an embedded resource.
Continuing with the example relationship between ``orders`` and ``customers`` from the previous section, imagine that you want to fetch the ``name``, ``city``, and ``state`` for each customer, along with the sum of amount of the customer's orders, grouped by the order date. This can be done in the following way:
.. tabs::
.. code-tab:: http
GET /customers?select=name,city,state,orders(amount.sum(),order_date) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/customers?select=name,city,state,orders(amount.sum(),order_date)"
.. code-block:: json
[
{
"name": "Customer A",
"city": "New York",
"state": "NY",
"orders": [
{
"sum": 215.22,
"order_date": "2023-09-01"
},
{
"sum": 905.73,
"order_date": "2023-09-02"
}
]
},
{
"name": "Customer B",
"city": "Los Angeles",
"state": "CA",
"orders": [
{
"sum": 329.71,
"order_date": "2023-09-01"
},
{
"sum": 425.87,
"order_date": "2023-09-03"
}
]
}
]
In this example, the ``amount`` column is summed and grouped by the ``order_date`` *within* the context of the embedded resource. That is, the ``name``, ``city``, and ``state`` from the ``customers`` table have no bearing on the aggregation performed in the context of the ``orders`` association; instead, each aggregation can be seen as being performed independently on just the orders belonging to a particular customer, using only the data from the embedded resource for both grouping and aggregation.
Using Columns from a Spreaded Resource
--------------------------------------
When you :ref:`spread an embedded resource <spread_embed>`, the columns from the spreaded resource are treated as if they were columns of the top-level resource, both when using them as grouping columns and when applying aggregate functions to them.
Grouping with Columns from a Spreaded Resource
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For instance, assume you want to sum the ``amount`` column from the ``orders`` table, using the ``city`` and ``state`` columns from the ``customers`` table as grouping columns. To achieve this, you may select these two columns from the ``customers`` table and spread them; they will then be used as grouping columns:
.. tabs::
.. code-tab:: http
GET /orders?select=amount.sum(),...customers(city,state) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/orders?select=amount.sum(),...customers(city,state)
The result will be the same as if ``city`` and ``state`` were columns from the ``orders`` table:
.. code-block:: json
[
{
"sum": 2000.29,
"city": "New York",
"state": "NY"
},
{
"sum": 9241.21,
"city": "Los Angeles",
"state": "CA"
}
]
Aggregate Functions with Columns from a Spreaded Resource
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now imagine that the ``customers`` table has a ``joined_date`` column that represents the date that the customer joined. You want to get both the most recent and the oldest ``joined_date`` for customers that placed an order on every distinct order date. This can be expressed as follows:
.. tabs::
.. code-tab:: http
GET /orders?select=order_date,...customers(joined_date.max(),joined_date.min()) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/orders?select=order_date,...customers(joined_date.max(),joined_date.min())
As columns from a spreaded resource are treated as if they were columns from the top-level resource, the ``max()`` and ``min()`` are applied *within* the context of the top-level, rather than within the context of the embedded resource, as in the previous section.
The result will be the same as if the aggregations were applied to columns from the top-level:
.. code-block:: json
[
{
"order_date": "2023-11-01",
"max": "2023-10-15",
"min": "2013-10-01"
},
{
"order_date": "2023-11-02",
"max": "2023-10-30",
"min": "2016-02-11"
}
]
+93
View File
@@ -0,0 +1,93 @@
.. _computed_cols:
Computed Fields
###############
Computed fields are virtual columns that are not stored in a table. PostgreSQL makes it possible to implement them using functions on table types.
.. code-block:: postgres
CREATE TABLE people (
first_name text
, last_name text
, job text
);
-- a computed field that combines data from two columns
CREATE FUNCTION full_name(people)
RETURNS text AS $$
SELECT $1.first_name || ' ' || $1.last_name;
$$ LANGUAGE SQL;
Horizontal Filtering on Computed Fields
=======================================
:ref:`h_filter` can be applied to computed fields. For example, we can do a :ref:`fts` on :code:`full_name`:
.. code-block:: postgres
-- (optional) you can add an index on the computed field to speed up the query
CREATE INDEX people_full_name_idx ON people
USING GIN (to_tsvector('english', full_name(people)));
.. tabs::
.. code-tab:: http
GET /people?full_name=fts.Beckett HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?full_name=fts.Beckett"
.. code-block:: json
[
{"first_name": "Samuel", "last_name": "Beckett", "job": "novelist"}
]
Vertical Filtering on Computed Fields
=====================================
Computed fields won't appear on the response by default but you can use :ref:`v_filter` to include them:
.. tabs::
.. code-tab:: http
GET /people?select=full_name,job HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?select=full_name,job"
.. code-block:: json
[
{"full_name": "Samuel Beckett", "job": "novelist"}
]
Ordering on Computed Fields
===========================
:ref:`ordering` on computed fields is also possible:
.. tabs::
.. code-tab:: http
GET /people?order=full_name.desc HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?order=full_name.desc"
.. important::
Computed columns must be created in the :ref:`exposed schema <db-schemas>` or in a schema in the :ref:`extra search path <db-extra-search-path>` to be used in this way. When placing the computed column in the :ref:`exposed schema <db-schemas>` you can use an **unnamed** parameter, as in the example above, to prevent it from being exposed as an :ref:`RPC <s_procs>` under ``/rpc``.
.. note::
- PostgreSQL 12 introduced `generated columns <https://www.postgresql.org/docs/12/ddl-generated-columns.html>`_, which can also compute a value based on other columns. However they're stored, not virtual.
- "computed fields" are documented on https://www.postgresql.org/docs/current/rowtypes.html#ROWTYPES-USAGE (search for "computed fields")
- On previous PostgREST versions this feature was documented with the name of "computed columns".
+50
View File
@@ -0,0 +1,50 @@
.. _cors:
CORS
####
By default, PostgREST sets highly permissive cross origin resource sharing, that is why it accepts Ajax requests from any domain. This behavior can be configured by using :ref:`server_cors_allowed_origins`.
It also handles `preflight requests <https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request>`_ done by the browser, which are cached using the returned ``Access-Control-Max-Age: 86400`` header (86400 seconds = 24 hours). This is useful to reduce the latency of the subsequent requests.
A ``POST`` preflight request would look like this:
.. tabs::
.. code-tab:: http
OPTIONS /items HTTP/1.1
Origin: http://example.com
Access-Control-Allow-Method: POST
Access-Control-Allow-Headers: Content-Type
.. code-tab:: bash Curl
curl -i "http://localhost:3000/items" \
-X OPTIONS \
-H "Origin: http://example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type"
.. code-block:: http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS, HEAD
Access-Control-Allow-Headers: Authorization, Content-Type, Accept, Accept-Language, Content-Language
Access-Control-Max-Age: 86400
.. _allowed_origins:
Allowed Origins
===============
With the following config setting, PostgREST will accept CORS requests from domains :code:`http://example.com` and :code:`http://example2.com`.
.. code-block::
server-cors-allowed-origins="http://example.com, http://example2.com"
@@ -0,0 +1,200 @@
.. _domain_reps:
Domain Representations
######################
Domain Representations separates "how the data is presented" from "how the data is stored". It works by creating `domains <https://www.postgresql.org/docs/current/sql-createdomain.html>`_ and `casts <https://www.postgresql.org/docs/current/sql-createcast.html>`_, the latter act on the former to present and receive the data in different formats.
.. contents::
:depth: 1
:local:
:backlinks: none
Custom Domain
=============
Suppose you want to use a ``uuid`` type for a primary key and want to present it shortened to web users.
For this, let's create a domain based on ``uuid``.
.. code-block:: postgres
create domain app_uuid as uuid;
-- and use it as our table PK.
create table profiles(
id app_uuid
, name text
);
-- some data for the example
insert into profiles values ('846c4ffd-92ce-4de7-8d11-8e29929f4ec4', 'John Doe');
Domain Response Format
======================
We can shorten the ``uuid`` with ``base64`` encoding. Let's use JSON as our response format for this example.
To change the domain format for JSON, create a function that converts ``app_uuid`` to ``json``.
.. code-block:: postgres
-- the name of the function is arbitrary
CREATE OR REPLACE FUNCTION json(app_uuid) RETURNS json AS $$
select to_json(encode(uuid_send($1),'base64'));
$$ LANGUAGE SQL IMMUTABLE;
-- check it works
select json('846c4ffd-92ce-4de7-8d11-8e29929f4ec4'::app_uuid);
json
----------------------------
"hGxP/ZLOTeeNEY4pkp9OxA=="
Then create a CAST to tell PostgREST to convert it automatically whenever a JSON response is requested.
.. code-block:: postgres
CREATE CAST (app_uuid AS json) WITH FUNCTION json(app_uuid) AS IMPLICIT;
With this you can obtain the data in the shortened format.
.. tabs::
.. code-tab:: http
GET /profiles HTTP/1.1
Accept: application/json
.. code-tab:: bash Curl
curl "http://localhost:3000/profiles" \
-H "Accept: application/json"
.. code-block:: json
[{"id":"hGxP/ZLOTeeNEY4pkp9OxA==","name":"John Doe"}]
.. note::
- Casts on domains are ignored by PostgreSQL, their interpretation is left to the application. We're discussing the possibility of including the Domain Representations behavior on `pgsql-hackers <https://www.postgresql.org/message-id/flat/CAGRrpzZKa%2BGu91j1SOvN3tM1f-7Gh_w441c5nAX1QqdH3Q31Lg%40mail.gmail.com>`_.
- It would make more sense to use ``base58`` encoding as it's URL friendly but for simplicity we use ``base64`` (supported natively in PostgreSQL).
.. important::
After creating a cast over a domain, you must refresh PostgREST schema cache. See :ref:`schema_reloading`.
Domain Filter Format
====================
For :ref:`h_filter` to work with the shortened format, you need a different conversion.
PostgREST considers the URL query string to be, in the most generic sense, ``text``. So let's create a function that converts ``text`` to ``app_uuid``.
.. code-block:: postgres
-- the name of the function is arbitrary
CREATE OR REPLACE FUNCTION app_uuid(text) RETURNS app_uuid AS $$
select substring(decode($1,'base64')::text from 3)::uuid;
$$ LANGUAGE SQL IMMUTABLE;
-- plus a CAST to tell PostgREST to use this function
CREATE CAST (text AS app_uuid) WITH FUNCTION app_uuid(text) AS IMPLICIT;
Now you can filter as usual.
.. tabs::
.. code-tab:: http
GET /profiles?id=eq.hGxP/ZLOTeeNEY4pkp9OxA== HTTP/1.1
Accept: application/json
.. code-tab:: bash Curl
curl "http://localhost:3000/profiles?id=eq.ZLOTeeNEY4pkp9OxA==" \
-H "Accept: application/json"
.. code-block:: json
[{"id":"hGxP/ZLOTeeNEY4pkp9OxA==","name":"John Doe"}]
.. note::
If there's no CAST from ``text`` to ``app_uuid`` defined, the filter will still work with the native uuid format (``846c4ffd-92ce-4de7-8d11-8e29929f4ec4``).
Domain Request Body Format
==========================
To accept the shortened format in a JSON request body, for example when creating a new record, define a ``json`` to ``app_uuid`` conversion.
.. code-block:: postgres
-- the name of the function is arbitrary
CREATE OR REPLACE FUNCTION app_uuid(json) RETURNS public.app_uuid AS $$
-- here we reuse the previous app_uuid(text) function
select app_uuid($1 #>> '{}');
$$ LANGUAGE SQL IMMUTABLE;
CREATE CAST (json AS public.app_uuid) WITH FUNCTION app_uuid(json) AS IMPLICIT;
Now we can :ref:`insert` (or :ref:`update`) as usual.
.. tabs::
.. code-tab:: http
POST /profiles HTTP/1.1
Content-Type: application/json
Prefer: return=representation
{"id":"zH7HbFJUTfy/GZpwuirpuQ==","name":"Jane Doe"}
.. code-tab:: bash Curl
curl "http://localhost:3000/profiles" \
-H "Prefer: return=representation" \
-H "Content-Type: application/json" \
-d @- <<JSON
{"id":"zH7HbFJUTfy/GZpwuirpuQ==","name":"Jane Doe"}
JSON
The response:
.. code-block:: json
[{"id":"zH7HbFJUTfy/GZpwuirpuQ==","name":"Jane Doe"}]
Note that on the database side we have our regular ``uuid`` format.
.. code-block:: postgres
select * from profiles;
id | name
--------------------------------------+----------
846c4ffd-92ce-4de7-8d11-8e29929f4ec4 | John Doe
cc7ec76c-5254-4dfc-bf19-9a70ba2ae9b9 | Jane Doe
(2 rows)
.. note::
If there's no CAST from ``json`` to ``app_uuid`` defined, the request body will still work with the native uuid format (``cc7ec76c-5254-4dfc-bf19-9a70ba2ae9b9``).
Advantages over Views
=====================
`Views <https://www.postgresql.org/docs/current/sql-createview.html>`_ also allow us to change the format of the underlying type. However they come with drawbacks that increase complexity.
1) Formatting the column in the view makes it `non-updatable <https://www.postgresql.org/docs/current/sql-createview.html#SQL-CREATEVIEW-UPDATABLE-VIEWS>`_ since Postgres doesn't know how to reverse the transform. This can be worked around using INSTEAD OF triggers.
2) When filtering by this column, we get full table scans for the same reason (also applies to :ref:`computed_cols`) . The performance loss here can be avoided with a computed index, or using a materialized generated column.
3) If the formatted column is used as a foreign key, PostgREST can no longer detect that relationship and :ref:`resource_embedding` breaks. This can be worked around with :ref:`computed_relationships`.
Domain Representations avoid all the above drawbacks. Their only drawback is that for existing tables, you have to change the column types. But this should be a fast operation since domains are binary coercible with their underlying types. A table rewrite won't be required.
.. note::
Why not create a `base type <https://www.postgresql.org/docs/current/sql-createtype.html#id-1.9.3.94.5.8>`_ instead? ``CREATE TYPE app_uuid (INTERNALLENGTH = 22, INPUT = app_uuid_parser, OUTPUT = app_uuid_formatter)``.
Creating base types need superuser, which is restricted on cloud hosted databases. Additionally this way lets “how the data is presented” dictate “how the data is stored” which would be backwards.
+312
View File
@@ -0,0 +1,312 @@
.. _custom_media:
Media Type Handlers
###################
Media Type Handlers allow PostgREST to deliver custom media types. These handlers extend the :ref:`builtin ones <builtin_media>` and can also override them.
Media types are expressed as type aliases using `domains <https://www.postgresql.org/docs/current/sql-createdomain.html>`_ and their name must comply to `RFC 6838 requirements <https://datatracker.ietf.org/doc/html/rfc6838#section-4.2>`_.
.. code-block:: postgres
CREATE DOMAIN "application/json" AS json;
Using these domains, :ref:`functions <s_procs>` can become handlers and `user-defined aggregates <https://www.postgresql.org/docs/current/xaggr.html>`_ can serve as handlers for :ref:`tables_views` and :ref:`table_functions`.
.. important::
- PostgREST vendor media types (``application/vnd.pgrst.plan``, ``application/vnd.pgrst.object`` and ``application/vnd.pgrst.array``) cannot be overriden.
- Long media types like ``application/vnd.openxmlformats-officedocument.wordprocessingml.document`` cannot be expressed as domains since they surpass `PostgreSQL identifier length <https://www.postgresql.org/docs/current/limits.html#LIMITS-TABLE>`_.
For these you can use the :ref:`any_handler`.
Handler Function
================
As an example, let's obtain the `TWKB <https://postgis.net/docs/ST_AsTWKB.html>`_ compressed binary format for a PostGIS geometry.
.. code-block:: postgres
create extension postgis;
create table lines (
id int primary key
, name text
, geom geometry(LINESTRING, 4326)
);
insert into lines values (1, 'line-1', 'LINESTRING(1 1,5 5)'::geometry), (2, 'line-2', 'LINESTRING(2 2,6 6)'::geometry);
For this you can create a vendor media type.
.. code-block:: postgres
create domain "application/vnd.twkb" as bytea;
And use it as a return type on a function, to make it a handler.
.. code-block:: postgres
create or replace function get_line (id int)
returns "application/vnd.twkb" as $$
select st_astwkb(geom) from lines where id = get_line.id;
$$ language sql;
.. note::
For PostgreSQL <= 12, you'll need a cast on the function body :code:`st_astwkb(geom)::"application/vnd.twkb"`.
Now you can request the ``TWKB`` output like so:
.. code-block:: bash
curl 'localhost:3000/rpc/get_line?id=1' -i \
-H "Accept: application/vnd.twkb"
HTTP/1.1 200 OK
Content-Type: application/vnd.twkb
# binary output
Note that PostgREST will automatically set the ``Content-Type`` to ``application/vnd.twkb``.
Handlers for Tables/Views
=========================
To benefit from a compressed format like ``TWKB``, it makes more sense to obtain many rows instead of one. Let's allow that by adding a handler for the table.
User-defined aggregates can be turned into handlers by using domain media types as the return type of their transition or final functions.
Let's create a transition function for this example.
.. code-block:: postgres
create or replace function twkb_handler_transition (state bytea, next lines)
returns "application/vnd.twkb" as $$
select state || st_astwkb(next.geom);
$$ language sql;
Now we'll use it on a new aggregate defined for the ``lines`` table.
.. code-block:: postgres
create or replace aggregate twkb_agg (lines) (
initcond = ''
, stype = "application/vnd.twkb"
, sfunc = twkb_handler_transition
);
Make a quick test on SQL to see it working.
.. code-block:: psql
SELECT twkb_agg(l) from lines l;
twkb_agg
---------------------------------------------------------------
\xa20002c09a0cc09a0c80ea3080ea30a2000280b51880b51880ea3080ea30
(1 row)
Now you can request the table endpoint with the ``twkb`` media type:
.. code-block:: bash
curl 'localhost:3000/lines' -i \
-H "Accept: application/vnd.twkb"
HTTP/1.1 200 OK
Content-Type: application/vnd.twkb
# binary output
If you have a table-valued function returning the same table type, the handler can also act upon on it.
.. code-block:: postgres
create or replace function get_lines ()
returns setof lines as $$
select * from lines;
$$ language sql;
.. code-block:: bash
curl 'localhost:3000/get_lines' -i \
-H "Accept: application/vnd.twkb"
HTTP/1.1 200 OK
Content-Type: application/vnd.twkb
# binary output
Overriding a Builtin Handler
============================
Let's override the existing ``text/csv`` handler for the table to provide a more complex CSV output.
It'll include a `Byte order mark (BOM) <https://en.wikipedia.org/wiki/Byte_order_mark>`_ plus a ``Content-Disposition`` header to set a name for the downloaded file.
Create a domain for the standard ``text/csv`` media type.
.. code-block:: postgres
create domain "text/csv" as text;
And a transition function that returns the domain.
.. code-block:: postgres
create or replace function bom_csv_trans (state text, next lines)
returns "text/csv" as $$
select state || next.id::text || ',' || next.name || ',' || next.geom::text || E'\n';
$$ language sql;
This time we'll add a final function. This will add the CSV header, the BOM and the ``Content-Disposition`` header.
.. code-block:: postgres
create or replace function bom_csv_final (data "text/csv")
returns "text/csv" as $$
-- set the Content-Disposition header
select set_config('response.headers', '[{"Content-Disposition": "attachment; filename=\"lines.csv\""}]', true);
select
-- EFBBBF is the BOM in UTF8 https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8
convert_from (decode (E'EFBBBF', 'hex'),'UTF8') ||
-- the header for the CSV
(E'id,name,geom\n' || data);
$$ language sql;
Now use the transition and final function as part of the new aggregate.
.. code-block:: postgres
create or replace aggregate bom_csv_agg (lines) (
initcond = ''
, stype = "text/csv"
, sfunc = bom_csv_trans
, finalfunc = bom_csv_final
);
.. code-block:: psql
select bom_csv_agg(l) from lines l;
bom_csv_agg
-----------------------------------------------------------------------------------------------------
id,name,geom +
1,line-1,0102000020E610000002000000000000000000F03F000000000000F03F00000000000014400000000000001440+
2,line-2,0102000020E6100000020000000000000000000040000000000000004000000000000018400000000000001840+
(1 row)
And request it like:
.. code-block:: bash
curl 'localhost:3000/lines' -i \
-H "Accept: text/csv"
HTTP/1.1 200 OK
Content-Type: text/csv
Content-Disposition: attachment; filename="lines.csv"
id,name,geom
1,line-1,0102000020E610000002000000000000000000F03F000000000000F03F00000000000014400000000000001440
2,line-2,0102000020E6100000020000000000000000000040000000000000004000000000000018400000000000001840
.. _any_handler:
The "Any" Handler
=================
For more flexibility, you can also define a catch-all handler by using a domain named ``*/*`` (any media type). This obeys to the following rules:
- Responds to all media types and even to requests that don't include an ``Accept`` header.
- Sets the ``Content-Type`` header to ``application/octet-stream`` by default, but this can be overridden inside the function with :ref:`guc_resp_hdrs`.
- This overrides all other handlers (:ref:`builtin <builtin_media>` or custom), so it's better to do it for an isolated function or view.
Let's define an any handler for a view that will always respond with ``XML`` output. It will accept ``text/xml``, ``application/xml``, ``*/*`` and reject other media types.
.. code-block:: postgres
create domain "*/*" as bytea;
-- we'll use an .xml suffix for the view to be clear its output is always XML
create view "lines.xml" as
select * from lines;
-- transition function
create or replace function lines_xml_trans (state "*/*", next "lines.xml")
returns "*/*" as $$
select state || xmlelement(name line, xmlattributes(next.id as id, next.name as name), next.geom)::text::bytea || E'\n' ;
$$ language sql;
-- final function
create or replace function lines_xml_final (data "*/*")
returns "*/*" as $$
declare
-- get the Accept header
req_accept text := current_setting('request.headers', true)::json->>'accept';
begin
-- when we need to override the default Content-Type (application/octet-stream) set by PostgREST
if req_accept = '*/*' then
perform set_config('response.headers', json_build_array(json_build_object('Content-Type', 'text/xml'))::text, true);
elsif req_accept IN ('application/xml', 'text/xml') then
perform set_config('response.headers', json_build_array(json_build_object('Content-Type', req_accept))::text, true);
else
-- we'll reject other non XML media types, we need to reject manually since */* will command PostgREST to accept all media types
raise sqlstate 'PT415' using message = 'Unsupported Media Type';
end if;
return data;
end; $$ language plpgsql;
-- new aggregate
create or replace aggregate lines_xml_agg ("lines.xml") (
stype = "*/*"
, sfunc = lines_xml_trans
, finalfunc = lines_xml_final
);
Test it on SQL:
.. code-block:: psql
select (encode(lines_xml_agg(x), 'escape'))::xml from "lines.xml" x;
encode
------------------------------------------------------------------------------------------------------------------------------
<line id="1" name="line-1">0102000020E610000002000000000000000000F03F000000000000F03F00000000000014400000000000001440</line>+
<line id="2" name="line-2">0102000020E6100000020000000000000000000040000000000000004000000000000018400000000000001840</line>+
Now we can omit the ``Accept`` header and it will respond with XML.
.. code-block:: bash
curl 'localhost:3000/lines.xml' -i
HTTP/1.1 200 OK
Content-Type: text/xml
<line id="1" name="line-1">0102000020E610000002000000000000000000F03F000000000000F03F00000000000014400000000000001440</line>
<line id="2" name="line-2">0102000020E6100000020000000000000000000040000000000000004000000000000018400000000000001840</line>
And it will accept only XML media types.
.. code-block:: bash
curl 'localhost:3000/lines.xml' -i \
-H "Accept: text/xml"
HTTP/1.1 200 OK
Content-Type: text/xml
.. code-block:: bash
curl 'localhost:3000/lines.xml' -i \
-H "Accept: application/xml"
HTTP/1.1 200 OK
Content-Type: text/xml
.. code-block:: bash
curl 'localhost:3000/lines.xml' -i \
-H "Accept: unknown/media"
HTTP/1.1 415 Unsupported Media Type
+104
View File
@@ -0,0 +1,104 @@
.. _open-api:
OpenAPI
=======
PostgREST automatically serves a full `OpenAPI <https://www.openapis.org/>`_ description on the root path. This provides a list of all endpoints (tables, foreign tables, views, functions), along with supported HTTP verbs and example payloads.
.. note::
By default, this output depends on the permissions of the role that is contained in the JWT role claim (or the :ref:`db-anon-role` if no JWT is sent). If you need to show all the endpoints disregarding the role's permissions, set the :ref:`openapi-mode` config to :code:`ignore-privileges`.
For extra customization, the OpenAPI output contains a "description" field for every `SQL comment <https://www.postgresql.org/docs/current/sql-comment.html>`_ on any database object. For instance,
.. code-block:: sql
COMMENT ON SCHEMA mammals IS
'A warm-blooded vertebrate animal of a class that is distinguished by the secretion of milk by females for the nourishment of the young';
COMMENT ON TABLE monotremes IS
'Freakish mammals lay the best eggs for breakfast';
COMMENT ON COLUMN monotremes.has_venomous_claw IS
'Sometimes breakfast is not worth it';
These unsavory comments will appear in the generated JSON as the fields, ``info.description``, ``definitions.monotremes.description`` and ``definitions.monotremes.properties.has_venomous_claw.description``.
Also if you wish to generate a ``summary`` field you can do it by having a multiple line comment, the ``summary`` will be the first line and the ``description`` the lines that follow it:
.. code-block:: plpgsql
COMMENT ON TABLE entities IS
$$Entities summary
Entities description that
spans
multiple lines$$;
Similarly, you can override the API title by commenting the schema.
.. code-block:: plpgsql
COMMENT ON SCHEMA api IS
$$FooBar API
A RESTful API that serves FooBar data.$$;
If you need to include the ``security`` and ``securityDefinitions`` options, set the :ref:`openapi-security-active` configuration to ``true``.
You can use a tool like `Swagger UI <https://swagger.io/tools/swagger-ui/>`_ to create beautiful documentation from the description and to host an interactive web-based dashboard. The dashboard allows developers to make requests against a live PostgREST server, and provides guidance with request headers and example request bodies.
.. important::
The OpenAPI information can go out of date as the schema changes under a running server. See :ref:`schema_reloading`.
.. _override_openapi:
Overriding Full OpenAPI Response
--------------------------------
You can override the whole default response with a function result. To do this, set the function on :ref:`db-root-spec`.
.. code:: bash
db-root-spec = "root"
.. code:: postgres
create or replace function root() returns json as $_$
declare
openapi json = $$
{
"swagger": "2.0",
"info":{
"title":"Overridden",
"description":"This is a my own API"
}
}
$$;
begin
return openapi;
end
$_$ language plpgsql;
.. tabs::
.. code-tab:: http
GET / HTTP/1.1
.. code-tab:: bash Curl
curl http://localhost:3000
.. code-block:: http
HTTP/1.1 200 OK
{
"swagger": "2.0",
"info":{
"title":"Overridden",
"description":"This is a my own API"
}
}
+52
View File
@@ -0,0 +1,52 @@
.. _options_requests:
OPTIONS method
==============
You can verify which HTTP methods are allowed on endpoints for tables and views by using an OPTIONS request. These methods are allowed depending on what operations *can* be done on the table or view, not on the database permissions assigned to them.
For a table named ``people``, OPTIONS would show:
.. tabs::
.. code-tab:: http
OPTIONS /people HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people" -X OPTIONS -i
.. code-block:: http
HTTP/1.1 200 OK
Allow: OPTIONS,GET,HEAD,POST,PUT,PATCH,DELETE
For a view, the methods are determined by the presence of INSTEAD OF TRIGGERS.
.. table::
:widths: auto
+--------------------+-------------------------------------------------------------------------------------------------+
| Method allowed | View's requirements |
+====================+=================================================================================================+
| OPTIONS, GET, HEAD | None (Always allowed) |
+--------------------+-------------------------------------------------------------------------------------------------+
| POST | INSTEAD OF INSERT TRIGGER |
+--------------------+-------------------------------------------------------------------------------------------------+
| PUT | INSTEAD OF INSERT TRIGGER, INSTEAD OF UPDATE TRIGGER, also requires the presence of a |
| | primary key |
+--------------------+-------------------------------------------------------------------------------------------------+
| PATCH | INSTEAD OF UPDATE TRIGGER |
+--------------------+-------------------------------------------------------------------------------------------------+
| DELETE | INSTEAD OF DELETE TRIGGER |
+--------------------+-------------------------------------------------------------------------------------------------+
| All the above methods are allowed for |
| `auto-updatable views <https://www.postgresql.org/docs/current/sql-createview.html#SQL-CREATEVIEW-UPDATABLE-VIEWS>`_ |
+--------------------+-------------------------------------------------------------------------------------------------+
For functions, the methods depend on their volatility. ``VOLATILE`` functions allow only ``OPTIONS,POST``, whereas the rest also permit ``GET,HEAD``.
.. important::
Whenever you add or remove tables or views, or modify a view's INSTEAD OF TRIGGERS on the database, you must refresh PostgREST's schema cache for OPTIONS requests to work properly. See the section :ref:`schema_reloading`.
+188
View File
@@ -0,0 +1,188 @@
Pagination and Count
####################
Pagination controls the number of rows returned for an :doc:`API resource <../api>` response. Combined with the count, you can traverse all the rows of a response.
.. _limits:
Limits and Pagination
---------------------
PostgREST uses HTTP range headers to describe the size of results. Every response contains the current range and, if requested, the total number of results:
.. code-block:: http
HTTP/1.1 200 OK
Range-Unit: items
Content-Range: 0-14/*
Here items zero through fourteen are returned. This information is available in every response and can help you render pagination controls on the client. This is an RFC7233-compliant solution that keeps the response JSON cleaner.
Query Parameters
~~~~~~~~~~~~~~~~
One way to request limits and offsets is by using query parameters. For example:
.. tabs::
.. code-tab:: http
GET /people?limit=15&offset=30 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?limit=15&offset=30"
This method is also useful for embedded resources, which we will cover in another section. The server always responds with range headers even if you use query parameters to limit the query.
Range Header
~~~~~~~~~~~~
You can use headers to specify the range of rows desired.
This request gets the first twenty people:
.. tabs::
.. code-tab:: http
GET /people HTTP/1.1
Range-Unit: items
Range: 0-19
.. code-tab:: bash Curl
curl "http://localhost:3000/people" -i \
-H "Range-Unit: items" \
-H "Range: 0-19"
Note that the server may respond with fewer if unable to meet your request:
.. code-block:: http
HTTP/1.1 200 OK
Range-Unit: items
Content-Range: 0-17/*
You may also request open-ended ranges for an offset with no limit, e.g. :code:`Range: 10-`.
.. _prefer_count:
Counting
--------
In order to obtain the total size of the table (such as when rendering the last page link in a pagination control), you can specify a ``Prefer: count=<value>`` header. The values can be ``exact``, ``planned`` and ``estimated``.
This also works on views and :ref:`table_functions`.
.. _exact_count:
Exact Count
~~~~~~~~~~~
To get the exact count, use ``Prefer: count=exact``.
.. tabs::
.. code-tab:: http
HEAD /bigtable HTTP/1.1
Range-Unit: items
Range: 0-24
Prefer: count=exact
.. code-tab:: bash Curl
curl "http://localhost:3000/bigtable" -I \
-H "Range-Unit: items" \
-H "Range: 0-24" \
-H "Prefer: count=exact"
Note that the larger the table the slower this query runs in the database. The server will respond with the selected range and total
.. code-block:: http
HTTP/1.1 206 Partial Content
Range-Unit: items
Content-Range: 0-24/3573458
.. _planned_count:
Planned Count
~~~~~~~~~~~~~
To avoid the shortcomings of :ref:`exact count <exact_count>`, PostgREST can leverage PostgreSQL statistics and get a fairly accurate and fast count.
To do this, specify the ``Prefer: count=planned`` header.
.. tabs::
.. code-tab:: http
HEAD /bigtable?limit=25 HTTP/1.1
Prefer: count=planned
.. code-tab:: bash Curl
curl "http://localhost:3000/bigtable?limit=25" -I \
-H "Prefer: count=planned"
.. code-block:: http
HTTP/1.1 206 Partial Content
Content-Range: 0-24/3572000
Note that the accuracy of this count depends on how up-to-date are the PostgreSQL statistics tables.
For example in this case, to increase the accuracy of the count you can do ``ANALYZE bigtable``.
See `ANALYZE <https://www.postgresql.org/docs/current/sql-analyze.html>`_ for more details.
.. _estimated_count:
Estimated Count
~~~~~~~~~~~~~~~
When you are interested in the count, the relative error is important. If you have a :ref:`planned count <planned_count>` of 1000000 and the exact count is
1001000, the error is small enough to be ignored. But with a planned count of 7, an exact count of 28 would be a huge misprediction.
In general, when having smaller row-counts, the estimated count should be as close to the exact count as possible.
To help with these cases, PostgREST can get the exact count up until a threshold and get the planned count when
that threshold is surpassed. To use this behavior, you can specify the ``Prefer: count=estimated`` header. The **threshold** is
defined by :ref:`db-max-rows`.
Here's an example. Suppose we set ``db-max-rows=1000`` and ``smalltable`` has 321 rows, then we'll get the exact count:
.. tabs::
.. code-tab:: http
HEAD /smalltable?limit=25 HTTP/1.1
Prefer: count=estimated
.. code-tab:: bash Curl
curl "http://localhost:3000/smalltable?limit=25" -I \
-H "Prefer: count=estimated"
.. code-block:: http
HTTP/1.1 206 Partial Content
Content-Range: 0-24/321
If we make a similar request on ``bigtable``, which has 3573458 rows, we would get the planned count:
.. tabs::
.. code-tab:: http
HEAD /bigtable?limit=25 HTTP/1.1
Prefer: count=estimated
.. code-tab:: bash Curl
curl "http://localhost:3000/bigtable?limit=25" -I \
-H "Prefer: count=estimated"
.. code-block:: http
HTTP/1.1 206 Partial Content
Content-Range: 0-24/3572000
+225
View File
@@ -0,0 +1,225 @@
.. _preferences:
Prefer Header
#############
PostgREST honors the Prefer HTTP header specified on `RFC 7240 <https://www.rfc-editor.org/rfc/rfc7240.html>`_. It allows clients to specify required and optional behaviors for their requests.
The following preferences are supported.
- ``Prefer: handling``. See :ref:`prefer_handling`.
- ``Prefer: timezone``. See :ref:`prefer_timezone`.
- ``Prefer: return``. See :ref:`prefer_return`.
- ``Prefer: count``. See :ref:`prefer_count`.
- ``Prefer: resolution``. See :ref:`prefer_resolution`.
- ``Prefer: missing``. See :ref:`bulk_insert_default`.
.. _prefer_handling:
Strict or Lenient Handling
==========================
The server ignores unrecognized or unfulfillable preferences by default. You can control this behavior with the ``handling`` preference. It can take two values: ``lenient`` (the default) or ``strict``.
``handling=strict`` will throw an error if you specify invalid preferences. For instance:
.. tabs::
.. code-tab:: http
GET /projects HTTP/1.1
Prefer: handling=strict, foo, bar
.. code-tab:: bash Curl
curl -i "http://localhost:3000/projects" \
-H "Prefer: handling=strict, foo, bar"
.. code-block:: http
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
.. code-block:: json
{
"code": "PGRST122",
"message": "Invalid preferences given with handling=strict",
"details": "Invalid preferences: foo, bar",
"hint": null
}
``handling=lenient`` ignores invalid preferences.
.. tabs::
.. code-tab:: http
GET /projects HTTP/1.1
Prefer: handling=lenient, foo, bar
.. code-tab:: bash Curl
curl -i "http://localhost:3000/projects" \
-H "Prefer: handling=lenient, foo, bar"
.. code-block:: http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
.. _prefer_timezone:
Timezone
========
The ``timezone`` preference allows you to change the `PostgreSQL timezone <https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-TIMEZONE>`_. It accepts all timezones in `pg_timezone_names <https://www.postgresql.org/docs/current/view-pg-timezone-names.html>`_.
.. tabs::
.. code-tab:: http
GET /timestamps HTTP/1.1
Prefer: timezone=America/Los_Angeles
.. code-tab:: bash Curl
curl -i "http://localhost:3000/timestamps" \
-H "Prefer: timezone=America/Los_Angeles"
.. code-block:: http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Preference-Applied: timezone=America/Los_Angeles
.. code-block:: json
[
{"t":"2023-10-18T05:37:59.611-07:00"},
{"t":"2023-10-18T07:37:59.611-07:00"},
{"t":"2023-10-18T09:37:59.611-07:00"}
]
For an invalid timezone, PostgREST returns values with the default timezone (configured on ``postgresql.conf`` or as a setting on the :ref:`authenticator <roles>`).
.. tabs::
.. code-tab:: http
GET /timestamps HTTP/1.1
Prefer: timezone=Jupiter/Red_Spot
.. code-tab:: bash Curl
curl -i "http://localhost:3000/timestamps" \
-H "Prefer: timezone=Jupiter/Red_Spot"
.. code-block:: http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
.. code-block:: json
[
{"t":"2023-10-18T12:37:59.611+00:00"},
{"t":"2023-10-18T14:37:59.611+00:00"},
{"t":"2023-10-18T16:37:59.611+00:00"}
]
Note that there's no ``Preference-Applied`` in the response.
However, with ``handling=strict``, an invalid timezone preference will throw an :ref:`error <pgrst122>`.
.. tabs::
.. code-tab:: http
GET /timestamps HTTP/1.1
Prefer: handling=strict, timezone=Jupiter/Red_Spot
.. code-tab:: bash Curl
curl -i "http://localhost:3000/timestamps" \
-H "Prefer: handling=strict, timezone=Jupiter/Red_Spot"
.. code-block:: http
HTTP/1.1 400 Bad Request
.. _prefer_return:
Return Representation
=====================
The ``return`` preference can be used to obtain information about affected resource when it's :ref:`inserted <insert>`, :ref:`updated <update>` or :ref:`deleted <delete>`.
This helps avoid a subsequent GET request.
Minimal
-------
With ``Prefer: return=minimal``, no response body will be returned. This is the default mode for all write requests.
Headers Only
------------
If the table has a primary key, the response can contain a :code:`Location` header describing where to find the new object by including the header :code:`Prefer: return=headers-only` in the request. Make sure that the table is not write-only, otherwise constructing the :code:`Location` header will cause a permissions error.
.. tabs::
.. code-tab:: http
POST /projects HTTP/1.1
Prefer: return=headers-only
{"id":33, "name": "x"}
.. code-tab:: bash Curl
curl -i "http://localhost:3000/projects" -X POST \
-H "Content-Type: application/json" \
-H "Prefer: return=headers-only" \
-d '{"id":33, "name": "x"}'
.. code-block:: http
HTTP/1.1 201 Created
Location: /projects?id=eq.34
Preference-Applied: return=headers-only
Full
----
On the other end of the spectrum you can get the full created object back in the response to your request by including the header :code:`Prefer: return=representation`. That way you won't have to make another HTTP call to discover properties that may have been filled in on the server side. You can also apply the standard :ref:`v_filter` to these results.
.. tabs::
.. code-tab:: http
POST /projects HTTP/1.1
Content-Type: application/json; charset=utf-8
Prefer: return=representation
{"id":33, "name": "x"}
.. code-tab:: bash Curl
curl -i "http://localhost:3000/projects" -X POST \
-H "Content-Type: application/json" \
-H "Prefer: return=representation" \
-d '{"id":33, "name": "x"}'
.. code::
HTTP/1.1 201 Created
Preference-Applied: return=representation
[
{
"id": 33,
"name": "x"
}
]
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,175 @@
Resource Representation
#######################
PostgREST uses proper HTTP content negotiation (`RFC7231 <https://datatracker.ietf.org/doc/html/rfc7231#section-5.3>`_) to deliver a resource representation.
That is to say the same API endpoint can respond in different formats like JSON or CSV depending on the request.
.. _res_format:
Response Format
===============
Use the Accept request header to specify the acceptable format (or formats) for the response:
.. tabs::
.. code-tab:: http
GET /people HTTP/1.1
Accept: application/json
.. code-tab:: bash Curl
curl "http://localhost:3000/people" \
-H "Accept: application/json"
.. _builtin_media:
Builtin Media Type Handlers
===========================
Builtin handlers are offered for common standard media types.
* ``text/csv`` and ``application/json``, for all API endpoints. See :ref:`tables_views` and :ref:`s_procs`.
* ``application/openapi+json``, for the root endpoint. See :ref:`open-api`.
* ``application/geo+json``, see :ref:`ww_postgis`.
* ``*/*``, resolves to ``application/json`` for API endpoints and to ``application/openapi+json`` for the root endpoint.
The following vendor media types handlers are also supported.
* ``application/vnd.pgrst.plan``, see :ref:`explain_plan`.
* ``application/vnd.pgrst.object`` and ``application/vnd.pgrst.array``, see :ref:`singular_plural` and :ref:`stripped_nulls`.
Any unrecognized media type will throw an error.
.. tabs::
.. code-tab:: http
GET /people HTTP/1.1
Accept: unknown/unknown
.. code-tab:: bash Curl
curl "http://localhost:3000/people" \
-H "Accept: unknown/unknown"
.. code-block:: http
HTTP/1.1 415 Unsupported Media Type
{"code":"PGRST107","details":null,"hint":null,"message":"None of these media types are available: unknown/unknown"}
To extend the accepted media types, you can use :ref:`custom_media`.
.. _singular_plural:
Singular or Plural
------------------
By default PostgREST returns all JSON results in an array, even when there is only one item. For example, requesting :code:`/items?id=eq.1` returns
.. code:: json
[
{ "id": 1 }
]
This can be inconvenient for client code. To return the first result as an object unenclosed by an array, specify :code:`vnd.pgrst.object` as part of the :code:`Accept` header
.. tabs::
.. code-tab:: http
GET /items?id=eq.1 HTTP/1.1
Accept: application/vnd.pgrst.object+json
.. code-tab:: bash Curl
curl "http://localhost:3000/items?id=eq.1" \
-H "Accept: application/vnd.pgrst.object+json"
This returns
.. code:: json
{ "id": 1 }
with a :code:`Content-Type: application/vnd.pgrst.object+json`.
When a singular response is requested but no entries are found, the server responds with an error message and 406 Not Acceptable status code rather than the usual empty array and 200 status:
.. code-block:: json
{
"message": "JSON object requested, multiple (or no) rows returned",
"details": "Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row",
"hint": null,
"code": "PGRST505"
}
.. note::
Many APIs distinguish plural and singular resources using a special nested URL convention e.g. `/stories` vs `/stories/1`. Why do we use `/stories?id=eq.1`? The answer is because a singular resource is (for us) a row determined by a primary key, and primary keys can be compound (meaning defined across more than one column). The more familiar nested urls consider only a degenerate case of simple and overwhelmingly numeric primary keys. These so-called artificial keys are often introduced automatically by Object Relational Mapping libraries.
Admittedly PostgREST could detect when there is an equality condition holding on all columns constituting the primary key and automatically convert to singular. However this could lead to a surprising change of format that breaks unwary client code just by filtering on an extra column. Instead we allow manually specifying singular vs plural to decouple that choice from the URL format.
.. _stripped_nulls:
Stripped Nulls
--------------
By default PostgREST returns all JSON null values. For example, requesting ``/projects?id=gt.10`` returns
.. code:: json
[
{ "id": 11, "name": "OSX", "client_id": 1, "another_col": "val" },
{ "id": 12, "name": "ProjectX", "client_id": null, "another_col": null },
{ "id": 13, "name": "Y", "client_id": null, "another_col": null }
]
On large result sets, the unused keys with ``null`` values can waste bandwith unnecessarily. To remove them, specify ``nulls=stripped`` as a parameter of ``application/vnd.pgrst.array``:
.. tabs::
.. code-tab:: http
GET /projects?id=gt.10 HTTP/1.1
Accept: application/vnd.pgrst.array+json;nulls=stripped
.. code-tab:: bash Curl
curl "http://localhost:3000/projects?id=gt.10" \
-H "Accept: application/vnd.pgrst.array+json;nulls=stripped"
This returns
.. code:: json
[
{ "id": 11, "name": "OSX", "client_id": 1, "another_col": "val" },
{ "id": 12, "name": "ProjectX" },
{ "id": 13, "name": "Y"}
]
.. _req_body:
Request Body
============
The server handles the following request body media types:
* ``application/json``
* ``application/x-www-form-urlencoded``
* ``text/csv``
For :ref:`tables_views` this works on ``POST``, ``PATCH`` and ``PUT`` methods. For :ref:`s_procs`, it works on ``POST`` methods.
For stored procedures there are three additional types:
* ``application/octet-stream``
* ``text/plain``
* ``text/xml``
See :ref:`s_proc_single_unnamed`.
+156
View File
@@ -0,0 +1,156 @@
.. _schemas:
Schemas
=======
PostgREST can expose a single or multiple schema's tables, views and functions. The :ref:`active database role <roles>` must have the usage privilege on the schemas to access them.
Single schema
-------------
To expose a single schema, specify a single value in :ref:`db-schemas`.
.. code:: bash
db-schemas = "api"
This schema is added to the `search_path <https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH>`_ of every request using :ref:`tx_settings`.
.. _multiple-schemas:
Multiple schemas
----------------
To expose multiple schemas, specify a comma-separated list on :ref:`db-schemas`:
.. code:: bash
db-schemas = "tenant1, tenant2"
To switch schemas, use the ``Accept-Profile`` and ``Content-Profile`` headers.
If you don't specify a Profile header, the first schema in the list(``tenant1`` here) is selected as the default schema.
Only the selected schema gets added to the `search_path <https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH>`_ of every request.
.. note::
These headers are based on the "Content Negotiation by Profile" spec: https://www.w3.org/TR/dx-prof-conneg
GET/HEAD
~~~~~~~~
For GET or HEAD, select the schema with ``Accept-Profile``.
.. tabs::
.. code-tab:: http
GET /items HTTP/1.1
Accept-Profile: tenant2
.. code-tab:: bash Curl
curl "http://localhost:3000/items" \
-H "Accept-Profile: tenant2"
Other methods
~~~~~~~~~~~~~
For POST, PATCH, PUT and DELETE, select the schema with ``Content-Profile``.
.. tabs::
.. code-tab:: http
POST /items HTTP/1.1
Content-Profile: tenant2
{...}
.. code-tab:: bash Curl
curl "http://localhost:3000/items" \
-X POST -H "Content-Type: application/json" \
-H "Content-Profile: tenant2" \
-d '{...}'
You can also select the schema for :ref:`s_procs` and :ref:`open-api`.
Restricted schemas
~~~~~~~~~~~~~~~~~~
You can only switch to a schema included in :ref:`db-schemas`. Using another schema will result in an error:
.. tabs::
.. code-tab:: http
GET /items HTTP/1.1
Accept-Profile: tenant3
{...}
.. code-tab:: bash Curl
curl "http://localhost:3000/items" \
-H "Accept-Profile: tenant3"
.. code-block::
{
"code":"PGRST106",
"details":null,
"hint":null,
"message":"The schema must be one of the following: tenant1, tenant2"
}
Dynamic schemas
~~~~~~~~~~~~~~~
To add schemas dynamically, you can use :ref:`in_db_config` plus :ref:`config reloading <config_reloading_notify>` and :ref:`schema cache reloading <schema_reloading_notify>`. Here are some options for how to do this:
- If the schemas' names have a pattern, like a ``tenant_`` prefix, do:
.. code-block:: postgresql
create or replace function postgrest.pre_config()
returns void as $$
select
set_config('pgrst.db_schemas', string_agg(nspname, ','), true)
from pg_namespace
where nspname like 'tenant_%';
$$ language sql;
- If there's no name pattern but they're created with a particular role (``CREATE SCHEMA mine AUTHORIZATION joe``), do:
.. code-block:: postgresql
create or replace function postgrest.pre_config()
returns void as $$
select
set_config('pgrst.db_schemas', string_agg(nspname, ','), true)
from pg_namespace
where nspowner = 'joe'::regrole;
$$ language sql;
- Otherwise, you might need to create a table that stores the allowed schemas.
.. code-block:: postgresql
create table postgrest.config (schemas text);
create or replace function postgrest.pre_config()
returns void as $$
select
set_config('pgrst.db_schemas', schemas, true)
from postgrest.config;
$$ language sql;
Then each time you add an schema, do:
.. code-block:: postgresql
NOTIFY pgrst, 'reload config';
NOTIFY pgrst, 'reload schema';
+491
View File
@@ -0,0 +1,491 @@
.. _s_procs:
Stored Procedures
=================
*"A single resource can be the equivalent of a database stored procedure, with the power to abstract state changes over any number of storage items"* -- `Roy T. Fielding <http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven#comment-743>`_
Procedures can perform any operations allowed by PostgreSQL (read data, modify data, :ref:`raise errors <raise_error>`, and even DDL operations). Every stored procedure in the :ref:`exposed schema <schemas>` and accessible by the :ref:`active database role <roles>` is executable under the :code:`/rpc` prefix.
If they return table types, Stored Procedures can:
- Use all the same :ref:`read filters as Tables and Views <read>` (horizontal/vertical filtering, counts, limits, etc.).
- Use :ref:`Resource Embedding <s_proc_embed>`, if the returned table type has relationships to other tables.
.. note::
Why the ``/rpc`` prefix? PostgreSQL allows a table or view to have the same name as a function. The prefix allows us to avoid routes collisions.
Calling with POST
-----------------
To supply arguments in an API call, include a JSON object in the request payload. Each key/value of the object will become an argument.
For instance, assume we have created this function in the database.
.. code-block:: plpgsql
CREATE FUNCTION add_them(a integer, b integer)
RETURNS integer AS $$
SELECT a + b;
$$ LANGUAGE SQL IMMUTABLE;
.. important::
Whenever you create or change a function you must refresh PostgREST's schema cache. See the section :ref:`schema_reloading`.
The client can call it by posting an object like
.. tabs::
.. code-tab:: http
POST /rpc/add_them HTTP/1.1
{ "a": 1, "b": 2 }
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/add_them" \
-X POST -H "Content-Type: application/json" \
-d '{ "a": 1, "b": 2 }'
.. code-block:: json
3
.. note::
PostgreSQL converts identifier names to lowercase unless you quote them like:
.. code-block:: postgres
CREATE FUNCTION "someFunc"("someParam" text) ...
Calling with GET
----------------
If the function doesn't modify the database, it will also run under the GET method (see :ref:`access_mode`).
.. tabs::
.. code-tab:: http
GET /rpc/add_them?a=1&b=2 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/add_them?a=1&b=2"
The function parameter names match the JSON object keys in the POST case, for the GET case they match the query parameters ``?a=1&b=2``.
.. _s_proc_single_json:
Functions with a single JSON parameter
--------------------------------------
You can also call a function that takes a single parameter of type JSON by sending the header :code:`Prefer: params=single-object` with your request. That way the JSON request body will be used as the single argument.
.. code-block:: plpgsql
CREATE FUNCTION mult_them(param json) RETURNS int AS $$
SELECT (param->>'x')::int * (param->>'y')::int
$$ LANGUAGE SQL;
.. tabs::
.. code-tab:: http
POST /rpc/mult_them HTTP/1.1
Prefer: params=single-object
{ "x": 4, "y": 2 }
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/mult_them" \
-X POST -H "Content-Type: application/json" \
-H "Prefer: params=single-object" \
-d '{ "x": 4, "y": 2 }'
.. code-block:: json
8
.. _s_proc_single_unnamed:
Functions with a single unnamed parameter
-----------------------------------------
You can make a POST request to a function with a single unnamed parameter to send raw ``json/jsonb``, ``bytea``, ``text`` or ``xml`` data.
To send raw JSON, the function must have a single unnamed ``json`` or ``jsonb`` parameter and the header ``Content-Type: application/json`` must be included in the request.
.. code-block:: plpgsql
CREATE FUNCTION mult_them(json) RETURNS int AS $$
SELECT ($1->>'x')::int * ($1->>'y')::int
$$ LANGUAGE SQL;
.. tabs::
.. code-tab:: http
POST /rpc/mult_them HTTP/1.1
Content-Type: application/json
{ "x": 4, "y": 2 }
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/mult_them" \
-X POST -H "Content-Type: application/json" \
-d '{ "x": 4, "y": 2 }'
.. code-block:: json
8
.. note::
If an overloaded function has a single ``json`` or ``jsonb`` unnamed parameter, PostgREST will call this function as a fallback provided that no other overloaded function is found with the parameters sent in the POST request.
To send raw XML, the parameter type must be ``xml`` and the header ``Content-Type: text/xml`` must be included in the request.
To send raw binary, the parameter type must be ``bytea`` and the header ``Content-Type: application/octet-stream`` must be included in the request.
.. code-block:: plpgsql
CREATE TABLE files(blob bytea);
CREATE FUNCTION upload_binary(bytea) RETURNS void AS $$
INSERT INTO files(blob) VALUES ($1);
$$ LANGUAGE SQL;
.. tabs::
.. code-tab:: http
POST /rpc/upload_binary HTTP/1.1
Content-Type: application/octet-stream
file_name.ext
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/upload_binary" \
-X POST -H "Content-Type: application/octet-stream" \
--data-binary "@file_name.ext"
.. code-block:: http
HTTP/1.1 200 OK
[ ... ]
To send raw text, the parameter type must be ``text`` and the header ``Content-Type: text/plain`` must be included in the request.
.. _s_procs_array:
Functions with array parameters
-------------------------------
You can call a function that takes an array parameter:
.. code-block:: postgres
create function plus_one(arr int[]) returns int[] as $$
SELECT array_agg(n + 1) FROM unnest($1) AS n;
$$ language sql;
.. tabs::
.. code-tab:: http
POST /rpc/plus_one HTTP/1.1
Content-Type: application/json
{"arr": [1,2,3,4]}
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/plus_one" \
-X POST -H "Content-Type: application/json" \
-d '{"arr": [1,2,3,4]}'
.. code-block:: json
[2,3,4,5]
For calling the function with GET, you can pass the array as an `array literal <https://www.postgresql.org/docs/current/arrays.html#ARRAYS-INPUT>`_,
as in ``{1,2,3,4}``. Note that the curly brackets have to be urlencoded(``{`` is ``%7B`` and ``}`` is ``%7D``).
.. tabs::
.. code-tab:: http
GET /rpc/plus_one?arr=%7B1,2,3,4%7D' HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/plus_one?arr=%7B1,2,3,4%7D'"
.. note::
For versions prior to PostgreSQL 10, to pass a PostgreSQL native array on a POST payload, you need to quote it and use an array literal:
.. tabs::
.. code-tab:: http
POST /rpc/plus_one HTTP/1.1
{ "arr": "{1,2,3,4}" }
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/plus_one" \
-X POST -H "Content-Type: application/json" \
-d '{ "arr": "{1,2,3,4}" }'
In these versions we recommend using function parameters of type JSON to accept arrays from the client.
.. _s_procs_variadic:
Variadic functions
------------------
You can call a variadic function by passing a JSON array in a POST request:
.. code-block:: postgres
create function plus_one(variadic v int[]) returns int[] as $$
SELECT array_agg(n + 1) FROM unnest($1) AS n;
$$ language sql;
.. tabs::
.. code-tab:: http
POST /rpc/plus_one HTTP/1.1
Content-Type: application/json
{"v": [1,2,3,4]}
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/plus_one" \
-X POST -H "Content-Type: application/json" \
-d '{"v": [1,2,3,4]}'
.. code-block:: json
[2,3,4,5]
In a GET request, you can repeat the same parameter name:
.. tabs::
.. code-tab:: http
GET /rpc/plus_one?v=1&v=2&v=3&v=4 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/plus_one?v=1&v=2&v=3&v=4"
Repeating also works in POST requests with ``Content-Type: application/x-www-form-urlencoded``:
.. tabs::
.. code-tab:: http
POST /rpc/plus_one HTTP/1.1
Content-Type: application/x-www-form-urlencoded
v=1&v=2&v=3&v=4
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/plus_one" \
-X POST -H "Content-Type: application/x-www-form-urlencoded" \
-d 'v=1&v=2&v=3&v=4'
.. _table_functions:
Table-Valued Functions
----------------------
A function that returns a table type can be filtered using the same filters as :ref:`tables and views <tables_views>`. They can also use :ref:`Resource Embedding <s_proc_embed>`.
.. code-block:: postgres
CREATE FUNCTION best_films_2017() RETURNS SETOF films ..
.. tabs::
.. code-tab:: http
GET /rpc/best_films_2017?select=title,director:directors(*) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/best_films_2017?select=title,director:directors(*)"
.. tabs::
.. code-tab:: http
GET /rpc/best_films_2017?rating=gt.8&order=title.desc HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/best_films_2017?rating=gt.8&order=title.desc"
.. _function_inlining:
Function Inlining
~~~~~~~~~~~~~~~~~
A function that follows the `rules for inlining <https://wiki.postgresql.org/wiki/Inlining_of_SQL_functions#Inlining_conditions_for_table_functions>`_ will also inline :ref:`filters <h_filter>`, :ref:`order <ordering>` and :ref:`limits <limits>`.
For example, for the following function:
.. code-block:: postgres
create function getallprojects() returns setof projects
language sql stable
as $$
select * from projects;
$$;
Let's get its :ref:`explain_plan` when calling it with filters applied:
.. tabs::
.. code-tab:: http
GET /rpc/getallprojects?id=eq.1 HTTP/1.1
Accept: application/vnd.pgrst.plan
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/getallprojects?id=eq.1" \
-H "Accept: application/vnd.pgrst.plan"
.. code-block:: psql
Aggregate (cost=8.18..8.20 rows=1 width=112)
-> Index Scan using projects_pkey on projects (cost=0.15..8.17 rows=1 width=40)
Index Cond: (id = 1)
Notice there's no "Function Scan" node in the plan, which tells us it has been inlined.
.. _scalar_functions:
Scalar functions
----------------
PostgREST will detect if the function is scalar or table-valued and will shape the response format accordingly:
.. tabs::
.. code-tab:: http
GET /rpc/add_them?a=1&b=2 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/add_them?a=1&b=2"
.. code-block:: json
3
.. tabs::
.. code-tab:: http
GET /rpc/best_films_2017 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/best_films_2017"
.. code-block:: json
[
{ "title": "Okja", "rating": 7.4},
{ "title": "Call me by your name", "rating": 8},
{ "title": "Blade Runner 2049", "rating": 8.1}
]
To manually choose a return format such as binary, see :ref:`custom_media`.
.. _untyped_functions:
Untyped functions
-----------------
Functions that return ``record`` or ``SETOF record`` are supported:
.. code-block:: postgres
create function projects_setof_record() returns setof record as $$
select * from projects;
$$ language sql;
.. tabs::
.. code-tab:: http
GET /rpc/projects_setof_record HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/projects_setof_record"
.. code-block:: json
[{"id":1,"name":"Windows 7","client_id":1},
{"id":2,"name":"Windows 10","client_id":1},
{"id":3,"name":"IOS","client_id":2}]
However note that they will fail when trying to use :ref:`v_filter` and :ref:`h_filter` on them.
So while they can be used for quick tests, it's recommended to always choose a strict return type for the function.
Overloaded functions
--------------------
You can call overloaded functions with different number of arguments.
.. code-block:: postgres
CREATE FUNCTION rental_duration(customer_id integer) ..
CREATE FUNCTION rental_duration(customer_id integer, from_date date) ..
.. tabs::
.. code-tab:: http
GET /rpc/rental_duration?customer_id=232 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/rental_duration?customer_id=232"
.. tabs::
.. code-tab:: http
GET /rpc/rental_duration?customer_id=232&from_date=2018-07-01 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/rental_duration?customer_id=232&from_date=2018-07-01"
.. important::
Overloaded functions with the same argument names but different types are not supported.
+990
View File
@@ -0,0 +1,990 @@
.. _tables_views:
Tables and Views
################
All views and tables of the :ref:`exposed schema <schemas>` and accessible by the :ref:`active database role <roles>` are available for querying. They are exposed in one-level deep routes.
.. _read:
Read
====
For instance the full contents of a table `people` is returned at
.. tabs::
.. code-tab:: http
GET /people HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people"
There are no deeply/nested/routes. Each route provides OPTIONS, GET, HEAD, POST, PATCH, and DELETE verbs depending entirely on database permissions.
.. note::
Why not provide nested routes? Many APIs allow nesting to retrieve related information, such as :code:`/films/1/director`. We offer a more flexible mechanism (inspired by GraphQL) to embed related information. It can handle one-to-many and many-to-many relationships. This is covered in the section about :ref:`resource_embedding`.
.. _h_filter:
Horizontal Filtering
--------------------
You can filter result rows by adding conditions on columns. For instance, to return people aged under 13 years old:
.. tabs::
.. code-tab:: http
GET /people?age=lt.13 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?age=lt.13"
You can evaluate multiple conditions on columns by adding more query string parameters. For instance, to return people who are 18 or older **and** are students:
.. tabs::
.. code-tab:: http
GET /people?age=gte.18&student=is.true HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?age=gte.18&student=is.true"
.. _operators:
Operators
~~~~~~~~~
These operators are available:
============ ======================== ==================================================================================
Abbreviation In PostgreSQL Meaning
============ ======================== ==================================================================================
eq :code:`=` equals
gt :code:`>` greater than
gte :code:`>=` greater than or equal
lt :code:`<` less than
lte :code:`<=` less than or equal
neq :code:`<>` or :code:`!=` not equal
like :code:`LIKE` LIKE operator (to avoid `URL encoding <https://en.wikipedia.org/wiki/Percent-encoding>`_ you can use ``*`` as an alias of the percent sign ``%`` for the pattern)
ilike :code:`ILIKE` ILIKE operator (to avoid `URL encoding <https://en.wikipedia.org/wiki/Percent-encoding>`_ you can use ``*`` as an alias of the percent sign ``%`` for the pattern)
match :code:`~` ~ operator, see :ref:`pattern_matching`
imatch :code:`~*` ~* operator, see :ref:`pattern_matching`
in :code:`IN` one of a list of values, e.g. :code:`?a=in.(1,2,3)`
also supports commas in quoted strings like
:code:`?a=in.("hi,there","yes,you")`
is :code:`IS` checking for exact equality (null,true,false,unknown)
isdistinct :code:`IS DISTINCT FROM` not equal, treating :code:`NULL` as a comparable value
fts :code:`@@` :ref:`fts` using to_tsquery
plfts :code:`@@` :ref:`fts` using plainto_tsquery
phfts :code:`@@` :ref:`fts` using phraseto_tsquery
wfts :code:`@@` :ref:`fts` using websearch_to_tsquery
cs :code:`@>` contains e.g. :code:`?tags=cs.{example, new}`
cd :code:`<@` contained in e.g. :code:`?values=cd.{1,2,3}`
ov :code:`&&` overlap (have points in common), e.g. :code:`?period=ov.[2017-01-01,2017-06-30]`
also supports array types, use curly braces instead of square brackets e.g.
:code: `?arr=ov.{1,3}`
sl :code:`<<` strictly left of, e.g. :code:`?range=sl.(1,10)`
sr :code:`>>` strictly right of
nxr :code:`&<` does not extend to the right of, e.g. :code:`?range=nxr.(1,10)`
nxl :code:`&>` does not extend to the left of
adj :code:`-|-` is adjacent to, e.g. :code:`?range=adj.(1,10)`
not :code:`NOT` negates another operator, see :ref:`logical_operators`
or :code:`OR` logical :code:`OR`, see :ref:`logical_operators`
and :code:`AND` logical :code:`AND`, see :ref:`logical_operators`
all :code:`ALL` comparison matches all the values in the list, see :ref:`logical_operators`
any :code:`ANY` comparison matches any value in the list, see :ref:`logical_operators`
============ ======================== ==================================================================================
For more complicated filters you will have to create a new view in the database, or use a stored procedure. For instance, here's a view to show "today's stories" including possibly older pinned stories:
.. code-block:: postgresql
CREATE VIEW fresh_stories AS
SELECT *
FROM stories
WHERE pinned = true
OR published > now() - interval '1 day'
ORDER BY pinned DESC, published DESC;
The view will provide a new endpoint:
.. tabs::
.. code-tab:: http
GET /fresh_stories HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/fresh_stories"
.. _logical_operators:
Logical operators
~~~~~~~~~~~~~~~~~
Multiple conditions on columns are evaluated using ``AND`` by default, but you can combine them using ``OR`` with the ``or`` operator. For example, to return people under 18 **or** over 21:
.. tabs::
.. code-tab:: http
GET /people?or=(age.lt.18,age.gt.21) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?or=(age.lt.18,age.gt.21)"
To **negate** any operator, you can prefix it with :code:`not` like :code:`?a=not.eq.2` or :code:`?not.and=(a.gte.0,a.lte.100)` .
You can also apply complex logic to the conditions:
.. tabs::
.. code-tab:: http
GET /people?grade=gte.90&student=is.true&or=(age.eq.14,not.and(age.gte.11,age.lte.17)) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?grade=gte.90&student=is.true&or=(age.eq.14,not.and(age.gte.11,age.lte.17))"
.. _modifiers:
Operator Modifiers
~~~~~~~~~~~~~~~~~~
You may further simplify the logic using the ``any/all`` modifiers of ``eq,like,ilike,gt,gte,lt,lte,match,imatch``.
For instance, to avoid repeating the same column for ``or``, use ``any`` to get people with last names that start with O or P:
.. tabs::
.. code-tab:: http
GET /people?last_name=like(any).{O*,P*} HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?last_name=like(any).{O*,P*}"
In a similar way, you can use ``all`` to avoid repeating the same column for ``and``. To get the people with last names that start with O and end with n:
.. tabs::
.. code-tab:: http
GET /people?last_name=like(all).{O*,*n} HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?last_name=like(all).{O*,*n}"
.. _pattern_matching:
Pattern Matching
~~~~~~~~~~~~~~~~
The pattern-matching operators (:code:`like`, :code:`ilike`, :code:`match`, :code:`imatch`) exist to support filtering data using patterns instead of concrete strings, as described in the `PostgreSQL docs <https://www.postgresql.org/docs/current/functions-matching.html>`__.
To ensure best performance on larger data sets, an `appropriate index <https://www.postgresql.org/docs/current/pgtrgm.html#PGTRGM-INDEX>`__ should be used and even then, it depends on the pattern value and actual data statistics whether an existing index will be used by the query planner or not.
.. _fts:
Full-Text Search
~~~~~~~~~~~~~~~~
The :code:`fts` filter mentioned above has a number of options to support flexible textual queries, namely the choice of plain vs phrase search and the language used for stemming. Suppose that :code:`tsearch` is a table with column :code:`my_tsv`, of type `tsvector <https://www.postgresql.org/docs/current/datatype-textsearch.html>`_. The following examples illustrate the possibilities.
.. tabs::
.. code-tab:: http
GET /tsearch?my_tsv=fts(french).amusant HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/tsearch?my_tsv=fts(french).amusant"
.. tabs::
.. code-tab:: http
GET /tsearch?my_tsv=plfts.The%20Fat%20Cats HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/tsearch?my_tsv=plfts.The%20Fat%20Cats"
.. tabs::
.. code-tab:: http
GET /tsearch?my_tsv=not.phfts(english).The%20Fat%20Cats HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/tsearch?my_tsv=not.phfts(english).The%20Fat%20Cats"
.. tabs::
.. code-tab:: http
GET /tsearch?my_tsv=not.wfts(french).amusant HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/tsearch?my_tsv=not.wfts(french).amusant"
Using `websearch_to_tsquery` requires PostgreSQL of version at least 11.0 and will raise an error in earlier versions of the database.
.. _v_filter:
Vertical Filtering
------------------
When certain columns are wide (such as those holding binary data), it is more efficient for the server to withhold them in a response. The client can specify which columns are required using the :code:`select` parameter.
.. tabs::
.. code-tab:: http
GET /people?select=first_name,age HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?select=first_name,age"
.. code-block:: json
[
{"first_name": "John", "age": 30},
{"first_name": "Jane", "age": 20}
]
The default is ``*``, meaning all columns. This value will become more important below in :ref:`resource_embedding`.
.. _renaming_columns:
Renaming Columns
~~~~~~~~~~~~~~~~
You can rename the columns by prefixing them with an alias followed by the colon ``:`` operator.
.. tabs::
.. code-tab:: http
GET /people?select=fullName:full_name,birthDate:birth_date HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?select=fullName:full_name,birthDate:birth_date"
.. code-block:: json
[
{"fullName": "John Doe", "birthDate": "04/25/1988"},
{"fullName": "Jane Doe", "birthDate": "01/12/1998"}
]
.. _casting_columns:
Casting Columns
~~~~~~~~~~~~~~~
Casting the columns is possible by suffixing them with the double colon ``::`` plus the desired type.
.. tabs::
.. code-tab:: http
GET /people?select=full_name,salary::text HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?select=full_name,salary::text"
.. code-block:: json
[
{"full_name": "John Doe", "salary": "90000.00"},
{"full_name": "Jane Doe", "salary": "120000.00"}
]
.. _json_columns:
JSON Columns
------------
You can specify a path for a ``json`` or ``jsonb`` column using the arrow operators(``->`` or ``->>``) as per the `PostgreSQL docs <https://www.postgresql.org/docs/current/functions-json.html>`__.
.. code-block:: postgres
CREATE TABLE people (
id int,
json_data json
);
.. tabs::
.. code-tab:: http
GET /people?select=id,json_data->>blood_type,json_data->phones HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?select=id,json_data->>blood_type,json_data->phones"
.. code-block:: json
[
{ "id": 1, "blood_type": "A-", "phones": [{"country_code": "61", "number": "917-929-5745"}] },
{ "id": 2, "blood_type": "O+", "phones": [{"country_code": "43", "number": "512-446-4988"}, {"country_code": "43", "number": "213-891-5979"}] }
]
.. tabs::
.. code-tab:: http
GET /people?select=id,json_data->phones->0->>number HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?select=id,json_data->phones->0->>number"
.. code-block:: json
[
{ "id": 1, "number": "917-929-5745"},
{ "id": 2, "number": "512-446-4988"}
]
This also works with filters:
.. tabs::
.. code-tab:: http
GET /people?select=id,json_data->blood_type&json_data->>blood_type=eq.A- HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?select=id,json_data->blood_type&json_data->>blood_type=eq.A-"
.. code-block:: json
[
{ "id": 1, "blood_type": "A-" },
{ "id": 3, "blood_type": "A-" },
{ "id": 7, "blood_type": "A-" }
]
Note that ``->>`` is used to compare ``blood_type`` as ``text``. To compare with an integer value use ``->``:
.. tabs::
.. code-tab:: http
GET /people?select=id,json_data->age&json_data->age=gt.20 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?select=id,json_data->age&json_data->age=gt.20"
.. code-block:: json
[
{ "id": 11, "age": 25 },
{ "id": 12, "age": 30 },
{ "id": 15, "age": 35 }
]
.. _composite_array_columns:
Composite / Array Columns
-------------------------
The arrow operators(``->``, ``->>``) can also be used for accessing composite fields and array elements.
.. code-block:: postgres
CREATE TYPE coordinates (
lat decimal(8,6),
long decimal(9,6)
);
CREATE TABLE countries (
id int,
location coordinates,
languages text[]
);
.. tabs::
.. code-tab:: http
GET /countries?select=id,location->>lat,location->>long,primary_language:languages->0&location->lat=gte.19 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/countries?select=id,location->>lat,location->>long,primary_language:languages->0&location->lat=gte.19"
.. code-block:: json
[
{
"id": 5,
"lat": "19.741755",
"long": "-155.844437",
"primary_language": "en"
}
]
.. important::
When using the ``->`` and ``->>`` operators on composite and array columns, PostgREST uses a query like ``to_jsonb(<col>)->'field'``. To make filtering and ordering on those nested fields use an index, the index needs to be created on the same expression, including the ``to_jsonb(...)`` call:
.. code-block:: postgres
CREATE INDEX ON mytable ((to_jsonb(data) -> 'identification' ->> 'registration_number'));
.. _ordering:
Ordering
--------
The reserved word ``order`` reorders the response rows. It uses a comma-separated list of columns and directions:
.. tabs::
.. code-tab:: http
GET /people?order=age.desc,height.asc HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?order=age.desc,height.asc"
If no direction is specified it defaults to ascending order:
.. tabs::
.. code-tab:: http
GET /people?order=age HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?order=age"
If you care where nulls are sorted, add ``nullsfirst`` or ``nullslast``:
.. tabs::
.. code-tab:: http
GET /people?order=age.nullsfirst HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?order=age.nullsfirst"
.. tabs::
.. code-tab:: http
GET /people?order=age.desc.nullslast HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?order=age.desc.nullslast"
You can also sort on fields of :ref:`composite_array_columns` or :ref:`json_columns`.
.. tabs::
.. code-tab:: http
GET /countries?order=location->>lat HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/countries?order=location->>lat"
.. _head_req:
HEAD
----
A HEAD method will behave identically to GET except that no body will be returned (`RFC 2616 <https://datatracker.ietf.org/doc/html/rfc2616#section-9.4>`_) .
As an optimization, the generated query won't execute an aggregate (to avoid unnecessary data transfer).
.. _insert:
Insert
======
All tables and `auto-updatable views <https://www.postgresql.org/docs/current/sql-createview.html#SQL-CREATEVIEW-UPDATABLE-VIEWS>`_ can be modified through the API, subject to permissions of the requester's database role.
To create a row in a database table post a JSON object whose keys are the names of the columns you would like to create. Missing properties will be set to default values when applicable.
.. tabs::
.. code-tab:: http
POST /table_name HTTP/1.1
{ "col1": "value1", "col2": "value2" }
.. code-tab:: bash Curl
curl "http://localhost:3000/table_name" \
-X POST -H "Content-Type: application/json" \
-d '{ "col1": "value1", "col2": "value2" }'
.. code::
HTTP/1.1 201 Created
No response body will be returned by default but you can use :ref:`prefer_return` to get the affected resource.
x-www-form-urlencoded
---------------------
URL encoded payloads can be posted with ``Content-Type: application/x-www-form-urlencoded``.
.. tabs::
.. code-tab:: http
POST /people HTTP/1.1
Content-Type: application/x-www-form-urlencoded
name=John+Doe&age=50&weight=80
.. code-tab:: bash Curl
curl "http://localhost:3000/people" \
-X POST -H "Content-Type: application/x-www-form-urlencoded" \
-d "name=John+Doe&age=50&weight=80"
.. note::
When inserting a row you must post a JSON object, not quoted JSON.
.. code::
Yes
{ "a": 1, "b": 2 }
No
"{ \"a\": 1, \"b\": 2 }"
Some JavaScript libraries will post the data incorrectly if you're not careful. For best results try one of the :ref:`clientside_libraries` built for PostgREST.
.. important::
It's recommended that you `use triggers instead of rules <https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_rules>`_.
Insertion on views with complex `rules <https://www.postgresql.org/docs/current/sql-createrule.html>`_ might not work out of the box with PostgREST due to its usage of CTEs.
If you want to keep using rules, a workaround is to wrap the view insertion in a stored procedure and call it through the :ref:`s_procs` interface.
For more details, see this `github issue <https://github.com/PostgREST/postgrest/issues/1283>`_.
.. _bulk_insert:
Bulk Insert
-----------
Bulk insert works exactly like single row insert except that you provide either a JSON array of objects having uniform keys, or lines in CSV format. This not only minimizes the HTTP requests required but uses a single INSERT statement on the back-end for efficiency.
To bulk insert CSV simply post to a table route with :code:`Content-Type: text/csv` and include the names of the columns as the first row. For instance
.. tabs::
.. code-tab:: http
POST /people HTTP/1.1
Content-Type: text/csv
name,age,height
J Doe,62,70
Jonas,10,55
.. code-tab:: bash Curl
curl "http://localhost:3000/people" \
-X POST -H "Content-Type: text/csv" \
--data-binary @- << EOF
name,age,height
J Doe,62,70
Jonas,10,55
EOF
An empty field (:code:`,,`) is coerced to an empty string and the reserved word :code:`NULL` is mapped to the SQL null value. Note that there should be no spaces between the column names and commas.
To bulk insert JSON post an array of objects having all-matching keys
.. tabs::
.. code-tab:: http
POST /people HTTP/1.1
Content-Type: application/json
[
{ "name": "J Doe", "age": 62, "height": 70 },
{ "name": "Janus", "age": 10, "height": 55 }
]
.. code-tab:: bash Curl
curl "http://localhost:3000/people" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
[
{ "name": "J Doe", "age": 62, "height": 70 },
{ "name": "Janus", "age": 10, "height": 55 }
]
EOF
.. _bulk_insert_default:
Bulk Insert with Default Values
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Any missing columns in the payload will be inserted as ``null`` values. To use the ``DEFAULT`` column value instead, use the ``Prefer: missing=default`` header.
Having:
.. code-block:: postgres
create table foo (
id bigint generated by default as identity primary key
, bar text
, baz int default 100
);
A request:
.. tabs::
.. code-tab:: http
POST /foo?columns=id,bar,baz HTTP/1.1
Content-Type: application/json
Prefer: missing=default, return=representation
[
{ "bar": "val1"
}
, { "bar": "val2"
, "baz": 15
}
]
.. code-tab:: bash Curl
curl "http://localhost:3000/foo?columns=id,bar,baz" \
-H "Content-Type: application/json" \
-H "Prefer: missing=default, return=representation" \
-d @- << EOF
[
{ "bar": "val1"
}
, { "bar": "val2"
, "baz": 15
}
]
EOF
Will result in:
.. code-block:: json
[
{ "id": 1
, "bar": "val1"
, "baz": 100
}
, { "id": 2
, "bar": "val2"
, "baz": 15
}
]
.. _specify_columns:
Specifying Columns
------------------
By using the :code:`columns` query parameter it's possible to specify the payload keys that will be inserted and ignore the rest of the payload.
.. tabs::
.. code-tab:: http
POST /datasets?columns=source,publication_date,figure HTTP/1.1
Content-Type: application/json
{
"source": "Natural Disaster Prevention and Control",
"publication_date": "2015-09-11",
"figure": 1100,
"location": "...",
"comment": "...",
"extra": "...",
"stuff": "..."
}
.. code-tab:: bash Curl
curl "http://localhost:3000/datasets?columns=source,publication_date,figure" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
{
"source": "Natural Disaster Prevention and Control",
"publication_date": "2015-09-11",
"figure": 1100,
"location": "...",
"comment": "...",
"extra": "...",
"stuff": "..."
}
EOF
In this case, only **source**, **publication_date** and **figure** will be inserted. The rest of the JSON keys will be ignored.
Using this also has the side-effect of being more efficient for :ref:`bulk_insert` since PostgREST will not process the JSON and
it'll send it directly to PostgreSQL.
.. _update:
Update
======
To update a row or rows in a table, use the PATCH verb. Use :ref:`h_filter` to specify which record(s) to update. Here is an example query setting the :code:`category` column to child for all people below a certain age.
.. tabs::
.. code-tab:: http
PATCH /people?age=lt.13 HTTP/1.1
{ "category": "child" }
.. code-tab:: bash Curl
curl "http://localhost:3000/people?age=lt.13" \
-X PATCH -H "Content-Type: application/json" \
-d '{ "category": "child" }'
Updates also support :ref:`prefer_return` plus :ref:`v_filter`.
.. warning::
Beware of accidentally updating every row in a table. To learn to prevent that see :ref:`block_fulltable`.
.. _prefer_resolution:
.. _upsert:
Upsert
======
You can make an upsert with :code:`POST` and the :code:`Prefer: resolution=merge-duplicates` header:
.. tabs::
.. code-tab:: http
POST /employees HTTP/1.1
Prefer: resolution=merge-duplicates
[
{ "id": 1, "name": "Old employee 1", "salary": 30000 },
{ "id": 2, "name": "Old employee 2", "salary": 42000 },
{ "id": 3, "name": "New employee 3", "salary": 50000 }
]
.. code-tab:: bash Curl
curl "http://localhost:3000/employees" \
-X POST -H "Content-Type: application/json" \
-H "Prefer: resolution=merge-duplicates" \
-d @- << EOF
[
{ "id": 1, "name": "Old employee 1", "salary": 30000 },
{ "id": 2, "name": "Old employee 2", "salary": 42000 },
{ "id": 3, "name": "New employee 3", "salary": 50000 }
]
EOF
By default, upsert operates based on the primary key columns, you must specify all of them. You can also choose to ignore the duplicates with :code:`Prefer: resolution=ignore-duplicates`. This works best when the primary key is natural, but it's also possible to use it if the primary key is surrogate (example: "id serial primary key"). For more details read `this issue <https://github.com/PostgREST/postgrest/issues/1118>`_.
.. important::
After creating a table or changing its primary key, you must refresh PostgREST schema cache for upsert to work properly. To learn how to refresh the cache see :ref:`schema_reloading`.
.. _on_conflict:
On Conflict
-----------
By specifying the ``on_conflict`` query parameter, you can make upsert work on a column(s) that has a UNIQUE constraint.
.. tabs::
.. code-tab:: http
POST /employees?on_conflict=name HTTP/1.1
Prefer: resolution=merge-duplicates
[
{ "name": "Old employee 1", "salary": 40000 },
{ "name": "Old employee 2", "salary": 52000 },
{ "name": "New employee 3", "salary": 60000 }
]
.. code-tab:: bash Curl
curl "http://localhost:3000/employees?on_conflict=name" \
-X POST -H "Content-Type: application/json" \
-H "Prefer: resolution=merge-duplicates" \
-d @- << EOF
[
{ "name": "Old employee 1", "salary": 40000 },
{ "name": "Old employee 2", "salary": 52000 },
{ "name": "New employee 3", "salary": 60000 }
]
EOF
.. _upsert_put:
PUT
---
A single row upsert can be done by using :code:`PUT` and filtering the primary key columns with :code:`eq`:
.. tabs::
.. code-tab:: http
PUT /employees?id=eq.4 HTTP/1.1
{ "id": 4, "name": "Sara B.", "salary": 60000 }
.. code-tab:: bash Curl
curl "http://localhost/employees?id=eq.4" \
-X PUT -H "Content-Type: application/json" \
-d '{ "id": 4, "name": "Sara B.", "salary": 60000 }'
All the columns must be specified in the request body, including the primary key columns.
.. _delete:
Delete
======
To delete rows in a table, use the DELETE verb plus :ref:`h_filter`. For instance deleting inactive users:
.. tabs::
.. code-tab:: http
DELETE /user?active=is.false HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/user?active=is.false" -X DELETE
Deletions also support :ref:`prefer_return` plus :ref:`v_filter`.
.. tabs::
.. code-tab:: http
DELETE /user?id=eq.1 HTTP/1.1
Prefer: return=representation
.. code-tab:: bash Curl
curl "http://localhost:3000/user?id=eq.1" -X DELETE \
-H "Prefer: return=representation"
.. code-block:: json
{"id": 1, "email": "johndoe@email.com"}
.. warning::
Beware of accidentally deleting all rows in a table. To learn to prevent that see :ref:`block_fulltable`.
.. _limited_update_delete:
Limited Update/Delete
=====================
You can limit the amount of affected rows by :ref:`update` or :ref:`delete` with the ``limit`` query parameter. For this, you must add an explicit ``order`` on a unique column(s).
.. tabs::
.. code-tab:: http
PATCH /users?limit=10&order=id&last_login=lt.2017-01-01 HTTP/1.1
{ "status": "inactive" }
.. code-tab:: bash Curl
curl -X PATCH "/users?limit=10&order=id&last_login=lt.2020-01-01" \
-H "Content-Type: application/json" \
-d '{ "status": "inactive" }'
.. tabs::
.. code-tab:: http
DELETE /users?limit=10&order=id&status=eq.inactive HTTP/1.1
.. code-tab:: bash Curl
curl -X DELETE "http://localhost:3000/users?limit=10&order=id&status=eq.inactive"
If your table has no unique columns, you can use the `ctid <https://www.postgresql.org/docs/current/ddl-system-columns.html>`_ system column.
Using ``offset`` to target a different subset of rows is also possible.
.. note::
There is no native ``UPDATE...LIMIT`` or ``DELETE...LIMIT`` support in PostgreSQL; the generated query simulates that behavior and is based on `this Crunchy Data blog post <https://www.crunchydata.com/blog/simulating-update-or-delete-with-limit-in-postgres-ctes-to-the-rescue>`_.
.. raw:: html
<script type="text/javascript">
let hash = window.location.hash;
const redirects = {
// Tables and Views
'#computed-virtual-columns': 'computed_fields.html#computed-fields',
'#limits-and-pagination': 'pagination_count.html#limits-and-pagination',
'#exact-count': 'pagination_count.html#exact-count',
'#planned-count': 'pagination_count.html#planned-count',
'#estimated-count': 'pagination_count.html#estimated-count',
'#prefer-return-headers-only': 'preferences.html#headers-only',
'#prefer-return-representation': 'preferences.html#full',
};
let willRedirectTo = redirects[hash];
if (willRedirectTo) {
window.location.href = willRedirectTo;
}
</script>
+110
View File
@@ -0,0 +1,110 @@
.. note::
This page is a work in progress.
.. _url_grammar:
URL Grammar
===========
.. _custom_queries:
Custom Queries
--------------
The PostgREST URL grammar limits the kinds of queries clients can perform. It prevents arbitrary, potentially poorly constructed and slow client queries. It's good for quality of service, but means database administrators must create custom views and stored procedures to provide richer endpoints. The most common causes for custom endpoints are
* Table unions
* More complicated joins than those provided by :ref:`resource_embedding`.
* Geo-spatial queries that require an argument, like "points near (lat,lon)"
Unicode support
---------------
PostgREST supports unicode in schemas, tables, columns and values. To access a table with unicode name, use percent encoding.
To request this:
.. code-block:: http
GET /موارد HTTP/1.1
Do this:
.. tabs::
.. code-tab:: http
GET /%D9%85%D9%88%D8%A7%D8%B1%D8%AF HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/%D9%85%D9%88%D8%A7%D8%B1%D8%AF"
.. _tabs-cols-w-spaces:
Table / Columns with spaces
~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can request table/columns with spaces in them by percent encoding the spaces with ``%20``:
.. tabs::
.. code-tab:: http
GET /Order%20Items?Unit%20Price=lt.200 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/Order%20Items?Unit%20Price=lt.200"
.. _reserved-chars:
Reserved characters
~~~~~~~~~~~~~~~~~~~
If filters include PostgREST reserved characters(``,``, ``.``, ``:``, ``()``) you'll have to surround them in percent encoded double quotes ``%22`` for correct processing.
Here ``Hebdon,John`` and ``Williams,Mary`` are values.
.. tabs::
.. code-tab:: http
GET /employees?name=in.(%22Hebdon,John%22,%22Williams,Mary%22) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/employees?name=in.(%22Hebdon,John%22,%22Williams,Mary%22)"
Here ``information.cpe`` is a column name.
.. tabs::
.. code-tab:: http
GET /vulnerabilities?%22information.cpe%22=like.*MS* HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/vulnerabilities?%22information.cpe%22=like.*MS*"
If the value filtered by the ``in`` operator has a double quote (``"``), you can escape it using a backslash ``"\""``. A backslash itself can be used with a double backslash ``"\\"``.
Here ``Quote:"`` and ``Backslash:\`` are percent-encoded values. Note that ``%5C`` is the percent-encoded backslash.
.. tabs::
.. code-tab:: http
GET /marks?name=in.(%22Quote:%5C%22%22,%22Backslash:%5C%5C%22) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/marks?name=in.(%22Quote:%5C%22%22,%22Backslash:%5C%5C%22)"
.. note::
Some HTTP libraries might encode URLs automatically(e.g. :code:`axios`). In these cases you should use double quotes
:code:`""` directly instead of :code:`%22`.
+209
View File
@@ -0,0 +1,209 @@
.. _authn:
Authentication
==============
PostgREST is designed to keep the database at the center of API security. All :ref:`authorization happens in the database <db_authz>` . It is PostgREST's job to **authenticate** requests -- i.e. verify that a client is who they say they are -- and then let the database **authorize** client actions.
.. _roles:
Overview of role system
-----------------------
There are three types of roles used by PostgREST, the **authenticator**, **anonymous** and **user** roles. The database administrator creates these roles and configures PostgREST to use them.
.. image:: ../_static/security-roles.png
The authenticator role is used for connecting to the database and should be configured to have very limited access. It is a chameleon whose job is to "become" other users to service authenticated HTTP requests.
.. code:: sql
CREATE ROLE authenticator LOGIN NOINHERIT NOCREATEDB NOCREATEROLE NOSUPERUSER;
CREATE ROLE anonymous NOLOGIN;
CREATE ROLE webuser NOLOGIN;
.. note::
The names "authenticator" and "anon" names are configurable and not sacred, we simply choose them for clarity. See :ref:`db-uri` and :ref:`db-anon-role`.
.. _user_impersonation:
User Impersonation
------------------
The picture below shows how the server handles authentication. If auth succeeds, it switches into the user role specified by the request, otherwise it switches into the anonymous role (if it's set in :ref:`db-anon-role`).
.. image:: ../_static/security-anon-choice.png
This role switching mechanism is called **user impersonation**. In PostgreSQL it's done with the ``SET ROLE`` statement.
.. note::
The impersonated roles will have their settings applied. See :ref:`impersonated_settings`.
.. _jwt_impersonation:
JWT-Based User Impersonation
----------------------------
We use `JSON Web Tokens <https://jwt.io/>`_ to authenticate API requests, this allows us to be stateless and not require database lookups for verification. As you'll recall a JWT contains a list of cryptographically signed claims. All claims are allowed but PostgREST cares specifically about a claim called role.
.. code:: json
{
"role": "user123"
}
When a request contains a valid JWT with a role claim PostgREST will switch to the database role with that name for the duration of the HTTP request.
.. code:: sql
SET LOCAL ROLE user123;
Note that the database administrator must allow the authenticator role to switch into this user by previously executing
.. code:: sql
GRANT user123 TO authenticator;
-- similarly for the anonymous role
-- GRANT anonymous TO authenticator;
If the client included no JWT (or one without a role claim) then PostgREST switches into the anonymous role. The database administrator must set the anonymous role permissions correctly to prevent anonymous users from seeing or changing things they shouldn't.
.. _jwt_generation:
JWT Generation
~~~~~~~~~~~~~~
You can create a valid JWT either from inside your database (see :ref:`sql_user_management`) or via an external service (see :ref:`external_jwt`).
.. _client_auth:
Client Auth
~~~~~~~~~~~
To make an authenticated request the client must include an :code:`Authorization` HTTP header with the value :code:`Bearer <jwt>`. For instance:
.. tabs::
.. code-tab:: http
GET /foo HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiamRvZSIsImV4cCI6MTQ3NTUxNjI1MH0.GYDZV3yM0gqvuEtJmfpplLBXSGYnke_Pvnl0tbKAjB4
.. code-tab:: bash Curl
curl "http://localhost:3000/foo" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiamRvZSIsImV4cCI6MTQ3NTUxNjI1MH0.GYDZV3yM0gqvuEtJmfpplLBXSGYnke_Pvnl0tbKAjB4"
The ``Bearer`` header value can be used with or without capitalization(``bearer``).
.. _jwt_caching:
JWT Caching
-----------
PostgREST validates ``JWTs`` on every request. We can cache ``JWTs`` to avoid this performance overhead.
To enable JWT caching, the config :code:`jwt-cache-max-lifetime` is to be set. It is the maximum number of seconds for which the cache stores the JWT validation results. The cache uses the :code:`exp` claim to set the cache entry lifetime. If the JWT does not have an :code:`exp` claim, it uses the config value. See :ref:`jwt-cache-max-lifetime` for more details.
.. note::
You can use the :ref:`server-timing_header` to see the effect of JWT caching.
Symmetric Keys
~~~~~~~~~~~~~~
Each token is cryptographically signed with a secret key. In the case of symmetric cryptography the signer and verifier share the same secret passphrase, which can be configured with :ref:`jwt-secret`.
If it is set to a simple string value like “reallyreallyreallyreallyverysafe” then PostgREST interprets it as an HMAC-SHA256 passphrase.
.. _asym_keys:
Asymmetric Keys
~~~~~~~~~~~~~~~
In asymmetric cryptography the signer uses the private key and the verifier the public key.
As described in the :ref:`configuration` section, PostgREST accepts a ``jwt-secret`` config file parameter. However you can also specify a literal JSON Web Key (JWK) or set. For example, you can use an RSA-256 public key encoded as a JWK:
.. code-block:: json
{
"alg":"RS256",
"e":"AQAB",
"key_ops":["verify"],
"kty":"RSA",
"n":"9zKNYTaYGfGm1tBMpRT6FxOYrM720GhXdettc02uyakYSEHU2IJz90G_MLlEl4-WWWYoS_QKFupw3s7aPYlaAjamG22rAnvWu-rRkP5sSSkKvud_IgKL4iE6Y2WJx2Bkl1XUFkdZ8wlEUR6O1ft3TS4uA-qKifSZ43CahzAJyUezOH9shI--tirC028lNg767ldEki3WnVr3zokSujC9YJ_9XXjw2hFBfmJUrNb0-wldvxQbFU8RPXip-GQ_JPTrCTZhrzGFeWPvhA6Rqmc3b1PhM9jY7Dur1sjYWYVyXlFNCK3c-6feo5WlRfe1aCWmwZQh6O18eTmLeT4nWYkDzQ"
}
.. note::
This could also be a JSON Web Key Set (JWKS) if it was contained within an array assigned to a `keys` member, e.g. ``{ keys: [jwk1, jwk2] }``.
Just pass it in as a single line string, escaping the quotes:
.. code-block:: ini
jwt-secret = "{ \"alg\":\"RS256\", … }"
To generate such a public/private key pair use a utility like `latchset/jose <https://github.com/latchset/jose>`_.
.. code-block:: bash
jose jwk gen -i '{"alg": "RS256"}' -o rsa.jwk
jose jwk pub -i rsa.jwk -o rsa.jwk.pub
# now rsa.jwk.pub contains the desired JSON object
You can specify the literal value as we saw earlier, or reference a filename to load the JWK from a file:
.. code-block:: ini
jwt-secret = "@rsa.jwk.pub"
JWT Claims Validation
~~~~~~~~~~~~~~~~~~~~~
PostgREST honors the :code:`exp` claim for token expiration, rejecting expired tokens.
JWT Security
~~~~~~~~~~~~
There are at least three types of common critiques against using JWT: 1) against the standard itself, 2) against using libraries with known security vulnerabilities, and 3) against using JWT for web sessions. We'll briefly explain each critique, how PostgREST deals with it, and give recommendations for appropriate user action.
The critique against the `JWT standard <https://datatracker.ietf.org/doc/html/rfc7519>`_ is voiced in detail `elsewhere on the web <https://web.archive.org/web/20230123041631/https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid>`_. The most relevant part for PostgREST is the so-called :code:`alg=none` issue. Some servers implementing JWT allow clients to choose the algorithm used to sign the JWT. In this case, an attacker could set the algorithm to :code:`none`, remove the need for any signature at all and gain unauthorized access. The current implementation of PostgREST, however, does not allow clients to set the signature algorithm in the HTTP request, making this attack irrelevant. The critique against the standard is that it requires the implementation of the :code:`alg=none` at all.
Critiques against JWT libraries are only relevant to PostgREST via the library it uses. As mentioned above, not allowing clients to choose the signature algorithm in HTTP requests removes the greatest risk. Another more subtle attack is possible where servers use asymmetric algorithms like RSA for signatures. Once again this is not relevant to PostgREST since it is not supported. Curious readers can find more information in `this article <https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/>`_. Recommendations about high quality libraries for usage in API clients can be found on `jwt.io <https://jwt.io/>`_.
The last type of critique focuses on the misuse of JWT for maintaining web sessions. The basic recommendation is to `stop using JWT for sessions <http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/>`_ because most, if not all, solutions to the problems that arise when you do, `do not work <http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/>`_. The linked articles discuss the problems in depth but the essence of the problem is that JWT is not designed to be secure and stateful units for client-side storage and therefore not suited to session management.
PostgREST uses JWT mainly for authentication and authorization purposes and encourages users to do the same. For web sessions, using cookies over HTTPS is good enough and well catered for by standard web frameworks.
.. _custom_validation:
Custom Validation
-----------------
PostgREST does not enforce any extra constraints besides JWT validation. An example of an extra constraint would be to immediately revoke access for a certain user. Using :ref:`db-pre-request` you can specify a stored procedure to call immediately after :ref:`user_impersonation` and before the main query itself runs.
.. code:: ini
db-pre-request = "public.check_user"
In the function you can run arbitrary code to check the request and raise an exception(see :ref:`raise_error`) to block it if desired. Here you can take advantage of :ref:`guc_req_headers_cookies_claims` for
doing custom logic based on the web user info.
.. code-block:: postgres
CREATE OR REPLACE FUNCTION check_user() RETURNS void AS $$
DECLARE
email text := current_setting('request.jwt.claims', true)::json->>'email';
BEGIN
IF email = 'evil.user@malicious.com' THEN
RAISE EXCEPTION 'No, you are evil'
USING HINT = 'Stop being so evil and maybe you can log in';
END IF;
END
$$ LANGUAGE plpgsql;
+879
View File
@@ -0,0 +1,879 @@
.. _configuration:
Configuration
#############
Configuration parameters can be provided via:
- :ref:`file_config`.
- :ref:`env_variables_config`, overriding values from the config file.
- :ref:`in_db_config`, overriding values from both the config file and environment variables.
Using :ref:`config_reloading` you can modify the parameters without restarting the server.
Minimum parameters
==================
The server is able to start without any config parameters, but it won't be able to serve requests unless it has :ref:`a role to serve anonymous requests with <db-anon-role>` - or :ref:`a secret to use for JWT authentication <jwt-secret>`.
.. _file_config:
Config File
===========
There is no predefined location for the config file, you must specify the file path as the one and only argument to the server:
.. code:: bash
./postgrest /path/to/postgrest.conf
The configuration file must contain a set of key value pairs:
.. code::
# postgrest.conf
# The standard connection URI format, documented at
# https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
db-uri = "postgres://user:pass@host:5432/dbname"
# The database role to use when no client authentication is provided.
# Should differ from authenticator
db-anon-role = "anon"
# The secret to verify the JWT for authenticated requests with.
# Needs to be 32 characters minimum.
jwt-secret = "reallyreallyreallyreallyverysafe"
jwt-secret-is-base64 = false
# Port the postgrest process is listening on for http requests
server-port = 3000
You can run ``postgrest --example`` to display all possible configuration parameters and how to use them in a configuration file.
.. _env_variables_config:
Environment Variables
=====================
Environment variables are capitalized, have a ``PGRST_`` prefix, and use underscores. For example: ``PGRST_DB_URI`` corresponds to ``db-uri`` and ``PGRST_APP_SETTINGS_*`` to ``app.settings.*``.
`libpq environment variables <https://www.postgresql.org/docs/current/libpq-envars.html>`_ are also supported for constructing the connection string, see :ref:`db-uri`.
See the full list of environment variable names on :ref:`config_full_list`.
.. _in_db_config:
In-Database Configuration
=========================
You can also configure the server with database settings by using a :ref:`pre-config <db-pre-config>` function. For example, you can configure :ref:`db-schemas` and :ref:`jwt-secret` like this:
.. code-block::
# postgrest.conf
db-pre-config = "postgrest.pre_config"
# or env vars
PGRST_DB_PRE_CONFIG = "postgrest.pre_config"
.. code-block:: postgresql
-- create a dedicated schema, hidden from the API
create schema postgrest;
-- grant usage on this schema to the authenticator
grant usage on schema postgrest to authenticator;
-- the function can configure postgREST by using set_config
create or replace function postgrest.pre_config()
returns void as $$
select
set_config('pgrst.db_schemas', 'schema1, schema2', true)
, set_config('pgrst.jwt_secret', 'REALLYREALLYREALLYREALLYVERYSAFE', true);
$$ language sql;
Note that underscores(``_``) need to be used instead of dashes(``-``) for the in-database config parameters. See the full list of in-database names on :ref:`config_full_list`.
You can disable the in-database configuration by setting :ref:`db-config` to ``false``.
.. note::
For backwards compatibility, you can do in-db config by modifying the :ref:`authenticator role <roles>`. This is no longer recommended as it requires SUPERUSER.
.. code:: postgresql
ALTER ROLE authenticator SET pgrst.db_schemas = "tenant1, tenant2, tenant3"
ALTER ROLE authenticator IN DATABASE <your_database_name> SET pgrst.db_schemas = "tenant4, tenant5" -- database-specific setting, overrides the previous setting
.. _config_reloading:
Configuration Reloading
=======================
It's possible to reload PostgREST's configuration without restarting the server. You can do this :ref:`via signal <config_reloading_signal>` or :ref:`via notification <config_reloading_notify>`.
- Any modification to the :ref:`file_config` will be applied during reload.
- Any modification to the :ref:`in_db_config` will be applied during reload.
- Not all settings are reloadable, see the reloadable list on :ref:`config_full_list`.
- It's not possible to change :ref:`env_variables_config` for a running process, hence reloading a Docker container configuration will not work. In these cases, you can restart the process or use :ref:`in_db_config`.
.. _config_reloading_signal:
Reload with signal
------------------
To reload the configuration via signal, send a SIGUSR2 signal to the server process.
.. code:: bash
killall -SIGUSR2 postgrest
.. _config_reloading_notify:
Reload with NOTIFY
------------------
To reload the configuration from within the database, you can use a NOTIFY command.
.. code:: postgresql
NOTIFY pgrst, 'reload config'
The ``"pgrst"`` notification channel is enabled by default. You can name the channel with :ref:`db-channel` and enable or disable it with :ref:`db-channel-enabled`.
.. _config_full_list:
List of parameters
==================
.. _admin-server-port:
admin-server-port
-----------------
=============== =======================
**Type** Int
**Default** `n/a`
**Reloadable** N
**Environment** PGRST_ADMIN_SERVER_PORT
**In-Database** `n/a`
=============== =======================
Specifies the port for the :ref:`health_check` endpoints.
.. _app.settings.*:
app.settings.*
--------------
=============== =======================
**Type** String
**Default** `n/a`
**Reloadable** &
**Environment** PGRST_APP_SETTINGS_*
**In-Database** `n/a`
=============== =======================
Arbitrary settings that can be used to pass in secret keys directly as strings, or via OS environment variables. For instance: :code:`app.settings.jwt_secret = "$(MYAPP_JWT_SECRET)"` will take :code:`MYAPP_JWT_SECRET` from the environment and make it available to postgresql functions as :code:`current_setting('app.settings.jwt_secret')`.
.. _db-aggregates-enabled:
db-aggregates-enabled
---------------------
=============== =======================
**Type** Boolean
**Default** False
**Reloadable** Y
**Environment** PGRST_DB_AGGREGATES_ENABLED
**In-Database** pgrst.db_aggregates_enabled
=============== =======================
When this is set to :code:`true`, the use of :ref:`aggregate_functions` is allowed.
It is recommended that this be set to ``false`` unless proper safeguards are in place to prevent potential performance problems from arising. For example, it is possible that a user may request the ``max()`` of an unindexed column in a table with millions of rows. At best, this would result in a slow query, and at worst, it could be abused to prevent other users from accessing your API (i.e. a form of denial-of-service attack.)
Proper safeguards could include:
- Use of a statement timeout. See :ref:`impersonated_settings`.
- Use of the `pg_plan_filter extension <https://github.com/pgexperts/pg_plan_filter>`_ to block excessively expensive queries.
.. _db-anon-role:
db-anon-role
------------
=============== =======================
**Type** String
**Default** `n/a`
**Reloadable** Y
**Environment** PGRST_DB_ANON_ROLE
**In-Database** pgrst.db_anon_role
=============== =======================
The database role to use when executing commands on behalf of unauthenticated clients. For more information, see :ref:`roles`.
When unset anonymous access will be blocked.
.. _db-channel:
db-channel
----------
=============== =======================
**Type** String
**Default** pgrst
**Reloadable** Y
**Environment** PGRST_DB_CHANNEL
**In-Database** `n/a`
=============== =======================
The name of the notification channel that PostgREST uses for :ref:`schema_reloading` and configuration reloading.
.. _db-channel-enabled:
db-channel-enabled
------------------
=============== =======================
**Type** Boolean
**Default** True
**Reloadable** Y
**Environment** PGRST_DB_CHANNEL_ENABLED
**In-Database** `n/a`
=============== =======================
When this is set to :code:`true`, the notification channel specified in :ref:`db-channel` is enabled.
You should set this to ``false`` when using PostgresSQL behind an external connection pooler such as PgBouncer working in transaction pooling mode. See :ref:`this section <external_connection_poolers>` for more information.
.. _db-config:
db-config
---------
=============== =======================
**Type** Boolean
**Default** True
**Reloadable** Y
**Environment** PGRST_DB_CONFIG
**In-Database** `n/a`
=============== =======================
Enables the in-database configuration.
.. _db-pre-config:
db-pre-config
-------------
=============== =======================
**Type** String
**Default** `n/a`
**Reloadable** Y
**Environment** PGRST_DB_PRE_CONFIG
**In-Database** pgrst.db_pre_config
=============== =======================
Name of the function that does :ref:`in_db_config`.
.. _db-extra-search-path:
db-extra-search-path
--------------------
=============== ==========================
**Type** String
**Default** public
**Reloadable** Y
**Environment** PGRST_DB_EXTRA_SEARCH_PATH
**In-Database** pgrst.db_extra_search_path
=============== ==========================
Extra schemas to add to the `search_path <https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH>`_ of every request. These schemas tables, views and stored procedures **don't get API endpoints**, they can only be referred from the database objects inside your :ref:`db-schemas`.
This parameter was meant to make it easier to use **PostgreSQL extensions** (like PostGIS) that are outside of the :ref:`db-schemas`.
Multiple schemas can be added in a comma-separated string, e.g. ``public, extensions``.
.. _db-max-rows:
db-max-rows
-----------
=============== ==========================
**Type** Int
**Default**
**Reloadable** Y
**Environment** PGRST_DB_MAX_ROWS
**In-Database** pgrst.db_max_rows
=============== ==========================
*For backwards compatibility, this config parameter is also available without prefix as "max-rows".*
A hard limit to the number of rows PostgREST will fetch from a view, table, or stored procedure. Limits payload size for accidental or malicious requests.
.. _db-plan-enabled:
db-plan-enabled
---------------
=============== ==========================
**Type** Boolean
**Default** False
**Reloadable** Y
**Environment** PGRST_DB_PLAN_ENABLED
**In-Database** pgrst.db_plan_enabled
=============== ==========================
When this is set to :code:`true`, the execution plan of a request can be retrieved by using the :code:`Accept: application/vnd.pgrst.plan` header. See :ref:`explain_plan`.
.. _db-pool:
db-pool
-------
=============== ==========================
**Type** Int
**Default** 10
**Reloadable** N
**Environment** PGRST_DB_POOL
**In-Database** n/a
=============== ==========================
Number of maximum connections to keep open in PostgREST's database pool.
.. _db-pool-acquisition-timeout:
db-pool-acquisition-timeout
---------------------------
=============== =================================
**Type** Int
**Default** 10
**Reloadable** N
**Environment** PGRST_DB_POOL_ACQUISITION_TIMEOUT
**In-Database** `n/a`
=============== =================================
Specifies the maximum time in seconds that the request will wait for the pool to free up a connection slot to the database.
.. _db-pool-max-idletime:
db-pool-max-idletime
--------------------
=============== =================================
**Type** Int
**Default** 30
**Reloadable** N
**Environment** PGRST_DB_POOL_MAX_IDLETIME
**In-Database** `n/a`
=============== =================================
*For backwards compatibility, this config parameter is also available as “db-pool-timeout”.*
Time in seconds to close idle pool connections.
.. _db-pool-max-lifetime:
db-pool-max-lifetime
--------------------
=============== =================================
**Type** Int
**Default** 1800
**Reloadable** N
**Environment** PGRST_DB_POOL_MAX_LIFETIME
**In-Database** `n/a`
=============== =================================
Specifies the maximum time in seconds of an existing connection in the pool.
.. _db-pool-automatic-recovery:
db-pool-automatic-recovery
--------------------------
=============== =================================
**Type** Boolean
**Default** True
**Reloadable** Y
**Environment** PGRST_DB_POOL_AUTOMATIC_RECOVERY
**In-Database** `n/a`
=============== =================================
Enables or disables connection retrying.
When disabled, PostgREST would terminate immediately after connection loss instead of retrying indefinitely. See :ref:`this section <automatic_recovery>` for more information.
.. _db-pre-request:
db-pre-request
--------------
=============== =================================
**Type** String
**Default** `n/a`
**Reloadable** Y
**Environment** PGRST_DB_PRE_REQUEST
**In-Database** pgrst.db_pre_request
=============== =================================
*For backwards compatibility, this config parameter is also available without prefix as "pre-request".*
A schema-qualified stored procedure name to call right after the :ref:`tx_settings` are set. See :ref:`pre-request`.
.. _db-prepared-statements:
db-prepared-statements
----------------------
=============== =================================
**Type** Boolean
**Default** True
**Reloadable** Y
**Environment** PGRST_DB_PREPARED_STATEMENTS
**In-Database** pgrst.db_prepared_statements
=============== =================================
Enables or disables prepared statements.
When disabled, the generated queries will be parameterized (invulnerable to SQL injection) but they will not be prepared (cached in the database session). Not using prepared statements will noticeably decrease performance, so it's recommended to always have this setting enabled.
You should only set this to ``false`` when using PostgresSQL behind an external connection pooler such as PgBouncer working in transaction pooling mode. See :ref:`this section <external_connection_poolers>` for more information.
.. _db-root-spec:
db-root-spec
------------
=============== =================================
**Type** String
**Default** `n/a`
**Reloadable** Y
**Environment** PGRST_DB_ROOT_SPEC
**In-Database** pgrst.db_root_spec
=============== =================================
Function to override the OpenAPI response. See :ref:`override_openapi`.
.. _db-schemas:
db-schemas
----------
=============== =================================
**Type** String
**Default** public
**Reloadable** Y
**Environment** PGRST_DB_SCHEMAS
**In-Database** pgrst.db_schemas
=============== =================================
*For backwards compatibility, this config parameter is also available in singular as "db-schema".*
The list of database schemas to expose to clients. See :ref:`schemas`.
.. _db-tx-end:
db-tx-end
---------
=============== =================================
**Type** String
**Default** commit
**Reloadable** N
**Environment** PGRST_DB_TX_END
**In-Database** `n/a`
=============== =================================
Specifies how to terminate the database transactions.
.. code:: bash
# The transaction is always committed
db-tx-end = "commit"
# The transaction is committed unless a "Prefer: tx=rollback" header is sent
db-tx-end = "commit-allow-override"
# The transaction is always rolled back
db-tx-end = "rollback"
# The transaction is rolled back unless a "Prefer: tx=commit" header is sent
db-tx-end = "rollback-allow-override"
.. _db-uri:
db-uri
------
=============== =================================
**Type** String
**Default** postgresql://
**Reloadable** N
**Environment** PGRST_DB_URI
**In-Database** `n/a`
=============== =================================
The standard `PostgreSQL connection string <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>`_, there are different ways to specify it:
URI Format
~~~~~~~~~~
.. code::
"postgres://authenticator:mysecretpassword@localhost:5433/postgres?parameters=val"
- Under this format symbols and unusual characters in the password or other fields should be percent encoded to avoid a parse error.
- If enforcing an SSL connection to the database is required you can use `sslmode <https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS>`_ in the URI, for example ``postgres://user:pass@host:5432/dbname?sslmode=require``.
- The user with whom PostgREST connects to the database is also known as the ``authenticator`` role. For more information see :ref:`roles`.
- When running PostgREST on the same machine as PostgreSQL, it is also possible to connect to the database using a `Unix socket <https://en.wikipedia.org/wiki/Unix_domain_socket>`_ and the `Peer Authentication method <https://www.postgresql.org/docs/current/auth-peer.html>`_ as an alternative to TCP/IP communication and authentication with a password, this also grants higher performance. To do this you can omit the host and the password, e.g. ``postgres://user@/dbname``, see the `libpq connection string <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>`_ documentation for more details.
Keyword/Value Format
~~~~~~~~~~~~~~~~~~~~
.. code::
"host=localhost port=5433 user=authenticator password=mysecretpassword dbname=postgres"
LIBPQ Environment Variables
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code::
PGHOST=localhost PGPORT=5433 PGUSER=authenticator PGDATABASE=postgres
Any parameter that is not set in the above formats is read from `libpq environment variables <https://www.postgresql.org/docs/current/libpq-envars.html>`_. The default connection string is ``postgresql://``, which reads **all** parameters from the environment.
External config file
~~~~~~~~~~~~~~~~~~~~
Choosing a value for this parameter beginning with the at sign such as ``@filename`` (e.g. ``@./configs/my-config``) loads the connection string out of an external file.
.. _jwt-aud:
jwt-aud
-------
=============== =================================
**Type** String
**Default** `n/a`
**Reloadable** Y
**Environment** PGRST_JWT_AUD
**In-Database** pgrst.jwt_aud
=============== =================================
Specifies the `JWT audience claim <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3>`_. If this claim is present in the client provided JWT then you must set this to the same value as in the JWT, otherwise verifying the JWT will fail.
.. _jwt-role-claim-key:
jwt-role-claim-key
------------------
=============== =================================
**Type** String
**Default** .role
**Reloadable** Y
**Environment** PGRST_JWT_ROLE_CLAIM_KEY
**In-Database** pgrst.jwt_role_claim_key
=============== =================================
*For backwards compatibility, this config parameter is also available without prefix as "role-claim-key".*
A JSPath DSL that specifies the location of the :code:`role` key in the JWT claims. This can be used to consume a JWT provided by a third party service like Auth0, Okta or Keycloak. Usage examples:
.. code:: bash
# {"postgrest":{"roles": ["other", "author"]}}
# the DSL accepts characters that are alphanumerical or one of "_$@" as keys
jwt-role-claim-key = ".postgrest.roles[1]"
# {"https://www.example.com/role": { "key": "author }}
# non-alphanumerical characters can go inside quotes(escaped in the config value)
jwt-role-claim-key = ".\"https://www.example.com/role\".key"
.. _jwt-secret:
jwt-secret
----------
=============== =================================
**Type** String
**Default** `n/a`
**Reloadable** Y
**Environment** PGRST_JWT_SECRET
**In-Database** pgrst.jwt_secret
=============== =================================
The secret or `JSON Web Key (JWK) (or set) <https://datatracker.ietf.org/doc/html/rfc7517>`_ used to decode JWT tokens clients provide for authentication. For security the key must be **at least 32 characters long**. If this parameter is not specified then PostgREST refuses authentication requests. Choosing a value for this parameter beginning with the at sign such as :code:`@filename` loads the secret out of an external file. This is useful for automating deployments. Note that any binary secrets must be base64 encoded. Both symmetric and asymmetric cryptography are supported. For more info see :ref:`asym_keys`.
Choosing a value for this parameter beginning with the at sign such as ``@filename`` (e.g. ``@./configs/my-config``) loads the secret out of an external file.
.. warning::
Only when using the :ref:`file_config`, if the ``jwt-secret`` contains a ``$`` character by itself it will give errors. In this case, use ``$$`` and PostgREST will interpret it as a single ``$`` character.
.. _jwt-secret-is-base64:
jwt-secret-is-base64
--------------------
=============== =================================
**Type** Boolean
**Default** False
**Reloadable** Y
**Environment** PGRST_JWT_SECRET_IS_BASE64
**In-Database** pgrst.jwt_secret_is_base64
=============== =================================
When this is set to :code:`true`, the value derived from :code:`jwt-secret` will be treated as a base64 encoded secret.
.. _jwt-cache-max-lifetime:
jwt-cache-max-lifetime
----------------------
=============== =================================
**Type** Int
**Default** 0
**Reloadable** Y
**Environment** PGRST_JWT_CACHE_MAX_LIFETIME
**In-Database** pgrst.jwt_cache_max_lifetime
=============== =================================
Maximum number of seconds of lifetime for cached entries. The default :code:`0` disables caching. See :ref:`jwt_caching`.
.. _log-level:
log-level
---------
=============== =================================
**Type** String
**Default** error
**Reloadable** N
**Environment** PGRST_LOG_LEVEL
**In-Database** `n/a`
=============== =================================
Specifies the level of information to be logged while running PostgREST.
.. code:: bash
# Only startup and db connection recovery messages are logged
log-level = "crit"
# All the "crit" level events plus server errors (status 5xx) are logged
log-level = "error"
# All the "error" level events plus request errors (status 4xx) are logged
log-level = "warn"
# All the "warn" level events plus all requests (every status code) are logged
log-level = "info"
Because currently there's no buffering for logging, the levels with minimal logging(``crit/error``) will increase throughput.
.. _openapi-mode:
openapi-mode
------------
=============== =================================
**Type** String
**Default** follow-privileges
**Reloadable** Y
**Environment** PGRST_OPENAPI_MODE
**In-Database** pgrst.openapi_mode
=============== =================================
Specifies how the OpenAPI output should be displayed.
.. code:: bash
# Follows the privileges of the JWT role claim (or from db-anon-role if the JWT is not sent)
# Shows information depending on the permissions that the role making the request has
openapi-mode = "follow-privileges"
# Ignores the privileges of the JWT role claim (or from db-anon-role if the JWT is not sent)
# Shows all the exposed information, regardless of the permissions that the role making the request has
openapi-mode = "ignore-privileges"
# Disables the OpenApi output altogether.
# Throws a `404 Not Found` error when accessing the API root path
openapi-mode = "disabled"
.. _openapi-security-active:
openapi-security-active
-----------------------
=============== =================================
**Type** Boolean
**Default** False
**Reloadable** Y
**Environment** PGRST_OPENAPI_SECURITY_ACTIVE
**In-Database** pgrst.openapi_security_active
=============== =================================
When this is set to :code:`true`, security options are included in the :ref:`OpenAPI output <open-api>`.
.. _openapi-server-proxy-uri:
openapi-server-proxy-uri
------------------------
=============== =================================
**Type** String
**Default** `n/a`
**Reloadable** N
**Environment** PGRST_OPENAPI_SERVER_PROXY_URI
**In-Database** pgrst.openapi_server_proxy_uri
=============== =================================
Overrides the base URL used within the OpenAPI self-documentation hosted at the API root path. Use a complete URI syntax :code:`scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]`. Ex. :code:`https://postgrest.com`
.. code:: json
{
"swagger": "2.0",
"info": {
"version": "0.4.3.0",
"title": "PostgREST API",
"description": "This is a dynamic API generated by PostgREST"
},
"host": "postgrest.com:443",
"basePath": "/",
"schemes": [
"https"
]
}
.. _server_cors_allowed_origins:
server-cors-allowed-origins
---------------------------
=============== ===================================
**Type** String
**Default** `n/a`
**Reloadable** N
**Environment** PGRST_SERVER_CORS_ALLOWED_ORIGINS
**In-Database** `pgrst.server_cors_allowed_origins`
=============== ===================================
Specifies allowed CORS origins in this config. See :ref:`cors`.
When this is not set or set to :code:`""`, PostgREST **accepts** CORS requests from any domain.
.. _server-host:
server-host
-----------
=============== =================================
**Type** String
**Default** !4
**Reloadable** N
**Environment** PGRST_SERVER_HOST
**In-Database** `n/a`
=============== =================================
Where to bind the PostgREST web server. In addition to the usual address options, PostgREST interprets these reserved addresses with special meanings:
* :code:`*` - any IPv4 or IPv6 hostname
* :code:`*4` - any IPv4 or IPv6 hostname, IPv4 preferred
* :code:`!4` - any IPv4 hostname
* :code:`*6` - any IPv4 or IPv6 hostname, IPv6 preferred
* :code:`!6` - any IPv6 hostname
.. _server-port:
server-port
-----------
=============== =================================
**Type** Int
**Default** 3000
**Reloadable** N
**Environment** PGRST_SERVER_PORT
**In-Database** `n/a`
=============== =================================
The TCP port to bind the web server. Use ``0`` to automatically assign a port.
.. _server-trace-header:
server-trace-header
-------------------
=============== =================================
**Type** String
**Default** `n/a`
**Reloadable** Y
**Environment** PGRST_SERVER_TRACE_HEADER
**In-Database** pgrst.server_trace_header
=============== =================================
The header name used to trace HTTP requests. See :ref:`trace_header`.
.. _server-timing-enabled:
server-timing-enabled
---------------------
=============== =================================
**Type** Boolean
**Default** False
**Reloadable** Y
**Environment** PGRST_SERVER_TIMING_ENABLED
**In-Database** pgrst.server_timing_enabled
=============== =================================
Enables the `Server-Timing <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing>`_ header.
See :ref:`server-timing_header`.
.. _server-unix-socket:
server-unix-socket
------------------
=============== =================================
**Type** String
**Default** `n/a`
**Reloadable** N
**Environment** PGRST_SERVER_UNIX_SOCKET
**In-Database** `n/a`
=============== =================================
`Unix domain socket <https://en.wikipedia.org/wiki/Unix_domain_socket>`_ where to bind the PostgREST web server.
If specified, this takes precedence over :ref:`server-port`. Example:
.. code:: bash
server-unix-socket = "/tmp/pgrst.sock"
.. _server-unix-socket-mode:
server-unix-socket-mode
-----------------------
=============== =================================
**Type** String
**Default** 660
**Reloadable** N
**Environment** PGRST_SERVER_UNIX_SOCKET_MODE
**In-Database** `n/a`
=============== =================================
`Unix file mode <https://en.wikipedia.org/wiki/File_system_permissions>`_ to be set for the socket specified in :ref:`server-unix-socket`
Needs to be a valid octal between 600 and 777.
.. code:: bash
server-unix-socket-mode = "660"
+94
View File
@@ -0,0 +1,94 @@
.. _connection_pool:
Connection Pool
===============
A connection pool is a cache of reusable database connections. It allows serving many HTTP requests using few database connections. Every request to an :doc:`API resource <api>` borrows a connection from the pool to start a :doc:`transaction <transactions>`.
Minimizing connections is paramount to performance. Each PostgreSQL connection creates a process, having too many can exhaust available resources.
Connection String
-----------------
For connecting to the database, the pool requires a connection string. You can configure it using :ref:`db-uri`.
.. _pool_growth_limit:
.. _dyn_conn_pool:
Dynamic Connection Pool
-----------------------
To conserve system resources, PostgREST uses a dynamic connection pool. This enables the number of connections in the pool to increase and decrease depending on request traffic.
- If all the connections are being used, a new connection is added. The pool can grow until it reaches the :ref:`db-pool` size. Note that its pointless to set this higher than the ``max_connections`` setting in your database.
- If a connection is unused for a period of time (:ref:`db-pool-max-idletime`), it will be released.
Connection lifetime
-------------------
Long-lived PostgreSQL connections can consume considerable memory (see `here <https://www.postgresql.org/message-id/CAFj8pRCQN2B2vrVMH1-bd-8xtzjytWR%2BAjZ%2BMCj9J2wPxKPa9Q%40mail.gmail.com>`_ for more details).
Under a busy system, the :ref:`db-pool-max-idletime` won't be reached and the connection pool can be full of long-lived connections.
To avoid this problem and save resources, a connection max lifetime (:ref:`db-pool-max-lifetime`) is enforced.
After the max lifetime is reached, connections from the pool will be released and new ones will be created. This doesn't affect running requests, only unused connections will be released.
Acquisition Timeout
-------------------
If all the available connections in the pool are busy, an HTTP request will wait until reaching a timeout (:ref:`db-pool-acquisition-timeout`).
If the request reaches the timeout, it will be aborted with the following response:
.. code-block:: http
HTTP/1.1 504 Gateway Timeout
{"code":"PGRST003",
"details":null,
"hint":null,
"message":"Timed out acquiring connection from connection pool."}
.. important::
Getting this error message is an indicator of a performance issue. To solve it, you can:
- Reduce your queries execution time.
- Check the request :ref:`explain_plan` to tune your query, this usually means adding indexes.
- Reduce the amount of requests.
- Reduce write requests. Do :ref:`bulk_insert` (or :ref:`upsert`) instead of inserting rows one by one.
- Reduce read requests. Use :ref:`resource_embedding`. Combine unrelated data into a single request using custom database views or functions.
- Use :ref:`s_procs` for combining read and write logic into a single request.
- Increase the :ref:`db-pool` size.
- Not a panacea since connections can't grow infinitely. Try the previous recommendations before this.
.. _automatic_recovery:
Automatic Recovery
------------------
The server will retry reconnecting to the database if connection loss happens.
- It will retry forever with exponential backoff, with a maximum backoff time of 32 seconds between retries. Each of these attempts are :ref:`logged <pgrst_logging>`.
- It will only stop retrying if the server deems the error to be fatal. This can be a password authentication failure or an internal error.
- The retries happen immediately after a connection loss, if :ref:`db-channel-enabled` is set to true (the default). Otherwise they'll happen once a request arrives.
- To ensure a valid state, the server reloads the :ref:`schema_cache` and :ref:`configuration` when recovering.
- To notify the client of the next retry, the server sends a ``503 Service Unavailable`` status with the ``Retry-After: x`` header. Where ``x`` is the number of seconds programmed for the next retry.
- Automatic recovery can be disabled by setting :ref:`db-pool-automatic-recovery` to ``false``.
.. _external_connection_poolers:
Using External Connection Poolers
---------------------------------
It's possible to use external connection poolers, such as PgBouncer. Session pooling is compatible, while transaction pooling requires :ref:`db-prepared-statements` set to ``false``. Statement pooling is not compatible with PostgREST.
Also set :ref:`db-channel-enabled` to ``false`` since ``LISTEN`` is not compatible with transaction pooling. Although it should not give any errors if left enabled.
.. note::
Its not recommended to use an external connection pooler. `Our benchmarks <https://github.com/PostgREST/postgrest/issues/2294#issuecomment-1139148672>`_ indicate it provides much lower performance than PostgREST built-in pool.
+176 -45
View File
@@ -1,52 +1,38 @@
.. _error_source:
Error Source
============
Errors
######
For the most part, error messages will come directly from the database with the same `structure that PostgreSQL uses <https://www.postgresql.org/docs/current/error-style-guide.html>`_. PostgREST will convert the ``MESSAGE``, ``DETAIL``, ``HINT`` and ``ERRCODE`` from the PostgreSQL error to JSON format and add an HTTP status code to the response (see :ref:`status_codes`). For instance, this is the error you will get when querying a nonexistent table:
PostgREST error messages follow the PostgreSQL error structure. It includes ``MESSAGE``, ``DETAIL``, ``HINT``, ``ERRCODE`` and will add an HTTP status code to the response.
Errors from PostgreSQL
======================
PostgREST will forward errors coming from PostgreSQL. For instance, on a failed constraint:
.. code-block:: http
GET /nonexistent_table?id=eq.1 HTTP/1.1
POST /projects HTTP/1.1
.. code-block:: http
HTTP/1.1 404 Not Found
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
.. code-block:: json
{
"hint": null,
"details": null,
"code": "42P01",
"message": "relation \"api.nonexistent_table\" does not exist"
}
However, some errors do come from PostgREST itself (such as those related to the :ref:`schema_cache`). These have the same structure as the PostgreSQL errors but are differentiated by the ``PGRST`` prefix in the ``code`` field (see :ref:`pgrst_errors`). For instance, when querying a function that does not exist, the error will be:
.. code-block:: http
POST /rpc/nonexistent_function HTTP/1.1
.. code-block:: http
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
.. code-block:: json
{
"hint": "If a new function was created in the database with this name and parameters, try reloading the schema cache.",
"details": null
"code": "PGRST202",
"message": "Could not find the api.nonexistent_function() function in the schema cache"
"code": "23502",
"details": "Failing row contains (null, foo, null).",
"hint": null,
"message": "null value in column \"id\" of relation \"projects\" violates not-null constraint"
}
.. _status_codes:
HTTP Status Codes
=================
-----------------
PostgREST translates `PostgreSQL error codes <https://www.postgresql.org/docs/current/errcodes-appendix.html>`_ into HTTP status as follows:
@@ -105,23 +91,53 @@ PostgREST translates `PostgreSQL error codes <https://www.postgresql.org/docs/cu
+--------------------------+-------------------------+---------------------------------+
| 42P01 | 404 | undefined table |
+--------------------------+-------------------------+---------------------------------+
| 42P17 | 500 | infinite recursion |
+--------------------------+-------------------------+---------------------------------+
| 42501 | | if authenticated 403, | insufficient privileges |
| | | else 401 | |
+--------------------------+-------------------------+---------------------------------+
| other | 400 | |
+--------------------------+-------------------------+---------------------------------+
Errors from PostgREST
=====================
Errors that come from PostgREST itself maintain the same structure but differ in the ``PGRST`` prefix in the ``code`` field. For instance, when querying a function that does not exist in the :doc:`schema cache <schema_cache>`:
.. code-block:: http
POST /rpc/nonexistent_function HTTP/1.1
.. code-block:: http
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
.. code-block:: json
{
"hint": "...",
"details": null
"code": "PGRST202",
"message": "Could not find the api.nonexistent_function() function in the schema cache"
}
.. _pgrst_errors:
PostgREST Error Codes
=====================
---------------------
PostgREST error codes have the form ``PGRSTgxx``, where ``PGRST`` is the prefix that differentiates the error from a PostgreSQL error, ``g`` is the group where the error belongs and ``xx`` is the number that identifies the error in the group.
PostgREST error codes have the form ``PGRSTgxx``.
- ``PGRST`` is the prefix that differentiates the error from a PostgreSQL error.
- ``g`` is the error group
- ``xx`` is the error identifier in the group.
.. _pgrst0**:
Group 0 - Connection
--------------------
~~~~~~~~~~~~~~~~~~~~
Related to the connection with the database.
@@ -137,8 +153,8 @@ Related to the connection with the database.
| PGRST001 | | |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst002: | 503 | Could not connect with the database when building the |
| | | :ref:`schema_cache` due to the PostgreSQL service not |
| PGRST002 | | running. |
| | | :doc:`Schema Cache <schema_cache>` |
| PGRST002 | | due to the PostgreSQL service not running. |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst003: | 504 | The request timed out waiting for a pool connection |
| | | to be available. See :ref:`db-pool-acquisition-timeout`. |
@@ -148,7 +164,7 @@ Related to the connection with the database.
.. _pgrst1**:
Group 1 - Api Request
---------------------
~~~~~~~~~~~~~~~~~~~~~
Related to the HTTP request elements.
@@ -203,10 +219,6 @@ Related to the HTTP request elements.
| | | See :ref:`guc_resp_status`. |
| PGRST112 | | |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst113: | 406 | More than one column was returned for a scalar result. |
| | | See :ref:`scalar_return_formats`. |
| PGRST113 | | |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst114: | 400 | For an :ref:`UPSERT using PUT <upsert_put>`, when |
| | | :ref:`limits and offsets <limits>` are used. |
| PGRST114 | | |
@@ -223,26 +235,46 @@ Related to the HTTP request elements.
| | | |
| PGRST117 | | |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst118: | 400 | Could not order the result using the related table because |
| | | there is no many-to-one or one-to-one relationship between |
| PGRST118 | | them. |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst119: | 400 | Could not use the spread operator on the related table |
| | | because there is no many-to-one or one-to-one relationship |
| PGRST119 | | between them. |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst120: | 400 | An embedded resource can only be filtered using the |
| | | ``is.null`` or ``not.is.null`` :ref:`operators <operators>`.|
| PGRST120 | | |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst121: | 400 | PostgREST can't parse the JSON objects in RAISE |
| | | ``PGRST`` error. See :ref:`raise headers <raise_headers>`. |
| PGRST121 | | |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst122: | 400 | Invalid preferences found in ``Prefer`` header with |
| | | ``Prefer: handling=strict``. See :ref:`prefer_handling`. |
| PGRST122 | | |
+---------------+-------------+-------------------------------------------------------------+
.. _pgrst2**:
Group 2 - Schema Cache
----------------------
~~~~~~~~~~~~~~~~~~~~~~
Related to a :ref:`stale schema cache <stale_schema>`. Most of the time, these errors are solved by :ref:`reloading the schema cache <schema_reloading>`.
+---------------+-------------+-------------------------------------------------------------+
| Code | HTTP status | Description |
+===============+=============+=============================================================+
| .. _pgrst200: | 400 | Caused by :ref:`stale_fk_relationships`, otherwise any of |
| .. _pgrst200: | 400 | Caused by stale foreign key relationships, otherwise any of |
| | | the embedding resources or the relationship itself may not |
| PGRST200 | | exist in the database. |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst201: | 300 | An ambiguous embedding request was made. |
| | | See :ref:`embed_disamb`. |
| | | See :ref:`complex_rels`. |
| PGRST201 | | |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst202: | 404 | Caused by a :ref:`stale_function_signature`, otherwise |
| .. _pgrst202: | 404 | Caused by a stale function signature, otherwise |
| | | the function may not exist in the database. |
| PGRST202 | | |
+---------------+-------------+-------------------------------------------------------------+
@@ -260,7 +292,7 @@ Related to a :ref:`stale schema cache <stale_schema>`. Most of the time, these e
.. _pgrst3**:
Group 3 - JWT
-------------
~~~~~~~~~~~~~
Related to the authentication process using JWT. You can follow the :ref:`tut1` for an example on how to implement authentication and the :doc:`Authentication page <auth>` for more information on this process.
@@ -285,7 +317,7 @@ Related to the authentication process using JWT. You can follow the :ref:`tut1`
.. _pgrst_X**:
Group X - Internal
------------------
~~~~~~~~~~~~~~~~~~
Internal errors. If you encounter any of these, you may have stumbled on a PostgREST bug, please `open an issue <https://github.com/PostgREST/postgrest/issues>`_ and we'll be glad to fix it.
@@ -296,3 +328,102 @@ Internal errors. If you encounter any of these, you may have stumbled on a Postg
| | | to the database. |
| PGRSTX00 | | |
+---------------+-------------+-------------------------------------------------------------+
Custom Errors
=============
You can customize the errors by using the `RAISE statement <https://www.postgresql.org/docs/current/plpgsql-errors-and-messages.html#PLPGSQL-STATEMENTS-RAISE>`_ on functions.
.. _raise_error:
RAISE errors with HTTP Status Codes
-----------------------------------
Custom status codes can be done by raising SQL exceptions inside :ref:`functions <s_procs>`. For instance, here's a saucy function that always responds with an error:
.. code-block:: postgresql
CREATE OR REPLACE FUNCTION just_fail() RETURNS void
LANGUAGE plpgsql
AS $$
BEGIN
RAISE EXCEPTION 'I refuse!'
USING DETAIL = 'Pretty simple',
HINT = 'There is nothing you can do.';
END
$$;
Calling the function returns HTTP 400 with the body
.. code-block:: json
{
"message":"I refuse!",
"details":"Pretty simple",
"hint":"There is nothing you can do.",
"code":"P0001"
}
One way to customize the HTTP status code is by raising particular exceptions according to the PostgREST :ref:`error to status code mapping <status_codes>`. For example, :code:`RAISE insufficient_privilege` will respond with HTTP 401/403 as appropriate.
For even greater control of the HTTP status code, raise an exception of the ``PTxyz`` type. For instance to respond with HTTP 402, raise ``PT402``:
.. code-block:: sql
RAISE sqlstate 'PT402' using
message = 'Payment Required',
detail = 'Quota exceeded',
hint = 'Upgrade your plan';
Returns:
.. code-block:: http
HTTP/1.1 402 Payment Required
Content-Type: application/json; charset=utf-8
{
"message": "Payment Required",
"details": "Quota exceeded",
"hint": "Upgrade your plan",
"code": "PT402"
}
.. _raise_headers:
Add HTTP Headers with RAISE
---------------------------
For full control over headers and status you can raise a ``PGRST`` SQLSTATE error. You can achieve this by adding the ``code``, ``message``, ``detail`` and ``hint`` in the postgresql error message field as a JSON object. Here, the ``details`` and ``hint`` are optional. Similarly, the ``status`` and ``headers`` must be added to the SQL error detail field as a JSON object. For instance:
.. code-block:: sql
RAISE sqlstate 'PGRST' USING
message = '{"code":"123","message":"Payment Required","details":"Quota exceeded","hint":"Upgrade your plan"}',
detail = '{"status":402,"headers":{"X-Powered-By":"Nerd Rage"}}';
Returns:
.. code-block:: http
HTTP/1.1 402 Payment Required
Content-Type: application/json; charset=utf-8
X-Powered-By: Nerd Rage
{
"message": "Payment Required",
"details": "Quota exceeded",
"hint": "Upgrade your plan",
"code": "123"
}
For non standard HTTP status, you can optionally add ``status_text`` to describe the status code. For status code ``419`` the detail field may look like this:
.. code-block:: sql
detail = '{"status":419,"status_text":"Page Expired","headers":{"X-Powered-By":"Nerd Rage"}}';
If PostgREST can't parse the JSON objects ``message`` and ``detail``, it will throw a ``PGRST121`` error. See :ref:`Errors from PostgREST<pgrst1**>`.
@@ -3,8 +3,7 @@
Schema Cache
============
Certain PostgREST features require metadata from the database schema. Getting this metadata requires executing expensive queries, so
in order to avoid repeating this work, PostgREST uses a schema cache.
Some PostgREST features need metadata from the database schema. Getting this metadata requires expensive queries. To avoid repeating this work, PostgREST uses a schema cache.
+--------------------------------------------+-------------------------------------------------------------------------------+
| Feature | Required Metadata |
@@ -29,84 +28,19 @@ in order to avoid repeating this work, PostgREST uses a schema cache.
.. _stale_schema:
The Stale Schema Cache
----------------------
Stale Schema Cache
------------------
When you make changes on the metadata mentioned above, the schema cache will turn stale on a running PostgREST. Future requests that use the above features will need the :ref:`schema cache to be reloaded <schema_reloading>`; otherwise, you'll get an error instead of the expected result.
One operational problem that comes with a cache is that it can go stale. This can happen for PostgREST when you make changes to the metadata before mentioned. Requests that depend on the metadata will fail.
For instance, let's see what would happen if you have a stale schema cache for foreign key relationships and function signatures.
.. _stale_fk_relationships:
Stale Foreign Key Relationships
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Suppose you add a ``cities`` table to your database and define a foreign key that references an existing ``countries`` table. Then, you make a request to get the ``cities`` and their belonging ``countries``.
.. tabs::
.. code-tab:: http
GET /cities?select=name,country:countries(id,name) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/cities?select=name,country:countries(id,name)"
The result will be an error:
.. code-block:: json
{
"hint": "Verify that 'cities' and 'countries' exist in the schema 'api' and that there is a foreign key relationship between them. If a new relationship was created, try reloading the schema cache.",
"details": null,
"code": "PGRST200",
"message": "Could not find a relationship between 'cities' and 'countries' in the schema cache"
}
As you can see, PostgREST couldn't find the newly created foreign key in the schema cache. See :ref:`schema_reloading` and :ref:`auto_schema_reloading` to solve this issue.
.. _stale_function_signature:
Stale Function Signature
~~~~~~~~~~~~~~~~~~~~~~~~
The same issue will occur on newly created functions on a running PostgREST.
.. code-block:: plpgsql
CREATE FUNCTION plus_one(num integer)
RETURNS integer AS $$
SELECT num + 1;
$$ LANGUAGE SQL IMMUTABLE;
.. tabs::
.. code-tab:: http
GET /rpc/plus_one?num=1 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/plus_one?num=1"
.. code-block:: json
{
"hint": "If a new function was created in the database with this name and parameters, try reloading the schema cache.",
"details": null,
"code": "PGRST202",
"message": "Could not find the api.plus_one(num) function in the schema cache"
}
Here, PostgREST tries to find the function on the stale schema to no avail. See :ref:`schema_reloading` and :ref:`auto_schema_reloading` to solve this issue.
You can solve this by reloading the cache manually or automatically.
.. _schema_reloading:
Schema Cache Reloading
----------------------
To reload the cache without restarting the PostgREST server, send a SIGUSR1 signal to the server process.
To manually reload the cache without restarting the PostgREST server, send a SIGUSR1 signal to the server process.
.. code:: bash
@@ -122,27 +56,29 @@ For docker you can do:
# or in docker-compose
docker-compose kill -s SIGUSR1 <service>
There's no downtime when reloading the schema cache. The reloading will happen on a background thread while requests keep being served.
Theres no downtime when reloading the schema cache. The reloading will happen on a background thread while serving requests.
.. _schema_reloading_notify:
Reloading with NOTIFY
~~~~~~~~~~~~~~~~~~~~~
There are environments where you can't send the SIGUSR1 Unix Signal (like on managed containers in cloud services or on Windows systems). For this reason, PostgREST also allows you to reload its schema cache through PostgreSQL `NOTIFY <https://www.postgresql.org/docs/current/sql-notify.html>`_ as follows:
PostgREST also allows you to reload its schema cache through PostgreSQL `NOTIFY <https://www.postgresql.org/docs/current/sql-notify.html>`_.
.. code-block:: postgresql
NOTIFY pgrst, 'reload schema'
The ``"pgrst"`` notification channel is enabled by default. For configuring the channel, see :ref:`db-channel` and :ref:`db-channel-enabled`.
This is useful in environments where you cant send the SIGUSR1 Unix Signal. Like on cloud managed containers or on Windows systems.
The ``pgrst`` notification channel is enabled by default. For configuring the channel, see :ref:`db-channel` and :ref:`db-channel-enabled`.
.. _auto_schema_reloading:
Automatic Schema Cache Reloading
--------------------------------
You can do automatic schema cache reloading in a pure SQL way and forget about stale schema cache errors with an `event trigger <https://www.postgresql.org/docs/current/event-trigger-definition.html>`_ and ``NOTIFY``.
You can do automatic schema cache reloading in a pure SQL way and forget about stale schema cache errors. For this use an `event trigger <https://www.postgresql.org/docs/current/event-trigger-definition.html>`_ and ``NOTIFY``.
.. code-block:: postgresql
@@ -160,9 +96,9 @@ You can do automatic schema cache reloading in a pure SQL way and forget about s
ON ddl_command_end
EXECUTE PROCEDURE pgrst_watch();
Now, whenever the ``pgrst_watch`` trigger is fired in the database, PostgREST will automatically reload the schema cache.
Now, whenever the ``pgrst_watch`` trigger fires, PostgREST will auto-reload the schema cache.
To disable auto reloading, drop the trigger:
To disable auto reloading, drop the trigger.
.. code-block:: postgresql
@@ -171,12 +107,12 @@ To disable auto reloading, drop the trigger:
Finer-Grained Event Trigger
~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can refine the previous event trigger and only react to the events relevant to the schema cache. This also prevents unnecessary
reloading when creating temporary tables(``CREATE TEMP TABLE``) inside functions.
You can refine the previous event trigger to only react to the events relevant to the schema cache. This also prevents unnecessary
reloading when creating temporary tables inside functions.
.. code-block:: postgresql
-- watch create and alter
-- watch CREATE and ALTER
CREATE OR REPLACE FUNCTION pgrst_ddl_watch() RETURNS event_trigger AS $$
DECLARE
cmd record;
@@ -203,7 +139,7 @@ reloading when creating temporary tables(``CREATE TEMP TABLE``) inside functions
END LOOP;
END; $$ LANGUAGE plpgsql;
-- watch drop
-- watch DROP
CREATE OR REPLACE FUNCTION pgrst_drop_watch() RETURNS event_trigger AS $$
DECLARE
obj record;
+381
View File
@@ -0,0 +1,381 @@
.. _transactions:
Transactions
============
After :ref:`user_impersonation`, every request to an :doc:`API resource <api>` runs inside a transaction. The sequence of the transaction is as follows:
.. code-block:: postgresql
START TRANSACTION; -- <Access Mode> <Isolation Level>
-- <Transaction-scoped settings>
-- <Main Query>
END; -- <Transaction End>
.. _access_mode:
Access Mode
-----------
The access mode determines whether the transaction can modify the database or not. There are 2 possible values: READ ONLY and READ WRITE.
Modifying the database inside READ ONLY transactions is not possible. PostgREST uses this fact to enforce HTTP semantics in GET and HEAD requests. Consider the following:
.. code-block:: postgresql
CREATE SEQUENCE callcounter_count START 1;
CREATE VIEW callcounter AS
SELECT nextval('callcounter_count');
Since the ``callcounter`` view modifies the sequence, calling it with GET or HEAD will result in an error:
.. tabs::
.. code-tab:: http
GET /callcounter HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/callcounter"
.. code-block:: http
HTTP/1.1 405 Method Not Allowed
{"code":"25006","details":null,"hint":null,"message":"cannot execute nextval() in a read-only transaction"}
Access Mode on Tables and Views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The access mode on :ref:`tables_views` is determined by the HTTP method.
.. list-table::
:header-rows: 1
* - HTTP Method
- Access Mode
* - GET, HEAD
- READ ONLY
* - POST, PATCH, PUT, DELETE
- READ WRITE
Access Mode on Functions
~~~~~~~~~~~~~~~~~~~~~~~~
:ref:`s_procs` additionally depend on the function `volatility <https://www.postgresql.org/docs/current/xfunc-volatility.html>`_.
.. list-table::
:header-rows: 2
* -
- Access Mode
-
-
* - HTTP Method
- VOLATILE
- STABLE
- IMMUTABLE
* - GET, HEAD
- READ ONLY
- READ ONLY
- READ ONLY
* - POST
- READ WRITE
- READ ONLY
- READ ONLY
.. note::
- The volatility marker is a promise about the behavior of the function. PostgreSQL will let you mark a function that modifies the database as ``IMMUTABLE`` or ``STABLE`` without failure. But, because of the READ ONLY transaction the function will fail under PostgREST.
- The :ref:`options_requests` method doesn't start a transaction, so it's not relevant here.
.. _isolation_lvl:
Isolation Level
---------------
Every transaction uses the PostgreSQL default isolation level: READ COMMITTED. Unless you modify `default_transaction_isolation <https://www.postgresql.org/docs/15/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-ISOLATION>`_ for an impersonated role or function.
.. code-block:: postgresql
ALTER ROLE webuser SET default_transaction_isolation TO 'repeatable read';
Every ``webuser`` gets its queries executed with ``default_transaction_isolation`` set to REPEATABLE READ.
Or to change the isolation level per function call.
.. code-block:: postgresql
CREATE OR REPLACE FUNCTION myfunc()
RETURNS text as $$
SELECT 'hello';
$$
LANGUAGE SQL
SET default_transaction_isolation TO 'serializable';
.. _tx_settings:
Transaction-Scoped Settings
---------------------------
PostgREST uses settings tied to the transaction lifetime. These can be used to get data about the HTTP request. Or to modify the HTTP response.
You can get these with ``current_setting``
.. code-block:: postgresql
-- request settings use the ``request.`` prefix.
SELECT
current_setting('request.<setting>', true);
And you can set them with ``set_config``
.. code-block:: postgresql
-- response settings use the ``response.`` prefix.
SELECT
set_config('response.<setting>', 'value1' ,true);
.. _guc_req_headers_cookies_claims:
Request Headers, Cookies and JWT claims
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PostgREST stores the headers, cookies and headers as JSON. To get them:
.. code-block:: postgresql
-- To get all the headers sent in the request
SELECT current_setting('request.headers', true)::json;
-- To get a single header, you can use JSON arrow operators
SELECT current_setting('request.headers', true)::json->>'user-agent';
-- value of sessionId in a cookie
SELECT current_setting('request.cookies', true)::json->>'sessionId';
-- value of the email claim in a jwt
SELECT current_setting('request.jwt.claims', true)::json->>'email';
.. important::
- The headers names are lowercased. e.g. If the request sends ``User-Agent: x`` this will be obtainable as ``current_setting('request.headers', true)::json->>'user-agent'``.
- The ``role`` in ``request.jwt.claims`` defaults to the value of :ref:`db-anon-role`.
- Settings don't become NULL after the transaction is committed, instead they're set to a an empty string ``''``.
+ This is considered expected behavior by PostgreSQL. For more details, see `this discussion <https://www.postgresql.org/message-id/flat/CAB_pDVVa84w7hXhzvyuMTb8f5kKV3bee_p9QTZZ58Rg7zYM7sw%40mail.gmail.com>`_.
+ To avoid this inconsistency, you can create a wrapper function like:
.. code-block:: postgresql
CREATE FUNCTION my_current_setting(text) RETURNS text
LANGUAGE SQL AS $$
SELECT nullif(current_setting($1, true), '');
$$;
.. _guc_req_path_method:
Request Path and Method
~~~~~~~~~~~~~~~~~~~~~~~
The path and method are stored as ``text``.
.. code-block:: postgresql
SELECT current_setting('request.path', true);
SELECT current_setting('request.method', true);
Request Role and Search Path
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Because of :ref:`user_impersonation`, PostgREST sets the standard ``role``. You can get this in different ways:
.. code-block:: postgresql
SELECT current_role;
SELECT current_user;
SELECT current_setting('role', true);
Additionally it also sets the ``search_path`` based on :ref:`db-schemas` and :ref:`db-extra-search-path`.
.. _guc_resp_hdrs:
Response Headers
~~~~~~~~~~~~~~~~
You can set ``response.headers`` to add headers to the HTTP response. For instance, this statement would add caching headers to the response:
.. code-block:: sql
-- tell client to cache response for two days
SELECT set_config('response.headers',
'[{"Cache-Control": "public"}, {"Cache-Control": "max-age=259200"}]', true);
.. code-block:: http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: no-cache, no-store, must-revalidate
Notice that the ``response.headers`` should be set to an *array* of single-key objects rather than a single multiple-key object. This is because headers such as ``Cache-Control`` or ``Set-Cookie`` need repeating when setting many values. An object would not allow the repeated key.
.. note::
PostgREST provided headers such as ``Content-Type``, ``Location``, etc. can be overriden this way. Note that irrespective of overridden ``Content-Type`` response header, the content will still be converted to JSON, unless you use :ref:`custom_media`.
.. _guc_resp_status:
Response Status Code
~~~~~~~~~~~~~~~~~~~~
You can set the ``response.status`` to override the default status code PostgREST provides. For instance, the following function would replace the default ``200`` status code.
.. code-block:: postgres
create or replace function teapot() returns json as $$
begin
perform set_config('response.status', '418', true);
return json_build_object('message', 'The requested entity body is short and stout.',
'hint', 'Tip it over and pour it out.');
end;
$$ language plpgsql;
.. tabs::
.. code-tab:: http
GET /rpc/teapot HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/teapot" -i
.. code-block:: http
HTTP/1.1 418 I'm a teapot
{
"message" : "The requested entity body is short and stout.",
"hint" : "Tip it over and pour it out."
}
If the status code is standard, PostgREST will complete the status message(**I'm a teapot** in this example).
.. _impersonated_settings:
Impersonated Role Settings
~~~~~~~~~~~~~~~~~~~~~~~~~~
PostgreSQL applies the connection role (:ref:`authenticator <roles>`) settings. Additionally, PostgREST applies the :ref:`impersonated roles <user_impersonation>` settings as transaction-scoped settings.
This allows finer-grained control over actions made by a role.
For example, consider `statement_timeout <https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-STATEMENT-TIMEOUT>`__. It allows you to abort any statement that takes more than a specified time. It is disabled by default.
.. code-block:: postgresql
ALTER ROLE authenticator SET statement_timeout TO '10s';
ALTER ROLE anonymous SET statement_timeout TO '1s';
With the above settings, all users get a global statement timeout of 10 seconds and :ref:`anonymous <roles>` users get a timeout of 1 second.
Settings with privileged context
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Settings that have a context which requires privileges won't be applied by default. This is so we don't cause permission errors.
For more details see `Understanding Postgres Parameter Context <https://www.enterprisedb.com/blog/understanding-postgres-parameter-context>`_.
However, starting from PostgreSQL 15, you can grant privileges for these settings with:
.. code-block:: postgresql
GRANT SET ON PARAMETER <setting> TO <authenticator>;
Function Settings
~~~~~~~~~~~~~~~~~
In addition to :ref:`impersonated_settings`, PostgREST will also apply function settings as transaction-scoped settings. This allows functions settings to override
the impersonated and connection role settings.
.. code-block:: postgresql
CREATE OR REPLACE FUNCTION myfunc()
RETURNS void as $$
SELECT pg_sleep(3); -- simulating some long-running process
$$
LANGUAGE SQL
SET statement_timeout TO '4s';
When calling the above function (see :ref:`s_procs`), the statement timeout will be 4 seconds.
.. note::
Currently, only ``statement_timeout`` is applied for functions.
.. _main_query:
Main query
----------
The main query is generated by requesting :ref:`tables_views` or :ref:`s_procs`. All generated queries use prepared statements (:ref:`db-prepared-statements`).
Transaction End
---------------
If the transaction doesn't fail, it will always end in a COMMIT. Unless :ref:`db-tx-end` is configured to ROLLBACK in any case or conditionally with ``Prefer: tx=rollback``. This can be used for testing purposes.
Aborting transactions
---------------------
Any database failure(like a failed constraint) will result in a rollback of the transaction. You can also :ref:`RAISE an error inside a function <raise_error>` to cause a rollback.
.. _pre-request:
Pre-Request
-----------
The pre-request is a function that can run after the :ref:`tx_settings` are set and before the :ref:`main_query`. It's enabled with :ref:`db-pre-request`.
This provides an opportunity to modify settings or raise an exception to prevent the request from completing.
.. _pre_req_headers:
Setting headers via pre-request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As an example, let's add some cache headers for all requests that come from an Internet Explorer(6 or 7) browser.
.. code-block:: postgresql
create or replace function custom_headers()
returns void as $$
declare
user_agent text := current_setting('request.headers', true)::json->>'user-agent';
begin
if user_agent similar to '%MSIE (6.0|7.0)%' then
perform set_config('response.headers',
'[{"Cache-Control": "no-cache, no-store, must-revalidate"}]', false);
end if;
end; $$ language plpgsql;
-- set this function on postgrest.conf
-- db-pre-request = custom_headers
Now when you make a GET request to a table or view, you'll get the cache headers.
.. tabs::
.. code-tab:: http
GET /people HTTP/1.1
User-Agent: Mozilla/4.01 (compatible; MSIE 6.0; Windows NT 5.1)
.. code-tab:: bash Curl
curl "http://localhost:3000/people" -i \
-H "User-Agent: Mozilla/4.01 (compatible; MSIE 6.0; Windows NT 5.1)"
-227
View File
@@ -1,227 +0,0 @@
PostgREST 10.0.0
================
Features
--------
XML/SOAP support for RPC
~~~~~~~~~~~~~~~~~~~~~~~~
RPC now understands the ``text/xml`` media type, allowing SQL functions to send XML output(``Accept: text/xml``) and receive XML input(``Content-Type: text/xml``). This makes SOAP endpoints possible, check the :ref:`create_soap_endpoint` how-to and the :ref:`scalar_return_formats` reference for more details.
GeoJSON support
~~~~~~~~~~~~~~~
GeoJSON is supported across the board(reads, writes, RPC) with the ``Accept: application/geo+json`` header, this depends on PostGIS from the versions 3.0.0 and up. The :ref:`working with PostGIS section <ww_postgis>` has an example to get you started.
Execution Plan
~~~~~~~~~~~~~~
The :ref:`execution plan <explain_plan>` of a request is now obtainable with the ``Accept: application/vnd.pgrst.plan`` header. The result can be in ``text`` or ``json`` formats and is compatible with EXPLAIN vizualizers like `explain.depesz.com <https://explain.depesz.com>`_ or `explain.dalibo.com <https://explain.dalibo.com>`_.
Resource Embedding
~~~~~~~~~~~~~~~~~~
- A :ref:`one-to-one relationship <one-to-one>` is now detected when a foreign key is unique.
- Using :ref:`computed_relationships`, you can add custom relationships or override automatically detected ones. This makes :ref:`resource_embedding` possible on Foreign Data Wrappers and complex SQL views.
Horizontal/Vertical Filtering
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- :ref:`Accessing fields of a Composite type or elements of an Array type <composite_array_columns>` is now possible with the arrow operators(``->``, ``->>``) in the same way you would access a JSON type fields.
- :ref:`pattern_matching` operators for `POSIX regular expressions <https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-POSIX-REGEXP>`_ are now available: ``match`` and ``imatch``, equivalent in PostgreSQL to ``~`` and ``~*`` respectively.
Insertions/Updates
~~~~~~~~~~~~~~~~~~
- ``limit`` can now affect the number of updated/deleted rows. See :ref:`limited_update_delete`.
OpenAPI
~~~~~~~
You can now activate the "Authorize" button in SwaggerUI by enabling the :ref:`openapi-security-active` configuration. Add your JWT token prepending :code:`Bearer` to it and you'll be able to request protected resources.
Administration
~~~~~~~~~~~~~~
- Two :ref:`health check endpoints <health_check>` are now exposed in a secondary port.
- :ref:`pgrst_logging` now shows the database user.
- It is now possible to execute PostgREST without specifying any configuration variable. The three that were mandatory on the previous versions, are no longer so.
- If :ref:`db-uri` is not set, PostgREST will use the `libpq environment variables <https://www.postgresql.org/docs/current/libpq-envars.html>`_ for the database connection.
- If :ref:`db-schemas` is not set, it will use the database ``public`` schema.
- If :ref:`db-anon-role` is not set, it will not allow anonymous requests.
Error messages
~~~~~~~~~~~~~~
- To increase consistency, all the errors messages are now normalized. The ``hint``, ``details``, ``code`` and ``message`` fields will always be present in the body, each one defaulting to a ``null`` value. In the same way, the :ref:`errors that were raised <raise_error>` with ``SQLSTATE`` now include the ``message`` and ``code`` in the body.
- To further clarify the source of an error, we now add a ``PGRST`` prefix to the error code of all the errors that are PostgREST-specific and don't come from the database. These errors have unique codes that identify them and are documented in the :ref:`pgrst_errors` section.
Documentation improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~
* Added a :doc:`/how-tos/working-with-postgresql-data-types` how-to, which contains explanations and examples on how to work with different PostgreSQL data types such as timestamps, ranges or PostGIS types, among others.
* Added in-database and environment variable settings for each :ref:`configuration variable <config_full_list>`.
* Added the :ref:`file_descriptors` subsection.
* Added a reference page for :doc:`Error documentation </errors>`.
* Moved the :ref:`error_source` and the :ref:`status_codes` sections to the :doc:`errors reference page </errors>`.
* Moved the *Casting type to custom JSON* how-to to the :ref:`casting_range_to_json` subsection.
* Removed direct links for PostgREST versions older than 8.0 from the versions menu.
* Removed the *Embedding table from another schema* how-to.
* Restructured the :ref:`resource_embedding` section:
- Added a :ref:`one-to-many` and :ref:`many-to-one` subsections.
- Renamed the *Embedding through join tables* subsection to :ref:`many-to-many`.
* Split up the *Insertions/Updates* section into :ref:`insert` and :ref:`update`.
Breaking changes
----------------
* Many-to-many relationships now require that foreign key columns be part of the join table composite key
- This was needed to reduce :ref:`embed_disamb` errors in complex schemas(`#2070 <https://github.com/PostgREST/postgrest/issues/2070>`_).
- For migrating to this version, the less invasive method is to use :ref:`computed_relationships` to replace the previous many-to-many relationships.
- Otherwise you can change your join table primary key. For example with ``alter table permission_user drop constraint permission_user_pkey, add primary key (id, user_id, permission_id);``
* Views now are not detected when embedding using :ref:`target_disamb`.
- This embedding form was easily made ambiguous whenever a new view was added(`#2277 <https://github.com/PostgREST/postgrest/issues/2277>`_).
- For migrating to this version, you can use :ref:`computed_relationships` to replace the previous view relationships.
- :ref:`hint_disamb` works as usual on views.
* ``limit/offset`` now limits the affected rows on ``UPDATE``/``DELETE``
- Previously, ``limit``/``offset`` only limited the returned rows but not the actual updated rows(`#2156 <https://github.com/PostgREST/postgrest/issues/2156>`_)
* ``max-rows`` is no longer applied on ``POST``, ``PATCH``, ``PUT`` and ``DELETE`` returned rows
- This was misleading because the affected rows were not really affected by ``max-rows``, only the returned rows were limited(`#2155 <https://github.com/PostgREST/postgrest/issues/2155>`_)
* Return ``204 No Content`` without ``Content-Type`` for RPCs returning ``VOID``
- Previously, those RPCs would return ``null`` as a body with ``Content-Type: application/json`` (`#2001 <https://github.com/PostgREST/postgrest/issues/2001>`_).
* Using ``Prefer: return=representation`` no longer returns a ``Location`` header
- This reduces unnecessary computing for all insertions (`#2312 <https://github.com/PostgREST/postgrest/issues/2312>`_)
Bug fixes
---------
* Return ``204 No Content`` without ``Content-Type`` for ``PUT`` (`#2058 <https://github.com/PostgREST/postgrest/issues/2058>`_)
* Clarify error for failed schema cache load. (`#2107 <https://github.com/PostgREST/postgrest/issues/2107>`_)
- From ``Database connection lost. Retrying the connection`` to ``Could not query the database for the schema cache. Retrying.``
* Fix silently ignoring filter on a non-existent embedded resource (`#1771 <https://github.com/PostgREST/postgrest/issues/1771>`_)
* Remove functions, which are not callable due to unnamed arguments, from schema cache and OpenAPI output. (`#2152 <https://github.com/PostgREST/postgrest/issues/2152>`_)
* Fix accessing JSON array fields with ``->`` and ``->>`` in ``?select=`` and ``?order=``. (`#2145 <https://github.com/PostgREST/postgrest/issues/2145>`_)
* Ignore ``max-rows`` on ``POST``, ``PATCH``, ``PUT`` and ``DELETE`` (`#2155 <https://github.com/PostgREST/postgrest/issues/2155>`_)
* Fix inferring a foreign key column as a primary key column on views (`#2254 <https://github.com/PostgREST/postgrest/issues/2254>`_)
* Restrict generated many-to-many relationships (`#2070 <https://github.com/PostgREST/postgrest/issues/2070>`_)
- Only adds many-to-many relationships when a table has foreign keys to two other tables and these foreign key columns are part of the table's primary key columns.
* Allow casting to types with underscores and numbers (e.g. ``select=oid_array::_int4``) (`#2278 <https://github.com/PostgREST/postgrest/issues/2278>`_)
* Prevent views from breaking one-to-many/many-to-one embeds when using column or foreign key as target (`#2277 <https://github.com/PostgREST/postgrest/issues/2277>`_, `#2238 <https://github.com/PostgREST/postgrest/issues/2238>`_, `#1643 <https://github.com/PostgREST/postgrest/issues/1643>`_)
- When using a column or foreign key as target for embedding (``/tbl?select=*,col-or-fk(*)``), only tables are now detected and views are not.
- You can still use a column or an inferred foreign key on a view to embed a table (``/view?select=*,col-or-fk(*)``)
* Increase the ``db-pool-timeout`` to 1 hour to prevent frequent high connection latency (`#2317 <https://github.com/PostgREST/postgrest/issues/2317>`_)
* The search path now correctly identifies schemas with uppercase and special characters in their names (regression) (`#2341 <https://github.com/PostgREST/postgrest/issues/2341>`_)
* "404 Not Found" on nested routes and "405 Method Not Allowed" errors no longer start an empty database transaction (`#2364 <https://github.com/PostgREST/postgrest/issues/2364>`_)
* Fix inaccurate result count when an inner embed was selected after a normal embed in the query string (`#2342 <https://github.com/PostgREST/postgrest/issues/2342>`_)
* ``OPTIONS`` requests no longer start an empty database transaction (`#2376 <https://github.com/PostgREST/postgrest/issues/2376>`_)
* Allow using columns with dollar sign ($) without double quoting in filters and ``select`` (`#2395 <https://github.com/PostgREST/postgrest/issues/2395>`_)
* Fix loop crash error on startup in PostgreSQL 15 beta 3. ``Log: "UNION types \"char\" and text cannot be matched."`` (`#2410 <https://github.com/PostgREST/postgrest/issues/2410>`_)
* Fix race conditions managing database connection helper (`#2397 <https://github.com/PostgREST/postgrest/issues/2397>`_)
* Allow ``limit=0`` in the request query to return an empty array (`#2269 <https://github.com/PostgREST/postgrest/issues/2269>`_)
Thanks
------
Big thanks from the `PostgREST team <https://github.com/orgs/PostgREST/people>`_ to our sponsors!
.. container:: image-container
.. image:: ../_static/cybertec-new.png
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
:width: 13em
.. image:: ../_static/2ndquadrant.png
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
:width: 13em
.. image:: ../_static/retool.png
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
.. image:: ../_static/gnuhost.png
:target: https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
.. image:: ../_static/supabase.png
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
:width: 13em
.. image:: ../_static/oblivious.jpg
:target: https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
* Evans Fernandes
* `Jan Sommer <https://github.com/nerfpops>`_
* `Franz Gusenbauer <https://www.igutech.at/>`_
* `Daniel Babiak <https://github.com/dbabiak>`_
* Tsingson Qin
* Michel Pelletier
* Jay Hannah
* Robert Stolarz
* Nicholas DiBiase
* Christopher Reid
* Nathan Bouscal
* Daniel Rafaj
* David Fenko
* Remo Rechkemmer
* Severin Ibarluzea
* Tom Saleeba
* Pawel Tyll
If you like to join them please consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
-153
View File
@@ -1,153 +0,0 @@
PostgREST 10.2.0
================
This minor version adds bug fixes and some features that provide stability to v10.0.0. These release notes include the changes added in versions `10.1.0 <https://github.com/PostgREST/postgrest/releases/tag/v10.1.0>`_, `10.1.1 <https://github.com/PostgREST/postgrest/releases/tag/v10.1.1>`_ and `10.1.2 <https://github.com/PostgREST/postgrest/releases/tag/v10.1.2>`_. You can look at the detailed changelog and download the pre-compiled binaries on the `GitHub release page <https://github.com/PostgREST/postgrest/releases/tag/v10.2.0>`_.
Features
--------
Pool Connection Lifetime
~~~~~~~~~~~~~~~~~~~~~~~~
To prevent memory leaks caused by long-lived connections, PostgREST limits their lifetime in the pool through :ref:`db-pool-max-lifetime`.
Pool Connection Acquisition Timeout
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There is now a time limit to wait for pool connections to be acquired. If a new request cannot get a connection in the time specified in :ref:`db-pool-acquisition-timeout` then a response with a ``504`` status is returned.
Documentation improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~
* Added HTTP status codes to the :ref:`pgrst_errors`.
* Added a how-to on :ref:`sql-user-management-using-postgres-users-and-passwords`.
* Updated the :ref:`Heroku installation page <deploy_heroku>`.
Changes
-------
* Removed ``db-pool-timeout`` option because it was removed in the ``hasql-pool`` library that PostgREST uses for SQL connections. (`#2444 <https://github.com/PostgREST/postgrest/issues/2444>`_)
Deprecated
----------
* Deprecate bulk-calls when including the ``Prefer: params=multiple-objects`` in the request. It is preferable to use a function with an :ref:`array <s_procs_array>` or JSON parameter for a better performance. (`#1385 <https://github.com/PostgREST/postgrest/issues/1385>`_)
Bug fixes
---------
* Reduce allocations communication with PostgreSQL, particularly for request bodies. (`#2261 <https://github.com/PostgREST/postgrest/issues/2261>`_, `#2349 <https://github.com/PostgREST/postgrest/issues/2349>`_, `#2467 <https://github.com/PostgREST/postgrest/issues/2467>`_)
* Fix ``SIGUSR1`` to fully flush the connection pool. (`#2401 <https://github.com/PostgREST/postgrest/issues/2401>`_, `#2444 <https://github.com/PostgREST/postgrest/issues/2444>`_)
* Fix opening an empty transaction on failed resource embedding. (`#2428 <https://github.com/PostgREST/postgrest/issues/2428>`_)
* Fix embedding the same table multiple times. (`#2455 <https://github.com/PostgREST/postgrest/issues/2455>`_)
* Fix a regression when embedding views where base tables have a different column order for foreign key columns (`#2518 <https://github.com/PostgREST/postgrest/issues/2518>`_)
* Fix a regression with the ``Location`` header when :ref:`inserting <insert>` into views with primary keys from multiple tables (`#2458 <https://github.com/PostgREST/postgrest/issues/2458>`_)
* Fix a regression in OpenAPI output with mode ``follow-privileges`` (`#2356 <https://github.com/PostgREST/postgrest/issues/2356>`_)
* Fix infinite recursion when loading schema cache with self-referencing view (`#2283 <https://github.com/PostgREST/postgrest/issues/2283>`_)
* Return status code ``200`` instead of ``404`` for ``PATCH`` requests which don't affect any rows (`#2343 <https://github.com/PostgREST/postgrest/issues/2343>`_)
* Treat the :ref:`computed relationships <computed_relationships>` that do not return ``SETOF`` as M2O/O2O relationship (`#2481 <https://github.com/PostgREST/postgrest/issues/2481>`_)
* Fix embedding a computed relationship with a normal relationship (`#2534 <https://github.com/PostgREST/postgrest/issues/2534>`_)
* Fix error message when ``[]`` is used inside ``select`` (`#2362 <https://github.com/PostgREST/postgrest/issues/2362>`_)
* Disallow ``!inner`` on computed columns (`#2475 <https://github.com/PostgREST/postgrest/issues/2475>`_)
* Ignore leading and trailing spaces in column names when parsing the query string (`#2285 <https://github.com/PostgREST/postgrest/issues/2285>`_)
* Fix ``UPSERT`` with PostgreSQL 15 (`#2545 <https://github.com/PostgREST/postgrest/issues/2545>`_)
* Fix embedding views with multiple references to the same base column (`#2459 <https://github.com/PostgREST/postgrest/issues/2459>`_)
* Fix regression when embedding views with partial references to multi column foreign keys (`#2548 <https://github.com/PostgREST/postgrest/issues/2548>`_)
* Fix regression when requesting ``limit=0`` and ``db-max-row`` is set (`#2558 <https://github.com/PostgREST/postgrest/issues/2558>`_)
* Return a clear error without hitting the database when trying to update or insert an unknown column with ``?columns`` (`#2542 <https://github.com/PostgREST/postgrest/issues/2542>`_)
* Fix bad M2M embedding on RPC (`#2565 <https://github.com/PostgREST/postgrest/issues/2565>`_)
* Replace misleading error message when no function is found with a hint containing functions/parameters names suggestions (`#2575 <https://github.com/PostgREST/postgrest/issues/2575>`_)
* Move explanation about "single parameters" from the ``message`` to the ``details`` in the error output (`#2582 <https://github.com/PostgREST/postgrest/issues/2582>`_)
* Replace misleading error message when no relationship is found with a hint containing parent/child names suggestions (`#2569 <https://github.com/PostgREST/postgrest/issues/2569>`_)
* Add the required OpenAPI items object when the parameter is an array (`#1405 <https://github.com/PostgREST/postgrest/issues/1405>`_)
* Add upsert headers for ``POST`` requests to the OpenAPI output (`#2592 <https://github.com/PostgREST/postgrest/issues/2592>`_)
* Fix foreign keys pointing to ``VIEW`` instead of ``TABLE`` in OpenAPI output (`#2623 <https://github.com/PostgREST/postgrest/issues/2623>`_)
* Consider any PostgreSQL authentication failure as fatal and exit immediately (`#2622 <https://github.com/PostgREST/postgrest/issues/2622>`_)
* Fix ``NOTIFY pgrst`` not reloading the db connections catalog cache (`#2620 <https://github.com/PostgREST/postgrest/issues/2620>`_)
* Fix ``db-pool-acquisition-timeout`` not logging to stderr when the timeout is reached (`#2667 <https://github.com/PostgREST/postgrest/issues/2667>`_)
* Fix PostgreSQL resource leak with long-lived connections through the :ref:`db-pool-max-lifetime` configuration (`#2638 <https://github.com/PostgREST/postgrest/issues/2638>`_)
* There is now a stricter parsing of the query string. Instead of silently ignoring, the parser now returns a :ref:`PostgREST error <pgrst100>` on invalid syntax. (`#2537 <https://github.com/PostgREST/postgrest/issues/2537>`_)
Thanks
------
Big thanks from the `PostgREST team <https://github.com/orgs/PostgREST/people>`_ to our sponsors!
.. container:: image-container
.. image:: ../_static/cybertec-new.png
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
:width: 13em
.. image:: ../_static/2ndquadrant.png
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
:width: 13em
.. image:: ../_static/retool.png
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
.. image:: ../_static/gnuhost.png
:target: https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
.. image:: ../_static/supabase.png
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
:width: 13em
.. image:: ../_static/oblivious.jpg
:target: https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
* Evans Fernandes
* `Jan Sommer <https://github.com/nerfpops>`_
* `Franz Gusenbauer <https://www.igutech.at/>`_
* `Daniel Babiak <https://github.com/dbabiak>`_
* Tsingson Qin
* Michel Pelletier
* Jay Hannah
* Robert Stolarz
* Nicholas DiBiase
* Christopher Reid
* Nathan Bouscal
* Daniel Rafaj
* David Fenko
* Remo Rechkemmer
* Severin Ibarluzea
* Tom Saleeba
* Pawel Tyll
If you like to join them please consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
-26
View File
@@ -1,26 +0,0 @@
v5.2.0
======
* Explicit qualification introduced in ``v5.0`` is no longer necessary, this section will not be included from this version onwards. A :ref:`db-extra-search-path` configuration parameter was introduced to avoid the need to explictly qualify database objects. If you install PostgreSQL extensions on the ``public`` schema, they'll work normally from now on.
* Now you can filter :ref:`tabs-cols-w-spaces`.
* Included the ability to quote columns that have :ref:`reserved-chars`.
* Thanks to `Zhou Feng <https://github.com/zhoufeng1989>`_, now is possible to reference an external file in :ref:`db-uri`.
* Thanks to `Russell Davies <https://github.com/russelldavies>`_, Json Web Key Sets are now accepted by :ref:`jwt-secret`.
Thanks
------
This release was made possible thanks to:
* `Daniel Babiak <https://github.com/dbabiak>`_
* `Michel Pelletier <https://github.com/michelp>`_
* Tsingson Qin
* Jay Hannah
* Victor Adossi
* Petr Beles
If you like to join them please consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
-79
View File
@@ -1,79 +0,0 @@
.. |br| raw:: html
<br />
v6.0.2
======
Full changelog is available at `PostgREST releases page <https://github.com/PostgREST/postgrest/releases>`_.
Added
-----
* Ignoring payload keys for insert/update can be now done with the ``?columns`` query parameter. See :ref:`specify_columns`.
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
* `websearch_to_tsquery <https://www.postgresql.org/docs/current/functions-textsearch.html#id-1.5.8.19.7.2.2.7.1.1.1>`_ can now be used
through the ``wfts`` operator. See :ref:`fts`.
|br| -- `@herulume <https://github.com/herulume>`_
* Resource Embedding on materialized views is now possible. See :ref:`embedding_views`.
|br| -- `@vitorbaptista <https://github.com/vitorbaptista>`_
* Bulk calling an RPC is now allowed. See :ref:`bulk_call`.
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
* It's now possible to request a ``text/plain`` output. See :ref:`scalar_return_formats`.
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
* Config option for specifying PostgREST database pool timeout ``db-pool-timeout``.
|br| -- `@Qu4tro <https://github.com/Qu4tro>`_
* Config option for binding the PostgREST web server to an unix socket. See :ref:`server-unix-socket`.
|br| -- `@Dansvidania <https://github.com/Dansvidania>`_
* Config option for extending the supported media types. See :ref:`raw-media-types`.
|br| -- `@Dansvidania <https://github.com/Dansvidania>`_
* We now offer an statically linked binary for Linux. Look for **postgrest-<version>-linux-x64-static.tar.xz** on the
`releases page <https://github.com/PostgREST/postgrest/releases>`_.
|br| -- `@clojurians-org <https://github.com/clojurians-org>`_
* A :ref:`how_tos` section was added to the documentation.
Changed
-------
* ``SIGHUP`` support was removed. You should use ``SIGUSR1`` instead. See :ref:`schema_reloading`.
* server-host default of ``127.0.0.1`` was changed to ``!4``. See :ref:`server-host`.
Thanks
------
This release is sponsored by:
.. image:: ../_static/cybertec.png
:target: https://www.cybertec-postgresql.com/en/
:width: 13em
.. image:: ../_static/2ndquadrant.png
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
:width: 13em
.. image:: ../_static/retool.png
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
* `Daniel Babiak <https://github.com/dbabiak>`_
* Evans Fernandes
* Tsingson Qin
* Michel Pelletier
* Jay Hannah
* Robert Stolarz
* Kofi Gumbs
* Nicholas DiBiase
* Christopher Reid
* Nathan Bouscal
If you like to join them please consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
-106
View File
@@ -1,106 +0,0 @@
.. |br| raw:: html
<br />
v7.0.0
======
You can download this release at the `PostgREST v7.0.0 release page <https://github.com/PostgREST/postgrest/releases/tag/v7.0.0>`_.
Added
-----
* Support for :ref:`Switching to a schema <multiple-schemas>` defined in :ref:`db-schemas`.
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_, `@mahmoudkassem <https://github.com/mahmoudkassem>`_
* Support for :ref:`planned_count` and :ref:`estimated_count`.
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_, `@LorenzHenk <https://github.com/LorenzHenk>`_
* Support for the :ref:`on_conflict <on_conflict>` query parameter to UPSERT based on a unique constraint.
|br| -- `@ykst <https://github.com/ykst>`_
* Support for :ref:`Resource Embedding Disambiguation <embed_disamb>`.
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
* Support for user defined socket permission via :ref:`server-unix-socket-mode` config option
|br| -- `@Dansvidania <https://github.com/Dansvidania>`_
* HTTP logic improvements -- `@steve-chavez <https://github.com/steve-chavez>`_
+ Support for HTTP HEAD requests.
+ GUCs for :ref:`guc_req_path_method`.
+ Support for :ref:`pre_req_headers`.
+ Allow overriding provided headers(Content-Type, Location, etc) by :ref:`guc_resp_hdrs`
+ Access to the ``Authorization`` header value through ``request.header.authorization``
* Documentation improvements
+ Explanation for :doc:`Schema Structure <../schema_structure>`.
+ Reference for :ref:`s_proc_embed`.
+ Reference for :ref:`mutation_embed`.
+ Reference for filters on :ref:`json_columns`.
+ How-to for :ref:`providing_img`.
+ Added :ref:`community_tutorials` section.
Fixed
-----
* Allow embedding a view when its source table foreign key is UNIQUE
|br| -- `@bwbroersma <https://github.com/bwbroersma>`_
* ``Accept: application/vnd.pgrst.object+json`` behavior is now enforced for POST/PATCH/DELETE regardless of ``Prefer: return=minimal``
|br| -- `@dwagin <https://github.com/dwagin>`_
* Fix self join resource embedding on PATCH
|br| -- `@herulume <https://github.com/herulume>`_, `@steve-chavez <https://github.com/steve-chavez>`_
* Allow PATCH/DELETE without ``Prefer: return=minimal`` on tables with no SELECT privileges
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
* Fix many to many resource embedding for RPC/PATCH
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
Changed
-------
* :ref:`bulk_call` should now be done by specifying a ``Prefer: params=multiple-objects`` header. This fixes a performance regression when calling stored procedures.
* Resource Embedding now outputs an error when multiple relationships between two tables are found, see :ref:`embed_disamb`.
* ``server-proxy-uri`` config option has been renamed to :ref:`openapi-server-proxy-uri`.
* Default Unix Socket file mode from 755 to 660
Thanks
------
This release was made possible thanks to:
.. image:: ../_static/cybertec.png
:target: https://www.cybertec-postgresql.com/en/
:width: 13em
.. image:: ../_static/2ndquadrant.png
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
:width: 13em
.. image:: ../_static/retool.png
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
* `Daniel Babiak <https://github.com/dbabiak>`_
* Evans Fernandes
* `Jan Sommer <https://github.com/nerfpops>`_
* Tsingson Qin
* Michel Pelletier
* Jay Hannah
* Robert Stolarz
* Kofi Gumbs
* Nicholas DiBiase
* Christopher Reid
* Nathan Bouscal
* Daniel Rafaj
* David Fenko
If you like to join them please consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
-69
View File
@@ -1,69 +0,0 @@
.. |br| raw:: html
<br />
v7.0.1
======
You can see the full changelog at `PostgREST v7.0.1 release page <https://github.com/PostgREST/postgrest/releases/tag/v7.0.1>`_.
Fixed
-----
* Fix overloaded computed columns on RPC
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
* Fix POST, PATCH, DELETE with ``?select=`` and ``Prefer: return=minimal`` and PATCH with empty body
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
* Fix missing ``openapi-server-proxy-uri`` config option
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
* Fix ``Content-Profile`` not working for POST RPC
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
* Fix PUT restriction for including all columns in payload
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
* Documentation improvements
+ Added package managers to :ref:`install`.
Changed
-------
* From this version onwards, the release page will include a single Linux static executable that can be run on any Linux distribution.
Thanks
------
This release was made possible thanks to:
.. image:: ../_static/cybertec.png
:target: https://www.cybertec-postgresql.com/en/
:width: 13em
.. image:: ../_static/2ndquadrant.png
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
:width: 13em
.. image:: ../_static/retool.png
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
* `Daniel Babiak <https://github.com/dbabiak>`_
* Evans Fernandes
* `Jan Sommer <https://github.com/nerfpops>`_
* Tsingson Qin
* Michel Pelletier
* Jay Hannah
* Robert Stolarz
* Kofi Gumbs
* Nicholas DiBiase
* Christopher Reid
* Nathan Bouscal
* Daniel Rafaj
* David Fenko
If you'd like to join them, consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.

Some files were not shown because too many files have changed in this diff Show More