Compare commits

..
421 Commits
Author SHA1 Message Date
steve-chavez ec89f6b90c bump version to 12.2.0 2024-06-11 09:57:05 -05:00
steve-chavez 82aa58a08f docs: deprecate EOL pg versions 2024-06-11 09:43:09 -05:00
Laurence Isla db85faf3ed docs: wrap in double quotes the filters with reserved characters in logical operators 2024-06-05 17:59:44 -05:00
Wolfgang Walther a2d00e305a nix: Make postgrest-release work on remotes without .git suffix 2024-06-05 21:32:41 +02:00
steve-chavez 4beac10d3d prevent GSSAPI error between Listener and pool
Brings back the the signaling/waiting between the connection pool and
the Listener.

Prevents the GSSAPI error shown on https://github.com/PostgREST/postgrest/issues/3569
2024-06-05 13:58:34 -05:00
Wolfgang Walther 70a8a80491 Revert "fix: Build static postgrest with GSSAPI support"
This reverts commit c94aa9ccd9.
2024-06-05 19:59:07 +02:00
Wolfgang Walther b4d235f72a nix: refactor to remove TODO
We have meanwhile received the commit in question.
2024-06-05 19:53:29 +02:00
steve-chavez 1a8b6972a8 correct exponential backoff on Listener
Clears the limitation mentioned on

https://github.com/PostgREST/postgrest/pull/3536

The Listener no longer uses the https://hackage.haskell.org/package/retry
package and instead uses a much simpler IORef in AppState for the
delays.

Additionally it no longer uses exception throwing/catching, which
is rather messy and brings some
concerns(https://github.com/PostgREST/postgrest/issues/3569#issuecomment-2146013327).
2024-06-05 08:52:18 -05:00
Laurence Isla aaf2d2e430 docs: add example for double embedding when doing OR filtering across embeds 2024-06-04 16:51:28 -05:00
Joel JakobssonandGitHub a46bea16e2 nix: fix slocat overlay with nix 2.22
This seems to happen on nix 2.22 only, v2.21 in CI and v2.20 locally work fine. The error is:

vendor folder is empty, please set 'vendorHash = null;' in your expression
For full logs, run 'nix-store -l /nix/store/kxnnr344n7gsxzc6kycj19hs19rvddjj-slocat-go-modules.drv'.
error: 1 dependencies of derivation '/nix/store/x9w480k36a11i99m6zp12d5cjijsn3lm-slocat.drv' failed to build

Since the slocat module doesn't actually have any dependencies, this shouldn't matter much.
2024-06-03 10:24:35 +02:00
Laurence Isla 50b2d302d0 docs: use "curl --get" for better readability when necessary 2024-05-31 20:24:06 -05:00
steve-chavez 82a43c2767 nix: postgrest-gen-ctags use haskdogs 2024-05-25 15:21:44 -05:00
steve-chavez a51a74b3c1 docs: shorten CLI 2024-05-24 18:05:45 -05:00
steve-chavez 1c371d7340 docs: clarify architecture 2024-05-24 18:05:45 -05:00
steve-chavez 30ca64d849 docs: improve observability 2024-05-24 18:05:45 -05:00
Laurence Isla 9c165e3edb fix: log connection pool events on log-level="debug" instead of "info" 2024-05-24 17:19:31 -05:00
Laurence Isla c8612f1df1 test: use only the first line in the logs to check the server version 2024-05-24 17:19:31 -05:00
steve-chavez da9e497ef1 changelog: add architecture doc 2024-05-23 19:34:42 -05:00
steve-chavez 47e9a2d134 refactor: Listener to own module 2024-05-23 19:34:42 -05:00
Laurence Isla 4e0ffa6d0a changelog: fix link for deprecated feature 2024-05-22 19:16:01 -05:00
Laurence Isla f5767b8d86 docs: add "config" and "schema_cache" endpoints to the admin server 2024-05-22 19:16:01 -05:00
Taimoor ZaeemandSteve Chavez 8cbcf9867b feat: add config db-hoisted-tx-settings to allow only hoisted function settings 2024-05-21 19:50:03 -05:00
Taimoor ZaeemandSteve Chavez a1582a6136 test: clean test_role_settings in io-tests 2024-05-21 19:50:03 -05:00
Laurence IslaandSteve Chavez 1ca5b6f6ba docs: ignore linkcheck for blog.frankel.ch 2024-05-21 17:23:11 -05:00
Laurence IslaandGitHub aea563bd82 fix: remove verbosity from some error logs
Error logs starting with "An error occured..." are replaced with "Failed to..."
2024-05-21 16:44:28 -05:00
Laurence IslaandGitHub 5d3d09923f refactor: unDRY PGRST prefix in errors for better search/grep 2024-05-21 14:34:00 -05:00
Wolfgang WaltherandWolfgang Walther 71711bb935 ci: Prevent caching cabal and stack cache in PRs
This was supposed to happen in c4b0bd34 and 279febe2, but somehow didn't
work, yet.
2024-05-21 20:44:41 +02:00
Wolfgang Walther ad5bb38d70 ci: Don't cancel previous runs for tag pipelines
Those create annoying "cancelled" notifications which looks like CI on
main was failing. It's not, though.

By just disabling the cancel-in-progress setting, but keeping the group
intact, this should queue multiple tag jobs / tag workflows behind each
other and still avoids the underlying problem which occurs when running
them in parallel.
2024-05-21 20:43:18 +02:00
Wolfgang Walther 89eae607f5 ci: Run build workflow in PRs which change composite actions
This should have been added in c4b0bd34 for cache-on-main and way before
that for artifact-from-cirrus.
2024-05-21 19:57:39 +02:00
Wolfgang Walther dcfc6673b7 ci: Fix out-of-sync GHC version for stack jobs 2024-05-21 19:39:15 +02:00
Laurence IslaandGitHub 7671d63f06 refactor: add isParent flag to O2O relationships
It allows to identify the side with the FK when isParent == False
2024-05-21 12:11:27 -05:00
Laurence Isla 04d6f41f45 chore: update commit prefix info in the PR template 2024-05-21 09:03:12 -05:00
Taimoor ZaeemandSteve Chavez 5cc32c7f87 fix: fix incorrect 413 error on pg 54* errors 2024-05-20 18:59:33 -05:00
renovate[bot]andWolfgang Walther 11c9e8dac9 chore(deps): update cachix/cachix-action action to v15 2024-05-20 19:26:45 +02:00
renovate[bot]andWolfgang Walther b8436fd397 chore(deps): update codecov/codecov-action action to v4.4.1 2024-05-20 19:26:18 +02:00
renovate[bot]andWolfgang Walther 4156070838 chore(deps): update haskell-actions/setup action to v2.7.3 2024-05-20 19:26:07 +02:00
Wolfgang WaltherandWolfgang Walther 9e6a89ffb8 ci: Install GHC/stack via haskell-actions/setup consistently
This action makes sure to always have the correct GHC and/or stack
version installed in all environments. This solves problem where ghc or
stack might not be available on newer macos images anymore or where
ghcup is not available by default on our new custom github runner on
arm.
2024-05-20 15:43:00 +02:00
Wolfgang WaltherandWolfgang Walther d08e5959a0 ci: Reduce stack cache size
This removes the GHC install from stack caches to reduce size.
2024-05-20 15:43:00 +02:00
Wolfgang WaltherandWolfgang Walther 279febe26b ci: Split cabal and stack work caches from regular cache
This is a first step to split up the cabal and stack caches in separate
pieces. Here we split the work folder, which just contains the
postgrest-specific build artifacts, into a separate cache.

More fine-grained caching should give us better cache hits and much
fewer upload size in the regular case, improving CI performance.

Since the work file caches are very small (about 30-40 MB) they are
cached for PRs, too. This will allow the majority of PRs, which only
change source code files, but no dependencies, to still have cached
their build files for additional commits.
2024-05-20 15:43:00 +02:00
Wolfgang WaltherandWolfgang Walther c4b0bd347b ci: Only save caches on main and release branches
This restores caches on all branches and pull requests, but only stores
them on the main branch and release branches. This prevents those caches
from being evicted early when we hit the 10 GB limit quickly.
2024-05-20 15:43:00 +02:00
steve-chavez 7e61c9deb0 feat: force read-write for listener connection 2024-05-19 23:14:19 -05:00
steve-chavez 3cf565614d fix: listener retries with exponential backoff
Also corrects the admin ready response which now considers the listener
state.
2024-05-19 20:48:59 -05:00
steve-chavez bfa4e1bedb test: isolate pg_terminate_backend to appname
The pg_terminate_backend done in the io test:

`test_fail_with_automatic_recovery_disabled_and_terminated_using_query`

Can affect other connections.
2024-05-19 19:58:27 -05:00
steve-chavez d5a4c5609e refactor: greppable 57P01 error code 2024-05-19 19:58:27 -05:00
renovate[bot]andWolfgang Walther 80783a7ee8 chore(deps): update actions/checkout action to v4.1.6 2024-05-19 21:24:42 +02:00
steve-chavez c8ba505b99 docs: linkcheck ignore patreon 2024-05-19 13:44:23 -05:00
steve-chavez ae91fd89b3 test: assert empty response text on 204 2024-05-19 13:44:23 -05:00
steve-chavez 756aad7827 fix: listener silent fail on replica
Update hasql-notifications to include the fix on
https://github.com/diogob/hasql-notifications/issues/24.

Which now reveals the following error:

```
$ postgrest-with-postgresql-16 --replica -f test/spec/fixtures/load.sql postgrest-run

17/May/2024:18:35:38 -0500: Successfully connected to PostgreSQL 16.2 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 13.2.0, 64-bit
17/May/2024:18:35:38 -0500: Could not listen for notifications on the "pgrst" channel. ERROR:  cannot execute LISTEN during recovery
17/May/2024:18:35:38 -0500: Retrying listening for notifications...
```

This is still not good because the LISTEN channel will be retried
forever without a backoff.
2024-05-18 23:33:04 -05:00
steve-chavez aa75412932 nix: PGRST_DB_URI preference for tmp db replica
When using `postgrest-with-postgresql-* --replica`, the PGRST_DB_URI
will set the replica host as preference. This to be able to run
quick manual tests with postgrest running on a replica.
2024-05-18 23:33:04 -05:00
Laurence IslaandWolfgang Walther ea4d1596b7 ci: fix release not executing when skipping CI for docs and tests 2024-05-17 08:09:19 +02:00
steve-chavez 33b6ba8199 refactor: move checkIsFatal logic to usePool
The fatal logic is now inside `usePool`. It centralizes the
logic which is better for Locality of Behavior.

Removes:

- The need to do checkIsFatal on other parts of the code
- SCFatalFail/ConnFatalFail states which are no longer needed.
2024-05-16 17:40:25 -05:00
renovate[bot]andWolfgang Walther 9d763aef00 chore(deps): update codecov/codecov-action action to v4.4.0 2024-05-15 09:20:39 +02:00
steve-chavez d25df459ea feat: add metric label for scache load
localhost:3001/metrics now includes:

pgrst_schema_cache_loads_total{status="FAIL"} 352.0
pgrst_schema_cache_loads_total{status="SUCCESS"} 3.0

This allows testing the failure case on:

https://github.com/PostgREST/postgrest/issues/3424#issuecomment-2104904910
2024-05-14 17:57:21 -05:00
steve-chavez 05447bae33 nix: add postgrest-gen-jwt/secret for manual tests
```
$ postgrest-gen-secret
uMd97XSQzNkA1CWhMZ7u88Pj0RNyhrpo

$ postgrest-gen-jwt postgrest_test_author
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA
```

Also modifies postgrest-run to include a default PGRST_JWT_SECRET for
quicker manual tests.
2024-05-13 12:37:08 -05:00
Wolfgang WaltherandWolfgang Walther f6b2aa5a5d chore(deps): Update stackage extra deps 2024-05-13 08:08:18 +02:00
Wolfgang WaltherandWolfgang Walther 923a271105 chore(deps): Update stackage snapshot to lts-22.20
The freebsd build is kept at 21.7 / GHC 9.4.5, because no newer GHC
versioin is supported here.
2024-05-13 08:08:18 +02:00
Wolfgang Walther b8fe512def ci: Avoid trying to upload loadtest reports for tag pipelines
In this case the loadtest doesn't run anymore, so the job would fail.
2024-05-12 15:28:13 +02:00
Wolfgang Walther 271b3b273d ci: Disable tagging releases in forks 2024-05-12 15:10:28 +02:00
Wolfgang Walther 5c040e74f5 ci: Prevent running check, docs and test suites on tags
This doesn't make sense, because each tag is only pushed when those
pipelines have already passed. Thus, we can save time and avoid wasting
resources and don't run those again.
2024-05-12 15:01:20 +02:00
Wolfgang Walther 4a3fdc175c ci: Skip release jobs on outdated tags
This happens a commit is pushed to main while the pipelines have not
finished for the previous commit. In this case the devel-tag pipelines
will run concurrently, leading to unpredictable results for the devel
release.
2024-05-12 14:58:27 +02:00
Wolfgang Walther aef29d49ba docs: Mark db_tx_end as db-configurable 2024-05-12 11:53:59 +02:00
Wolfgang Walther c6f152f5b4 refactor: Remove left-over raw_media_types from Database.hs
This was removed in #2825.
2024-05-12 11:53:45 +02:00
Taimoor ZaeemandWolfgang Walther 7a5079542c docs: update docs for pg error 53400 2024-05-12 11:47:05 +02:00
Wolfgang WaltherandWolfgang Walther 71885bdba6 test: Avoid freeport() collisions in io tests
It's very unlikely, but it can (and did) happen that both the server and
admin ports have the same number returned from freeport(). This then
leads to a situation where PostgREST will accept the same port in both
cases, because the host "localhost" will allow binding to ipv4 or ipv6
respectively. This will make the IO tests fail.

This change makes sure that the admin port will never be the same as the
server port and thus avoids this problem.
2024-05-12 11:45:06 +02:00
Wolfgang WaltherandWolfgang Walther 8392357863 test: Fix internal_schema_cache_sleep after 747c78f6
The $subject commit broke internal_schema_cache_sleep for other tests.
This reverts the order change, but keeps the scaling by x1000 to ms and
thus changes other users of this setting to the new scale.
2024-05-12 11:44:43 +02:00
Laurence IslaandSteve Chavez 334c2710f6 changelog: update to 12.0.3 2024-05-11 13:30:50 -05:00
Wolfgang Walther 07222cff7b chore: Restrict macos to v12 2024-05-10 08:19:29 +02:00
David BaynardandWolfgang Walther 575dd4cf70 nix: Make default.nix extensible
Moving values defined with `let` to the function arguments (with
defaults) means other consumers of `default.nix` can customize these
values.

One example is a `flake.nix`, which can then supply the `nixpkgs` input.
2024-05-09 22:23:06 +02:00
Wolfgang Walther d03b321659 ci: Actually pass GHC_VERSION to arm build script 2024-05-09 21:58:37 +02:00
Wolfgang Walther 5580fe0040 ci: Make arm scripts fail on error 2024-05-09 21:46:10 +02:00
Wolfgang Walther 7d713b8c53 ci: Tag arm docker image properly for releases 2024-05-09 21:46:10 +02:00
Wolfgang Walther 2cb45bd5f8 ci: Extract changelog properly for releases 2024-05-09 21:46:10 +02:00
Andrei DziahelandWolfgang Walther 3026c1f308 fix: Parse accept header case-insensitively
The Accept header is parsed case-insensitively now, introducing proper
handling of media types specified in upper- and/or mixed-case.

Fixes #3478
2024-05-09 19:30:42 +02:00
Wolfgang Walther 1fa35cb3b8 test: Fix coverage & style check from c4295b3d 2024-05-09 18:50:00 +02:00
Wolfgang Walther 01a56db7d8 test: Make unicode insert test work in parallel mode
Changing the PK here will avoid duplicate conflicts with another test.

References #1799
2024-05-09 18:05:44 +02:00
Wolfgang Walther 577a7c7598 test: Move limited delete/update tests into separate spec file
This potentially allows to run the remaining tests in those files in
parallel mode.

References #1799
2024-05-09 18:05:44 +02:00
Wolfgang Walther c4295b3d63 test: Make PgSafeUpdateSpec parallel-ready 2024-05-09 18:05:44 +02:00
Wolfgang Walther aa94e436fa test: Missing space 2024-05-09 18:05:44 +02:00
Wolfgang Walther cf7a789e9e test: Improve failing test output for requestMutation 2024-05-09 18:05:44 +02:00
Wolfgang Walther a7ed5db78a test: Sort test suites in spec/Main.hs 2024-05-09 18:05:44 +02:00
Wolfgang WaltherandWolfgang Walther d903a8a115 nix: Adjust postgrest-release to new release workflow
This changes the postgrest-release tool to work with our new workflow.
It can be run on main and the v* release branches. When on a release
branch, it will bump a patch version and push to that branch only.

When on main, it will bump a minor version by default. To bump a major
version, pass --major. This first bump will be force-pushed to the
v<major> branch. A second bump to the current development version will
then be pushed to the main branch.

The tool will not tag commits anymore - this happens in CI
automatically.

References #3113
Resolves #3082
2024-05-09 18:05:04 +02:00
Wolfgang Walther a427fb67f6 ci: Fetch tags before checking whether tag exists 2024-05-09 14:53:38 +02:00
Wolfgang Walther 3afa5f6a36 ci: Move docker-hub-readme.md to base folder
This is not related to nix tooling anymore, because a github action
without any nix tooling is updating this now.
2024-05-09 14:35:52 +02:00
renovate[bot]andWolfgang Walther fdcadb3d53 chore(deps): update actions/checkout action to v4.1.5 2024-05-09 14:08:51 +02:00
Wolfgang WaltherandWolfgang Walther 747c78f6f4 test: Prevent test_admin_ready_includes_schema_cache_state from timing out
By increasing the delays in this test by factor 400x, postgrest will not
swamp pg with connection retries after the failed schema cache anymore.

This would happen because there is no backoff included after fatal
errors. Once it does, the io tests hang indefinitely in CI.
2024-05-09 13:36:03 +02:00
Wolfgang WaltherandWolfgang Walther e96e16fa27 test: Reset statement timeout after each test
The statement timeout needs to be cleaned up after each test that
modifies it instead of before the test. Otherwise the changed timeout
leaks into other tests.
2024-05-09 13:36:03 +02:00
steve-chavez 0060abeb01 feat: /live and /ready respond with 500 on failure
503 is still used by /ready to indicate a transient state
that can be recovered from.
2024-05-08 17:19:48 -05:00
steve-chavez f9e9740999 nix: add postgrest-ctags command
Generates ctags for Haskell and Python code.
2024-05-08 12:27:26 -05:00
steve-chavez 1b584f7e9c refactor: is ready Admin logic to AppState 2024-05-08 11:27:58 -05:00
Wolfgang WaltherandWolfgang Walther 1374178f27 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-07 14:42:12 +02:00
Wolfgang Walther cb6151eb1b ci: Make artifact-from-cirrus action succeed when cirrus job doesn't start up in PR 2024-05-07 08:29:31 +02:00
Wolfgang Walther b006016d07 ci: Make "release / tag" job detect existing tags
The release / tag job has logic to decide whether to push a new tag on
stable branches, which depends on the all the tags being fetched. The
checkout action doesn't do that by default, so enable that.
2024-05-07 08:09:00 +02:00
steve-chavez df9b373465 docs: better place for application_name 2024-05-06 11:12:45 -05:00
steve-chavez b34c00c522 changelog: add missing entry for 3184 2024-05-06 11:12:45 -05:00
steve-chavez 1d4f31514f changelog: move 3340 from fixed to added
Since it was a feature
2024-05-06 11:12:45 -05:00
steve-chavez 7e91e5311d fix: not adding application_name on all URIs 2024-05-06 11:12:45 -05:00
Taimoor ZaeemandSteve Chavez 21bc48ad9a fix: fix wrong 503 Service Unavailable on pg error 53400 2024-05-06 07:54:11 -05:00
renovate[bot]andWolfgang Walther cabe744f01 chore(deps): update ubuntu docker tag to v24 2024-05-05 18:23:53 +02:00
Wolfgang Walther 463dfb552d chore(deps): update actions/checkout hash for release/prepare job
Accidentally committed an outdated version, this aligns it with all the
other references to the same action.
2024-05-05 17:48:44 +02:00
Wolfgang Walther a57d12b102 ci: Use the new DOCKER_ vars for docker-arm job 2024-05-05 17:11:22 +02:00
Wolfgang Walther d9ba9a8208 ci: Avoid devel release failure when no assets to clean up exist 2024-05-05 14:35:40 +02:00
Wolfgang Walther 8433f9812b ci: Replace assets of existing devel release properly 2024-05-05 13:38:58 +02:00
Wolfgang Walther c67f1c398c ci: Fix docker release job after 2f98d837 2024-05-05 13:33:48 +02:00
Wolfgang Walther 2f98d837ff ci: Push docker hub description automatically on main branch 2024-05-04 21:55:52 +02:00
Wolfgang Walther eec35d4569 ci: Avoid recreating devel release
Instead, the existing release is edited, which avoids notifications
for pre-releases.
2024-05-04 21:54:46 +02:00
Wolfgang Walther 9fe90bf9a8 ci: Fix pushing of devel-arm docker tag 2024-05-04 21:53:36 +02:00
Wolfgang Walther 58d8133454 ci: Enable docker push for releases 2024-05-04 21:14:40 +02:00
Wolfgang Walther fe0f2f708a ci: Use "devel" as the pre-release name instead of the commit title 2024-05-04 21:14:23 +02:00
Wolfgang WaltherandWolfgang Walther dd8d51ab71 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.

Resolves #2006
Resolves #2997
2024-05-04 20:42:45 +02:00
Wolfgang Walther cdcab34852 nix: Make postgrest-lint fail on dead nix code 2024-05-04 20:34:59 +02:00
Wolfgang Walther 1ca2d1f8be 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 14:56:57 +02:00
Wolfgang Walther 5aa62d944d ci: Force running FreeBSD build in tag and push pipelines
This now behaves similar to other CI build jobs, which always run on the
main branches, but only conditionally on PRs, depending on which files
changed.
2024-05-04 14:56:57 +02:00
renovate[bot]andWolfgang Walther 5c29742d7f chore(deps): update nixbuild/nix-quick-install-action action to v28 2024-05-04 13:35:13 +02:00
renovate[bot]andWolfgang Walther cc7165ca10 chore(deps): update codecov/codecov-action action to v4.3.1 2024-05-04 12:21:53 +02:00
renovate[bot]andWolfgang Walther 277342bf8c chore(deps): update ubuntu:jammy docker digest to a6d2b38 2024-05-04 12:21:37 +02:00
steve-chavez d9a51f23ce test: sanity tests for primary and replica
* new --replica option to `postgrest-with-postgresql-*`
* new command `postgrest-test-replica`
* new sanity tests on test_replica.py
* add postgrest-test-replica to postgrest-check and postgrest-coverage
2024-04-30 18:35:40 -05:00
steve-chavez cdb877135b test: flush stdout so readline doesn't hang 2024-04-30 13:52:51 -05:00
Taimoor ZaeemandSteve Chavez 7e5fd317e6 docs: add docs page for CLI 2024-04-29 12:51:54 -05:00
Laurence IslaandGitHub 40bd9a7769 ci: DRY the cleanup job for ARM servers 2024-04-29 12:20:00 -05:00
renovate[bot]andWolfgang Walther 5e41121551 chore(deps): update ubuntu:jammy docker digest to 6d7b5d3 2024-04-26 08:34:18 +02:00
renovate[bot]andWolfgang Walther 46d8dfde42 chore(deps): update actions/checkout action to v4.1.4 2024-04-26 08:31:33 +02:00
Laurence Isla 85a4e35a2d docs: fix http status for PGRST121 error 2024-04-24 15:04:35 -05:00
renovate[bot]andWolfgang Walther 359a14533b chore(deps): update actions/download-artifact action to v4.1.7 2024-04-24 20:53:56 +02:00
Laurence IslaandGitHub 69bbce5b32 feat: improve PGRST121 error message
* Clarify the message field
* Show failed MESSAGE or DETAIL in the the PGRST121 error's details field
* Show the correct JSON format in the hint field
2024-04-24 13:33:10 -05:00
steve-chavez 3eff4670f0 docs: metrics for schema cache and connection pool 2024-04-23 19:08:37 -05:00
steve-chavez 357400b2b8 feat: schema cache metrics 2024-04-23 19:08:37 -05:00
steve-chavez 653c7955b2 feat: connection pool metrics in admin server 2024-04-23 19:08:37 -05:00
steve-chavez 29cd7d195c nix: add instructions for haskell-packages.nix 2024-04-23 19:08:37 -05:00
Laurence IslaandGitHub e788776ec3 ci: clean ARM server even when previous steps or jobs fail 2024-04-23 16:34:55 -05:00
steve-chavez 3e615bd0d9 feat: add log-level=debug 2024-04-22 21:36:29 -05:00
renovate[bot]andWolfgang Walther bddfa2782d chore(deps): update actions/checkout action to v4.1.3 2024-04-22 21:30:23 +02:00
Wolfgang Walther 6858693291 ci: Be explicit about the version of linux and windows runner images
Using the -latest tag is potentially prone to errors, because an update of the tag
could break our CI. This recently happend with macos-latest, which we downgraded
to macos-12 earlier.

Using an explicit version reference makes this problem much less likely - in fact
renovate will pick up new versions once they exist and will suggest updates for it.
Thus, we will see the failures in a related PR instead of randomly everywhere.
2024-04-22 21:20:30 +02:00
renovate[bot]andWolfgang Walther dcb9f92b80 chore(deps): update actions/upload-artifact action to v4.3.3 2024-04-22 21:14:58 +02:00
renovate[bot]andWolfgang Walther 6a38cc098d chore(deps): update actions/download-artifact action to v4.1.6 2024-04-22 21:14:44 +02:00
Wolfgang WaltherandWolfgang Walther 81ceac8ba3 ci: revert to macos-12
GitHub Actions updated the latest macos image to macos-14 [1], which made both
of our macos build jobs fail.

This reverts to macos-12, which should fix CI for the moment. We can then look
into migrating to macos-14 explicitly.

[1]: https://github.blog/changelog/2024-04-01-macos-14-sonoma-is-generally-available-and-the-latest-macos-runner-image/
2024-04-22 21:11:03 +02:00
Taimoor ZaeemandSteve Chavez 88abf600c4 fix: fix wrong http status on pg error 42P17 infinite recursion 2024-04-22 13:40:07 -05:00
Wolfgang WaltherandWolfgang Walther 80f83f0366 nix: Update to latest nixpkgs#master 2024-04-20 17:06:29 +02:00
Wolfgang WaltherandWolfgang Walther b6a50ab8ed nix: Allow changing to a fork in nixpkgs-version.nix 2024-04-20 17:06:29 +02:00
Wolfgang Walther 87a463f857 test: fix broken io tests after last commit
Forgot to update the snapshots.
2024-04-20 16:03:53 +02:00
Wolfgang Walther ed407350ad fix: hoist function settings with memory units properly
f9ee1f7e introduced the hoisting of function settings as transaction-scoped
settings. However, this currently doesn't work with memory units, which are
case-sensitive according to the docs [1]. This removes the lowercasing of
values to make them work.

This is not added to the CHANGELOG, because this feature was not released, yet.

[1]: https://www.postgresql.org/docs/current/config-setting.html#CONFIG-SETTING-NAMES-VALUES
2024-04-20 13:58:04 +02:00
renovate[bot]andWolfgang Walther de788c9832 chore(deps): update ubuntu docker tag to v22 2024-04-20 12:39:22 +02:00
Wolfgang Walther 07dc37e66f chore: limit docutils to <0.21.0
sphinx-rtd-theme currently does not support docutils 0.21.x.

This restriction can be lifted once the upstream issue has been resolved:
https://github.com/readthedocs/sphinx_rtd_theme/issues/1557
2024-04-20 12:38:24 +02:00
renovate[bot]andWolfgang Walther c2994fb0b1 chore(deps): update ubuntu:focal docker digest to 71b82b8 2024-04-20 12:33:30 +02:00
renovate[bot]andWolfgang Walther 6863fe1e26 chore(deps): update actions/download-artifact action to v4.1.5 2024-04-20 12:33:06 +02:00
renovate[bot]andWolfgang Walther dc6399abad chore(deps): update actions/upload-artifact action to v4.3.2 2024-04-20 12:32:55 +02:00
Laurence IslaandSteve Chavez 0d13b842a6 fix: OpenAPI now tags a FK correctly on O2O relationships 2024-04-18 17:08:14 -05:00
steve-chavez 1bf0c54dd6 feat: log connection pool events on log-level=info 2024-04-15 18:31:51 -05:00
Steve ChavezandGitHub 9d1dc783bf test: use postgrest.read_stdout in io tests (#3412)
It's easier to maintain this way in case there are new log lines.
2024-04-15 13:51:23 -05:00
steve-chavez 69c6ce9c38 refactor: use LogLevel in Logger
* remove Logger dependency on Auth.
2024-04-14 20:10:01 -05:00
steve-chavez c57ec52229 refactor: make stdout explicit on Logger
Otherwise it's hard to know we're logging to stdout.
2024-04-14 20:10:01 -05:00
Taimoor ZaeemandSteve Chavez 973201a8d3 fix: remove rejected mediatype application/vnd.pgrst.object+json from response 2024-04-13 16:07:38 -05:00
steve-chavez fbc4d565ca refactor: move debounce from AppState to Logger
Will allow to capture accurate timeout metrics.
2024-04-12 14:29:39 -05:00
steve-chavez 2de32fc108 refactor: observation handler to AppConfig
With this:

- Is no longer necessary to pass observer as an argument
  to every function that needs observations.
- We can invoke the observer on every function that uses AppConfig.
  However it'd be better to just call the observer in the upper modules
  (like on App.hs).
2024-04-12 14:29:39 -05:00
Laurence IslaandGitHub 460259548d perf: fix space leaks by downgrading fuzzyset to v0.2.4 2024-04-11 14:13:20 -05:00
steve-chavez b88191299f docs: remove schema cache dep from Query 2024-04-10 19:35:23 -05:00
Laurence Isla 4bcd6725fe chore: update sponsor logos 2024-04-10 18:37:57 -05:00
Laurence Isla 70396d3f6c docs: add dark mode
- Uses sphinx-rtd-dark-mode for sphinx-rtd-theme
- Adds dark mode logos for sponsors
2024-04-10 18:37:57 -05:00
steve-chavez c9136816a2 docs: move ARCHITECTURE.md to architecture.rst 2024-04-10 15:51:08 -05:00
steve-chavez 4428253efe docs: move health_check.rst to admin_server.rst 2024-04-10 15:51:08 -05:00
steve-chavez 47a4e2bfd0 docs: add proxy and config reloading to arch 2024-04-10 13:57:49 -05:00
renovate[bot]andWolfgang Walther 5880e62d7a chore(deps): update codecov/codecov-action action to v4.3.0 2024-04-10 09:10:27 +02:00
steve-chavez 2772b78013 docs: add config/cli/developer to arch 2024-04-08 15:51:30 -05:00
steve-chavez b91908b749 docs: add architecture description 2024-04-08 13:50:00 -05:00
steve-chavez ab73624366 docs: add architecture diagram 2024-04-08 11:57:03 -05:00
steve-chavez 5ab317caa0 refactor: one entrypoint for Plan/Response/Query
- deduplicates timing calculation for the different steps
- enabling query logging later on will be simpler
2024-04-04 08:58:55 -05:00
renovate[bot]andWolfgang Walther a66738b892 chore(deps): update codecov/codecov-action action to v4.2.0 2024-04-04 07:17:26 +02:00
steve-chavez 3d55f77bae Revert "fix: slow responses on schema cache reload"
This reverts commit 727ef465c1.

Also documents requests waiting for the schema cache.
2024-04-02 23:15:09 -05:00
steve-chavez d7c64a93f8 docs: update schema cache 2024-04-01 19:03:14 -05:00
steve-chavez 2543b8d724 fix: clarify PGRST204 error message 2024-04-01 19:03:14 -05:00
Wolfgang Walther 6de3ba543d nix: Use minimal set of texlive dependencies for docs-render 2024-03-31 22:05:02 +02:00
steve-chavez 06ff56d323 refactor: move isolation/settings logic to Plan.hs 2024-03-28 18:31:42 -05:00
steve-chavez c33ca4e60c refactor: dry timings calculation for openapi 2024-03-28 18:31:42 -05:00
steve-chavez b75cc853b4 chore: fix compilation
The App.hs module was missing NamedFieldPuns.
2024-03-27 19:18:17 -05:00
steve-chavez 745e7868b0 refactor: dry some timings calculation 2024-03-27 18:22:34 -05:00
Laurence Isla 378c11104b test: fix some in-db config values
To correctly test in-db override of config file values, the former must be different from the latter.
2024-03-27 15:59:35 -05:00
Laurence Isla 428a6fef63 fix: in-db config values not loading for pgrst.server_trace_header and pgrst.server_cors_allowed_origins 2024-03-27 15:59:35 -05:00
renovate[bot]andWolfgang Walther d02540ac44 chore(deps): update codecov/codecov-action action to v4.1.1 2024-03-26 19:49:51 +01:00
Laurence IslaandGitHub 3f162535b8 nix: add example on how to use a library locally 2024-03-26 11:39:07 -05:00
steve-chavez a5bb20bbf8 refactor: remove unreacheable 404 2024-03-25 18:17:01 +01:00
renovate[bot]andWolfgang Walther f1f01f1f5c chore(deps): update actions/cache action to v4.0.2 2024-03-19 21:58:16 +01:00
Wolfgang Walther ee359192fa ci: Fix duplicated version number for actions/cache 2024-03-19 21:57:30 +01:00
steve-chavez 941ea0f929 test: notify do nothing 2024-03-19 13:22:51 +03:30
steve-chavez ee8b3ef8fe fix: log on LISTEN notification 2024-03-19 13:22:51 +03:30
Ian WijmaandGitHub 47b70b8329 chore: fix link to docs in readme
Fixed the link to the archived documentation repo.
2024-03-16 11:41:44 +01:00
steve-chavez 727ef465c1 fix: slow responses on schema cache reload 2024-03-15 11:53:29 -05:00
renovate[bot]andWolfgang Walther 210cded560 chore(deps): update nixbuild/nix-quick-install-action action to v27 2024-03-15 17:13:24 +01:00
steve-chavez 11d8da046c fix: incorrect /ready response on slow schema load 2024-03-15 11:03:01 -05:00
steve-chavez 4b289b1c97 test: requests wait for schema cache load
* nix: add postgrest-test-big-schema command
2024-03-15 06:18:26 -05:00
steve-chavez 92ac7e574e chore: clarify concurrent notifications test 2024-03-14 23:17:05 -05:00
steve-chavez ec7ab271d6 chore: add postgrest roles to big_schema.sql 2024-03-14 11:21:17 -05:00
steve-chavez 4a2c851ae5 chore: update cabal/stack new hasql-notifications 2024-03-13 20:43:46 -05:00
steve-chavez 86e15dbb77 fix: upgrade hasql-notifications to show error 2024-03-13 11:14:11 -05:00
Steve ChavezandGitHub 00f5780415 fix: don't hide error on LISTEN channel failure (#3323) 2024-03-11 19:56:35 -05:00
Steve ChavezandGitHub 650249ed29 perf: remove json_typeof (#3316) 2024-03-08 16:02:28 -05:00
steve-chavez 9405a62de8 docs: update links to cache/config notify reload 2024-03-08 10:31:55 -05:00
steve-chavez 05cdbb34c2 test: insignificant JSON whitespace on writes 2024-03-07 17:14:16 -05:00
steve-chavez 8eb88ef218 test: reduce threshold on memory tests 2024-03-07 16:01:21 -05:00
Laurence IslaandWolfgang Walther cf7b9bad08 nix: only json output for postgrest-dump-schema 2024-03-07 16:50:14 +01:00
Laurence IslaandWolfgang Walther c276e9741e nix: fix postgrest-dump-schema with optional yaml output 2024-03-07 16:50:14 +01:00
renovate[bot]andWolfgang Walther 6a2677d985 chore(deps): update ubuntu:focal docker digest to 80ef4a4 2024-03-07 16:27:56 +01:00
Steve ChavezandGitHub 58999f5102 changelog: add how to detect o2o rels on v10.0.0 (#3312) 2024-03-06 20:21:58 -05:00
Steve ChavezandGitHub 2f91853cb1 docs: add index usage section (#3299) 2024-03-02 11:00:31 -05:00
Laurence IslaandWolfgang Walther b8b957667b docs: add enums to working with data types how-to 2024-03-02 12:14:13 +01:00
renovate[bot]andWolfgang Walther db56c3eecc chore(deps): update actions/download-artifact action to v4.1.4 2024-03-02 12:10:10 +01:00
Wolfgang Walther d5cb6b57ea chore: Prevent unnecessary rebases for renovate PRs
This saves a few CI cycles.
2024-03-01 08:57:47 +01:00
Wolfgang Walther f72b47c485 chore: Add full semver comment to used actions 2024-03-01 08:54:27 +01:00
renovate[bot]andWolfgang Walther 0eda5df644 chore(deps): update actions/cache digest to ab5e6d0 2024-03-01 08:11:53 +01:00
Steve ChavezandGitHub d3f15baa4a nix: add postgrest-profiled-run (#3292) 2024-02-29 16:44:10 -05:00
Wolfgang WaltherandWolfgang Walther 29ffb9329b ci: Build on FreeBSD only when build workflow runs
This prevents running the freebsd build when only tests or docs change.
2024-02-29 21:27:05 +01:00
Wolfgang Walther a1480c758e ci: Run postgrest-test-doctests without nix-shell
Resolves #3183
2024-02-27 10:02:42 +01:00
Laurence IslaandGitHub 2479e0d4a5 changelog: clarify breaking change about dropping legacy gucs 2024-02-26 20:13:28 -05:00
renovate[bot]andWolfgang Walther 4878719b90 chore(deps): update actions/download-artifact digest to 87c5514 2024-02-26 22:13:53 +01:00
Wolfgang Walther 629ace0103 ci: Replace actions cache for macos build job with smart cachix lookup 2024-02-26 22:06:01 +01:00
Wolfgang Walther 73199127bb chore: Remove cabal.project.non-nix
We don't have any source-repository-package stanzas in there anymore, so there's no
need to keep it around anymore.
2024-02-26 22:06:01 +01:00
Wolfgang Walther a090f28cea ci: Install only the required nix tools
A previous commit allowed to select each tool separately on the toolbox.

This commit makes use of that for CI to possibly speed up loading from cachix
a little bit. It will also cause fewer cache misses when nix code is changed.

Resolves #3183
2024-02-26 22:06:01 +01:00
Wolfgang Walther c7eb4036a1 nix: Export all tools on each toolbox
This allows targeting each tool separately for installs.
2024-02-26 22:06:01 +01:00
Wolfgang Walther 08901323a3 nix: Move parallelCurl to devTools
This is not a with-tool, because it doesn't wrap another command.
2024-02-26 22:06:01 +01:00
Wolfgang Walther 1db6c04a28 ci: Remove unused .github/release script 2024-02-26 22:06:01 +01:00
Wolfgang Walther e70f001cfc ci: Remove matrix build for GHC 9.4.8 via Cabal on Linux
This is to reduce storage requirements for GitHub Actions cache. We already build with GHC 9.4.x
via Nix on Linux x64, via stack on FreeBSD, MacOS and Windows and via Cabal on Linux ARM. That
should cover 9.4.x enough.
2024-02-26 22:06:01 +01:00
Wolfgang Walther 3b497a5db4 ci: Remove useless cache- prefix for cache names
This just makes the name longer than needed.
2024-02-26 22:06:01 +01:00
renovate[bot]andWolfgang Walther 5bf0b9ca14 chore(deps): update codecov/codecov-action action to v4.1.0 2024-02-26 22:00:48 +01:00
Wolfgang Walther e5aee49891 ci: Prevent Upload Reports job from running on release branches
This won't work, since the workflows on the backbranches are structured differently
and thus the job can't find the loadtest.md file anywhere.
2024-02-24 23:33:25 +01:00
renovate[bot]andWolfgang Walther 873ac6221e chore(deps): pin dependencies 2024-02-24 21:15:38 +01:00
renovate[bot]andWolfgang Walther f97c6e3db3 chore(deps): update dependency urllib3 to v2.2.1 2024-02-24 20:38:43 +01:00
Wolfgang WaltherandWolfgang Walther d6153be67a ci: Split Lint & Style from Test workflow
The Lint & Style job needs to run on all PRs, not only when something "test" related
changes. Otherwise not all workflow, nix or other files are style-checked and linted.
2024-02-24 20:38:25 +01:00
renovate[bot]andWolfgang Walther e6cde92f11 chore(deps): update dependency sphinxext-opengraph to v0.9.1 2024-02-24 20:01:04 +01:00
renovate[bot]andWolfgang Walther f7879bd4b1 chore(deps): update dependency docutils to v0.20.1 2024-02-24 20:00:51 +01:00
Wolfgang WaltherandWolfgang Walther 11accb760a ci: Pass cachix token to all nix jobs 2024-02-24 19:59:46 +01:00
Wolfgang WaltherandWolfgang Walther 44e041282c 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:59:46 +01:00
renovate[bot]andWolfgang Walther f2483dc722 chore(deps): update codecov/codecov-action action to v4 2024-02-24 18:41:06 +01:00
renovate[bot]andWolfgang Walther c18ccf5232 chore: Switch from dependabot to renovate
This allows much better configuration and customization and will keep our
release branches up2date, too.
2024-02-24 18:10:30 +01:00
Wolfgang WaltherandWolfgang Walther 79e172a65c ci: Fix failing Upload Reports job 2024-02-24 17:27:01 +01:00
Wolfgang WaltherandWolfgang Walther b49e3b1e5b ci: Only run ARM-related jobs if related settings are present
This allows CI to properly run through in a fork, which doesn't have the SSH_ARM_xxx settings.
2024-02-24 17:27:01 +01:00
Wolfgang Walther 6caaee777b nix: Simplify static.nix
The enable-executable-static flag is set by default, so doesn't make a difference.

The pkg-config improvement for libpq was merged upstream, so we can use the same
here already.
2024-02-24 15:38:02 +01:00
Wolfgang Walther 86b2e59f6f chore: Add tested-with field to postgrest.cabal
This documents supported GHC versions. GHC 9.8.1 is currently commented
out to reflect the fact that PostgREST can't currently be built with it
straight from hackage - we still require some overrides in cabal.project
for that.
2024-02-24 14:07:55 +01:00
Wolfgang Walther 0738785d72 test: Improve dump-schema snapshot test formatting
Splitting the output into separate files and adding top-level newlines makes this
much better to read and understand when looking at diffs.

Inspired by #1699
2024-02-24 14:01:19 +01:00
Wolfgang WaltherandWolfgang Walther 2c2513b9cd deps: Use libpq v16 for freebsd build 2024-02-24 13:46:05 +01:00
Wolfgang WaltherandWolfgang Walther 9314dede2d ci: Build with GHC 9.8.2 2024-02-24 13:37:24 +01:00
Wolfgang Walther ccc386cf38 nix: Add deadnix to postgrest-lint and remove dead nix code 2024-02-24 13:08:04 +01:00
Wolfgang Walther caf0a70171 nix: Clarify pkg-config usage in static.nix a bit
This will make it easier to make the switch from pkgsCross.libpq to pkgsStatic.libpq,
once that is possible to build upstream - if ever.
2024-02-24 12:08:40 +01:00
Wolfgang Walther 22e8906c40 nix: Run faster tasks first in postgrest-lint
This avoid long waiting times when waiting for the result of shellcheck or actionlint.
2024-02-23 14:02:05 +01:00
Wolfgang WaltherandWolfgang Walther 0b3f6de015 ci: Merge docs-spellcheck and docs-dictcheck jobs
Both jobs run really quick, running them separate is just a waste of resources. Plus,
they are semantically closely related anyway.
2024-02-22 20:12:18 +01:00
Wolfgang WaltherandWolfgang Walther c59736b902 ci: Rename release jobs for consistency 2024-02-22 20:12:18 +01:00
Wolfgang WaltherandWolfgang Walther 19423b0c0a ci: Only run PR workflows when files have changed 2024-02-22 20:12:18 +01:00
Wolfgang WaltherandWolfgang Walther e9abb64fc0 ci: Split Build workflow from CI workflow 2024-02-22 20:12:18 +01:00
Wolfgang WaltherandWolfgang Walther 57ef98a316 ci: Split Test workflow from CI workflow 2024-02-22 20:12:18 +01:00
Wolfgang WaltherandWolfgang Walther 31a3864f85 ci: Remove files from arm server immediately on non-release push
While this duplicates code a little bit, it makes it much simpler to refactor later on.
2024-02-22 20:12:18 +01:00
Wolfgang WaltherandWolfgang Walther 073280ad32 ci: Require docs job to pass before making a release 2024-02-22 20:12:18 +01:00
Wolfgang WaltherandWolfgang Walther 333f8cf592 ci: Cancel all workflows consistently in pull requests without blocking main
The previous setup would cause multiple commits on main to be stuck in a pending
state, waiting for the previous run to be finished.

The new group specification is taken from:
https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value

This also adds the same settings for the docs workflow.
2024-02-22 20:12:18 +01:00
Wolfgang WaltherandWolfgang Walther f9fdf666ad ci: Use if without ${{ }} where possible 2024-02-22 20:12:18 +01:00
Wolfgang WaltherandWolfgang Walther c54e0d3353 ci: Use actions/download-artifact instead of dawidd6/action-download-artifact
The "default" action now supports downloading from different workflow runs, too, so
we might as well use it.
2024-02-22 20:12:18 +01:00
steve-chavez 3a3601cbeb feat: log schema cache load time 2024-02-21 18:16:37 -05:00
Wolfgang WaltherandWolfgang Walther 1a141c19df nix: Add postgrest-docs-render to render latex documents 2024-02-21 09:40:11 +01:00
Wolfgang WaltherandWolfgang Walther b8f35d880f docs: Fix broken redirects in api.rst 2024-02-21 09:40:11 +01:00
Wolfgang WaltherandWolfgang Walther 791b86cbf6 docs: Add note about not support Stored Procedures
Resolves https://github.com/PostgREST/postgrest-docs/issues/147
2024-02-21 09:40:11 +01:00
Wolfgang WaltherandWolfgang Walther 944b02fbb8 docs: Rename Stored Procedures to Functions consistently
This avoids confusing our RPCs with actual CREATE PROCEDURE, which we don't
support.

References https://github.com/PostgREST/postgrest-docs/issues/147
2024-02-21 09:40:11 +01:00
Wolfgang WaltherandWolfgang Walther 95b8751496 docs: Simplify SQL for user management how-tos
All those DROP IF EXISTS and CREATE IF NOT EXISTS etc. just give a lot more text to
read and understand. If in fact a user creates the same thing twice, they should be
able to understand the error message from postgres.
2024-02-21 09:40:11 +01:00
Wolfgang WaltherandWolfgang Walther 9b1ff2235a docs: Simplify auth examples with OUT parameter
The jwt_token type was not created consistently in all examples, which can
be confusing when following those. To return an object with a single key
named token, it's enough to have an OUT parameter to the function.

Resolves https://github.com/PostgREST/postgrest-docs/issues/280
2024-02-21 09:40:11 +01:00
Steve ChavezandGitHub 7c6c056e92 refactor: make observation messages pure (#3250)
removes the observation messages from the Logger
2024-02-20 18:42:38 -05:00
Laurence IslaandGitHub 229bc77886 docs: using or across embedded resources 2024-02-20 17:15:42 -05:00
Wolfgang WaltherandWolfgang Walther 8a21a9c34e fix: Dump media handlers and timezones with --dump-schema
Those were left out of the schema dump when the features were introduced, probably
because ByteString doesn't have a toJSON instance. Changing the type to Text solves
this easily.

Resolves #3237
2024-02-20 18:44:17 +01:00
Steve ChavezandGitHub 6d506df6f3 refactor: add observation module (#3232) 2024-02-20 12:29:33 -05:00
Taimoor ZaeemandGitHub 32e1900370 feat: dump schema cache through admin API (#3233) 2024-02-19 21:20:29 -05:00
Wolfgang WaltherandWolfgang Walther fac7acafa3 chore: Add docs/_build to .gitignore again to allow switching to old release branches easily 2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther 645217333b docs: Display version number more prominently
Resolves https://github.com/PostgREST/postgrest-docs/issues/734
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther fcd29caecf docs: Use code-block postgres consistently 2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther 553ac79d9b docs: Add example on how to automatically store mimetype in files table
Resolves https://github.com/PostgREST/postgrest-docs/issues/612
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther b2ad914435 docs: Improve introduction for datatypes how-to
Not all examples on this page are strictly using string representation, especially bytea.
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther 8ee4e9f0bb docs: Sort datatypes how-to alphabetically
The examples in this sections were in seemingly random order before.
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther 8f2b6b1652 docs: Add hint about schema reloading with db-config=true
Resolves https://github.com/PostgREST/postgrest-docs/issues/490
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther b1deb049c8 docs: Clarify behavior of jwt-aud and tokens without aud claim
Resolves https://github.com/PostgREST/postgrest-docs/issues/479
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther 51db5ce26f docs: Add example for ordering by json field
Resolves https://github.com/PostgREST/postgrest-docs/issues/364
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther e7dde2806c docs: Change tutorials to use identity column instead of serial/sequence
Resolves https://github.com/PostgREST/postgrest-docs/issues/311
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther 4325a4f9d5 docs: Add links to resource embedding from insert/update/delete sections
Resolves https://github.com/PostgREST/postgrest-docs/issues/277
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther 77715dcabf docs: Rename remaining instances of computed columns to computed fields
Resolves https://github.com/PostgREST/postgrest-docs/issues/368
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther aaaf0572ce docs: Remove deprecated auth0 features
As mentioned in https://github.com/PostgREST/postgrest/discussions/3088 rules and hooks are not
available to new tenants anymore, so the note is not helpful anymore.

Resolves #3088
Resolves https://github.com/PostgREST/postgrest-docs/issues/85
Resolves https://github.com/PostgREST/postgrest-docs/issues/715
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther 500aad3360 docs: Clarify secret wording in tutorial 1
Resolves https://github.com/PostgREST/postgrest-docs/issues/108
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther 3d95c41115 docs: Remove note about risk of asymmetric keys for JWT auth
The obviously wrong statement is, that PostgREST does not support asymmetric keys, while it
does. Extending on this type of attack is not necessary, because it is in fact covered by
the paragraph before - reading the algorithm from the JWT header is the problem in that case,
too. We don't do that.

This leaves us with the sentence about how the chosen library is the most important part. While
that is correct, the hint about high quality libraries for use on the *client* side is mis-
leading: The important part here is the library we choose to implement PostgREST with, not the
client-side lib. Thus, removing the whole paragraph is the best thing to do here.

Resolves https://github.com/PostgREST/postgrest-docs/issues/123
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther 6eaaa9aa9b docs: Remove note about possible memory leak on alpine
We don't have any justification to add this hint anywhere.

Resolves https://github.com/PostgREST/postgrest-docs/issues/399
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther f33bccbcdb docs: Use port 5432 in tutorial 0 to avoid connecting to the wrong server
Some users connect PostgREST to the wrong PostgreSQL instance - likely because they are
not even aware that another instance is running. By using the standard port 5432 instead
of 5433, we avoid this problem. The user will be made aware very early that they have
another postgresql instance running - and can solve the problem at this stage. If they
decide to change the port, they are much more likely to remember that in the later stages
of the tutorial, too.

Resolves https://github.com/PostgREST/postgrest-docs/issues/304
2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther 6c886b1291 docs: Update output in tutorials to latest version 2024-02-19 21:54:15 +01:00
Wolfgang WaltherandWolfgang Walther 0e7af7b77b docs: Various formatting improvements 2024-02-19 21:54:15 +01:00
Wolfgang Walther 4a796e0758 nix: Show connection hints for postgrest-with-postgresql-xx on stdout
This allows running this together with postgrest-run --dump-schema and
piping the result into jq without syntax errors.
2024-02-19 17:49:16 +01:00
Wolfgang Walther 8ff9f3292e test: Hide "cast on domain" warnings in spec tests
Also removes a few other unused settings which originate from running pg_dump.
2024-02-19 17:49:16 +01:00
Wolfgang WaltherandWolfgang Walther 5804b754e3 chore(deps): Remove useless pins in cabal.project.non-nix
Those were mistakenly added to support GHC 9.8.1, before I understood hackage revisions.
2024-02-19 17:47:37 +01:00
Wolfgang Walther 16bc853114 nix: Change default socket location for libpq back to /var/run/postgresql
Commit 85fbb233 accidentally changed the default socket location in which libpq is
looking for postgresql unix sockets. This is changed in nixpkgs via patch. By imp-
orting the default patches, this is changed back to what it was before. Without
those patches it was changed from /run/postgresql to /tmp.

Not a bugfix, because it was not released, yet.
2024-02-19 16:24:17 +01:00
Wolfgang Walther 272c1cc1e2 nix: Fix docs-spellcheck with multiple references on a single line 2024-02-18 19:35:40 +01:00
Wolfgang Walther 72de95b069 docs: Fix spelling mistakes 2024-02-18 19:35:40 +01:00
Wolfgang Walther c196d406d4 nix: Make postgrest-watch postgrest-docs-check work
This moves the _build folder into the repo root, to avoid postgrest-watch ending in an
infinite loop of restarting the build.

Also, for repeated use during development, running linkcheck is not a good idea, this
will quickly result in rate-limiting requests from various servers.
2024-02-18 16:12:20 +01:00
Wolfgang WaltherandWolfgang Walther b435f1b2d4 nix: Move docs build and serve scripts into nix
This is long overdue. We expect everyone contributing to postgrest to use the nix tools,
so it makes no sense to carry around external tools anymore.
2024-02-18 13:10:00 +01:00
Wolfgang WaltherandWolfgang Walther 3750f35e3f chore: Deduplicate static files between core and docs 2024-02-18 13:10:00 +01:00
Wolfgang WaltherandWolfgang Walther c2a333efc4 chore: Bump version docs to 12.1-dev
See c004840e.
2024-02-18 13:10:00 +01:00
Laurence IslaandWolfgang Walther a1f2ecadda nix: Move docs tools into core infrastructure 2024-02-18 13:10:00 +01:00
Wolfgang WaltherandWolfgang Walther e110fdbd2c nix: Refactor checkedShellScript's inRootDir to workingDir
This allows more flexible control over the working directory. Values for workingDir must always start
with a / and will then be relative to the repo root.
2024-02-18 13:10:00 +01:00
Wolfgang Walther 75a86ba873 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:43:03 +01:00
Wolfgang Walther b2dda6a5d3 ci: Refactor conditions to check for branch events 2024-02-17 17:38:39 +01:00
Wolfgang Walther 9a145f6931 Merge branch 'docs/main' into main 2024-02-17 15:14:44 +01:00
Wolfgang Walther 311a38193b chore: Run postgrest-style on docs files
This prevents CI from failing after the merge.
2024-02-17 13:43:50 +01:00
Wolfgang Walther 4954859d68 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:50 +01:00
Wolfgang Walther a7afcc4de9 chore: Remove accidentally committed submodule
This was accidentally added in b22bb74.
2024-02-17 13:13:00 +01:00
Wolfgang Walther 1b855d805e docs: Replace some permanent redirections with their target
And sort the ecosystemi's _devops list again.
2024-02-16 22:36:57 +01:00
Laurence IslaandWolfgang Walther 4059e1ea76 Add tutorial for Godot 4 + PostgREST in ecosystem 2024-02-16 22:14:05 +01:00
Laurence IslaandWolfgang Walther 93e50b1d90 docs: Enable non-broken link again 2024-02-16 22:11:00 +01:00
Wolfgang WaltherandWolfgang Walther 2ae96e7733 docs: Fix broken stackoverflow link 2024-02-16 22:04:07 +01:00
Wolfgang WaltherandWolfgang Walther 9167ae9462 chore: Remove leftover .test file 2024-02-16 22:04:07 +01:00
Wolfgang WaltherandWolfgang Walther 3664343989 chore: Add build tools for translations
Signed-off-by: Wolfgang Walther <walther@technowledgy.de>
2024-02-16 22:04:07 +01:00
Wolfgang WaltherandWolfgang Walther 18d105f8c2 nix: Update nixpkgs to same version as core repo 2024-02-16 22:04:07 +01:00
Wolfgang Walther c004840e76 Bump to v12.1
Going forward, an uneven minor version will be a development version, while an even minor will be
considered a stable version to be released. This is similar to what GHC does and was discussed in
#3113.

This bump should have happened after branching off v12.0.0, but obviously we didn't know about it
back then. This will happen immediately after branching off a new release from now on.
2024-02-16 16:38:32 +01:00
Wolfgang Walther 00fbe9ff3e 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-02-15 19:01:12 +01:00
Wolfgang Walther 2466f4e738 fix: Return 406 instead of 415 for non-acceptable media type
415 is for Content-Type and 406 for Accept headers.
2024-02-15 19:01:12 +01:00
Andrei DziahelandGitHub 3432f75ed4 fix: fixes server timings' precision (#3227)
* test: fix test_io accordingly
2024-02-14 12:26:34 -05:00
Wolfgang Walther b22bb748f2 nix: Remove left-over commented out code in postgrest-test-doctests
This seems to be commented out for a while already, but nobody reported
problems, so far.
2024-02-11 11:38:53 +01:00
steve-chavez 21e8ca5051 feat: log full pg version to stderr on connection 2024-02-10 19:21:55 -05:00
Wolfgang WaltherandWolfgang Walther 410fa9508b nix: Make postgrest-with-postgresql-xxx postgrest-test-io work better
The upside is that postgrest-with-postgresql-xxx postgrest-test-io works as expected
now. The downside is, that postgrest-with-postgresql-xxx psql now starts without
any schema. This now needs an explicit postgrest-with-postgresql-xxx -f path/to.sql
to do anything useful.

Resolves #2864
2024-02-10 20:51:55 +01:00
Wolfgang Walther dcfdd8dfc9 ci: Only try to download loadtest artifact after conclusion of CI workflow
Removing the condition when migrating the loadtest workflow was not helpufl, this
triggers the report job a few times per pipeline. The goal was to always download
the report, even when the overall pipeline fails because of some other jobs.
Explicitly checking for both success and failure should be enough.
2024-02-10 20:42:06 +01:00
Wolfgang Walther ca777315b0 ci: Consistently put two blank lines between jobs for better visual separation 2024-02-10 20:16:22 +01:00
Wolfgang Walther f513a71c7a ci: Use latest stable nix version in CI
This was pinned to 2.13.6 in https://github.com/PostgREST/postgrest/pull/2692#issuecomment-1448938894
because of a regression in nix 2.14. Latest is 2.16.x now, so maybe this bug is already fixed.
2024-02-10 20:16:22 +01:00
Wolfgang Walther 09c5cbece5 ci: Remove workaround for ghcup on github actions runner images
This was introduced in https://github.com/PostgREST/postgrest/pull/2655.
2024-02-10 20:16:22 +01:00
Wolfgang Walther 073320133d ci: Remove matrix configuration for Build-Cabal-Arm job
This avoids the "${{ matrix.ghc }}" display when the job is skipped in PR workflows.
2024-02-10 20:16:22 +01:00
Wolfgang Walther f17f23bd9f ci: Make stack build fail when lock file is out of date
When updating stack.yaml, we need to make sure to update stack.yaml.lock, too.

This check prevents them from getting out of sync by failing CI in this case.

This improves cachability.
2024-02-10 20:16:22 +01:00
Wolfgang Walther fd1efa4635 ci: Merge CI and Loadtest workflows
The reason why those workflows were split in the first place was just to obtain loadtest
results quicker, because the in the separated workflow, only the single loadtest job
needs to finish before the artifacts can be downloaded.

However, the disadvantage of this approach was, that the results were not as easily
accessible as they could be in a single workflow. Additionally, it's possible to depend
on the "prepopulate nix" job for efficiency if the loadtest runs in the main workflow.
2024-02-10 20:15:35 +01:00
Wolfgang Walther db16683ba2 ci: Simplify loadtest workflow
The two different PR and Merge jobs were introduced to be able to test the main branch
against the latest release. However, this is now included in the PR job, too, so no need
for the two separate jobs anymore.
2024-02-10 20:15:09 +01:00
Wolfgang Walther 8a3b0c60ad 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-10 12:35:56 +01:00
steve-chavez 5424be76d9 feat: log schema cache stats to stderr
adjust the memory tests
2024-02-09 16:18:04 -05:00
Wolfgang Walther 066fa8b6aa ci: Improve cache keys and their restore prefixes
The cache key for nix now depends on default.nix and shell.nix in the root folder and all
.patch files in the nix folder. Those may change the output of our nix derivations, so
must be included. At the same time, there is no reason to include the actions/setup-nix
folder. This would only lead to new caches being created every time we update one of the
dependent actions in this file. Finally, we never restore caches with a different id any-
more. There is no point in having the style job fall back to the static cache for example.

The cache keys for cabal can be more explicit: We only have one postgrest.cabal and one
relevant cabal.project file. We were missing the cabal.project.freeze file, though, which
affects the dependencies used, too.
2024-02-09 18:43:53 +01:00
Taimoor ZaeemandSteve Chavez f9ee1f7e73 feat: apply all function settings as transaction-scoped settings 2024-02-09 11:37:01 -05:00
Laurence IslaandGitHub 4a0b93c451 Fix technical_specs in sample DB using UNIQUE instead of PRIMARY KEY 2024-02-08 14:10:17 -05:00
Wolfgang Walther 49584728b6 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-06 18:27:20 +01:00
Wolfgang Walther 868913d179 ci: Replace seed cachix workflow with new daemon mode
cachix-action v14 added a new daemon mode, which pushes new derivations to the store
as soon as they have been built. This replaces the seed cachix workflow nicely by just
pushing from all jobs directly.
2024-02-06 10:20:02 +01:00
Wolfgang Walther 74d8ddec36 nix: Make postgrest-push-cachix more efficient by passing each derivation only once 2024-02-06 10:20:02 +01:00
Wolfgang WaltherandWolfgang Walther ea02729ebd nix: Use -split-sections with pkgsStatic to make static executable smaller again
This works around https://github.com/NixOS/nixpkgs/issues/286285 to use -split-sections
in a cross-compiling scenario. This will reduce the size of the static executable and also
remove the remaining references to /nix/store/.. reducing closure size dramatically.

This also fixes the docker image blowing up in size since we switched to pkgsStatic.
2024-02-06 10:19:31 +01:00
Wolfgang WaltherandWolfgang Walther 9fe0f50709 ci: Add freebsd executable to releases
This was temporarily disabled, because of timeouts in Cirrus. This seems to work well again.
2024-02-06 10:19:31 +01:00
Wolfgang WaltherandWolfgang Walther 1c02e1e3a0 ci: Prevent creating artifact for dynamic ubuntu build
This is not used in the release process anyway, because we are using the static build.
2024-02-06 10:19:31 +01:00
Wolfgang WaltherandWolfgang Walther 7d096e5de5 ci: Update stack.yaml.lock 2024-02-06 10:19:31 +01:00
Wolfgang WaltherandWolfgang Walther 0eca2954b3 ci: Reduce file size of cabal and stack based builds
By passing -split-sections to all dependencies, GHC will link only the
modules we actually use and not the full package for each dependency.
This does neither work on MacOS nor Windows, thus we don't do it for
stack right now.

Stripping unused symbols in CI will further decrease the size of those files.
2024-02-06 10:19:31 +01:00
Wolfgang WaltherandWolfgang Walther cc9b725005 nix: Remove libkrb5 references from static executable 2024-02-06 10:19:31 +01:00
Wolfgang WaltherandWolfgang Walther 4a1b9a4609 nix: Remove libpq reference from static executable 2024-02-06 10:19:31 +01:00
Wolfgang WaltherandWolfgang Walther f51090f3d1 nix: Check static executable for /nix/store references
This makes the static build fail in case any references to the nix store
are left over. Those will increase the closure size of the nix derivation
massively and lead to a huge docker image.

At the same time, those references will not be functional on non-nix systems,
to which the static executable is distributed, anyway.
2024-02-06 10:19:31 +01:00
Ivan KasatenkoandGitHub 71887e4b78 feat: dump config through Admin API 2024-02-05 19:16:05 -05:00
Andrei DziahelandWolfgang Walther a45058ba02 ci: reuse more previous caches (Nix & Stack) 2024-02-05 21:51:32 +01:00
Wolfgang Walther 310d04065e test: Make failing loadtest fixtures pass again
PR #2358 added a bulk insert to the loadtest. However this broke the regular insert test,
which just returned 400 Bad Request because of a missing PK value since. Adding the new
id column in the payload to the ?columns= argument fixes that.
2024-02-05 21:40:21 +01:00
Wolfgang Walther 63ff6d1f15 nix: Fix postgrest-loadtest after vegeta update
The recent nixpkgs update gave us a new version of vegeta. This version includes a new
DNS cache features - which unfortunately doesn't play well with unix sockets. Disabling
the DNS cache makes requests succeed again.
2024-02-05 21:38:23 +01:00
steve-chavez 45cabacdcc fix: wrong subquery error returning as 400 status 2024-02-02 22:08:56 -05:00
dependabot[bot]andGitHub bbc0bda6f5 build(deps): bump LouisBrunner/checks-action from 1.6.2 to 2.0.0 (#3195) 2024-01-29 09:35:22 -05:00
dependabot[bot]andGitHub c1b46ffa1b build(deps): bump codecov/codecov-action from 3.1.4 to 3.1.5 (#3196) 2024-01-29 09:34:33 -05:00
Laurence IslaandGitHub 439db880dd Simplify htmx how-to functions using Pico CSS and Ionicons 2024-01-26 20:28:58 -05:00
Wolfgang WaltherandWolfgang Walther 43f552dbab ci: Improve caching for static and dynamic postgrest nix packages 2024-01-26 23:24:12 +01:00
Wolfgang WaltherandWolfgang Walther c43696c9a6 nix: Move postgrest-check-static inside derivation of static package
This will make the static build fail if we're not producing a static binary.
2024-01-26 23:24:12 +01:00
Andrei DziahelandWolfgang Walther 411cf430ad ci: revert cache-nix-action to v4
Upgrading c-n-action seems to break restoring Nix store rendering the cache useless (nix-community/cache-nix-action#27). This rolls the change back.
2024-01-26 18:40:45 +01:00
Wolfgang Walther 071a3d436f ci: Fix Seed-Cachix jobs on linux 2024-01-26 18:10:38 +01:00
Wolfgang WaltherandWolfgang Walther c94aa9ccd9 fix: Build static postgrest with GSSAPI support
Resolves #2815
2024-01-26 17:17:05 +01:00
Wolfgang WaltherandWolfgang Walther dcb6c5bb13 nix: Bump libpq version to link against v16 2024-01-26 17:17:05 +01:00
Wolfgang WaltherandWolfgang Walther 85fbb233a7 nix: Build only libpq instead of full postgresql package 2024-01-26 17:17:05 +01:00
Wolfgang WaltherandWolfgang Walther 259d97acee nix: Build static postgrest executable via pkgsStatic
This replaces the build via static-haskell-nix and will hopefully make
it possible to cross compile in the future.
2024-01-26 17:17:05 +01:00
Wolfgang WaltherandWolfgang Walther 3a639c7172 ci: Fix FreeBSD build on cirrus 2024-01-25 21:27:17 +01:00
Wolfgang WaltherandWolfgang Walther d75be243ac ci: Build with GHC 9.8.1 via Cabal 2024-01-25 21:27:17 +01:00
Wolfgang WaltherandWolfgang Walther 2ffb97991d ci: Build with GHC 9.6.4 via Cabal
This confirms that building with GHC 9.6.x works already, even if we can't
switch to it for the nix and stack builds, yet.
2024-01-25 21:27:17 +01:00
Wolfgang WaltherandWolfgang Walther 2e298824d6 deps: Replace postgresql-libpq fork with upstream v0.10
Our changes to reduce memory usage have been merged and released
upstream, so no need for the fork anymore.
2024-01-25 21:27:17 +01:00
steve-chavez 76b1c00935 docs: fix link to op modifiers 2024-01-22 23:49:59 -05:00
Andrei DziahelandWolfgang Walther d7246b4851 ci: nix-store --realise to tools:
Since we're building only tools now, we can just specify them directly
in the `tools:` parameter of `setup-nix` action.
2024-01-22 18:17:38 +01:00
Andrei DziahelandWolfgang Walther 286103daf2 ci: do not rebuild postgrest
specify outputs to cache excluding postgrest
2024-01-22 18:17:38 +01:00
Andrei DziahelandSteve Chavez 9c78751871 ci: fix c-n-a input names 2024-01-22 11:28:41 -05:00
dependabot[bot]andSteve Chavez 820917bcb6 build(deps): bump nix-community/cache-nix-action
Bumps [nix-community/cache-nix-action](https://github.com/nix-community/cache-nix-action) from 4.0.3 to 5.0.1.
- [Release notes](https://github.com/nix-community/cache-nix-action/releases)
- [Changelog](https://github.com/nix-community/cache-nix-action/blob/main/RELEASES.md)
- [Commits](https://github.com/nix-community/cache-nix-action/compare/v4.0.3...v5.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 11:28:41 -05:00
dependabot[bot]andGitHub 02312804f0 build(deps): bump actions/cache from 3 to 4 (#3179) 2024-01-22 09:13:30 -05:00
Steve ChavezandGitHub 7a387c7a30 nix: add parallel-curl wrapper (#3172)
stock curl doesn't make it easy to make many parallel requests to a
single endpoint. The endpoint has to be repeated N times.

```
curl --parallel https://example.com https://example.com ..
```

This provides a wrapper for parallel curl. It's useful for testing
scenarios like pool timeouts.

```
parallel-curl N https://example.com
```
2024-01-18 20:03:01 -05:00
Wolfgang WaltherandWolfgang Walther 6682baebd2 nix: Update nixpkgs and build with GHC 9.4.8 2024-01-18 18:54:33 +01:00
Wolfgang WaltherandWolfgang Walther e4c60889ee nix: Make postgrest-nixpkgs-upgrade take the latest stable version
Previously this command upgraded to the latest unstable version of nixpkgs,
but this was often broken. Taking the latest stable branch should give
better results.
2024-01-18 18:54:33 +01:00
Steve ChavezandGitHub c070cb9502 Revert "build(deps): bump nix-community/cache-nix-action from 4.0.3 to 5.0.1 in /.github/actions/setup-nix" (#3167) 2024-01-17 17:55:38 -05:00
dependabot[bot]andGitHub a8165a89a0 build(deps): bump nix-community/cache-nix-action (#3135) 2024-01-17 17:25:10 -05:00
Laurence IslaandGitHub 9ef0e677e0 Use -g to prevent globbing in some curl examples 2024-01-17 12:39:42 -05:00
Laurence IslaandGitHub cb5d80aff8 Remove HTTP Snippets 2024-01-17 09:07:37 -05:00
Laurence IslaandGitHub be2778edb5 Replace the term 'password' with 'secret' to clarify the Tutorial 1 2024-01-17 09:06:20 -05:00
ParashoeandLaurence Isla 0e7d507e5e Spelling correction (#740)
Spell correct "callounter" to "callcounter"
2024-01-15 20:22:42 -05:00
Laurence IslaandGitHub 74a3217bee Update sponsor logo 2024-01-15 20:17:55 -05:00
Laurence IslaandGitHub e063a29212 Fix Sphinx version error in RTD (#739) 2024-01-15 20:09:54 -05:00
Laurence IslaandGitHub f5fca59b2e chore: update sponsor logo (#3161) 2024-01-15 20:09:42 -05:00
dependabot[bot]andGitHub 740fcdf5ab build(deps): bump cachix/cachix-action in /.github/actions/setup-nix (#3159) 2024-01-15 14:54:44 -05:00
dependabot[bot]andGitHub f864437055 Bump cachix/install-nix-action from 24 to 25 (#737) 2024-01-15 14:38:23 -05:00
steve-chavez 4bb98225d8 docs: remove Heroku integration 2024-01-10 17:23:13 -05:00
steve-chavez fdaeea8fd2 fix: misleading "Starting.." logs on scache reload 2024-01-09 20:37:25 -05:00
steve-chavez 6c3d7a946d deprecate: params=single-object preference 2024-01-04 17:00:38 -05:00
steve-chavez c29d876f90 docs: add params preference 2024-01-04 16:09:07 -05:00
steve-chavez 1e4efafd9e docs: add tx preference 2024-01-04 16:09:07 -05:00
steve-chavez 50ac91d1ae docs: testing of media handlers as a note
Otherwise users think they're required steps. See the opening comment on
https://github.com/PostgREST/postgrest/issues/3124.
2024-01-02 23:44:53 -05:00
Taimoor ZaeemandSteve Chavez f73845159c add documentation for max-affected preference 2023-12-30 23:07:45 -05:00
Laurence IslaandGitHub 0ce37f8fd6 Update sponsor image (#728) 2023-12-20 19:16:48 -05:00
Laurence IslaandGitHub b2095a8ef6 chore: update sponsor image 2023-12-20 19:16:31 -05:00
Laurence Isla d99e3ac805 changelog: update to 12.0.2 2023-12-20 18:26:05 -05:00
steve-chavez 3af63cb94c fix: empty row on handler function
Closes https://github.com/PostgREST/postgrest/issues/3126
2023-12-20 16:18:09 -05:00
steve-chavez 1680a6eed3 fix: aggregates not working for all schemas
Closes https://github.com/PostgREST/postgrest/issues/3124
2023-12-20 15:20:18 -05:00
Laurence IslaandSteve Chavez c97bd1f01b Add warning and fix to htmx how-to 2023-12-19 13:30:06 -05:00
steve-chavez 9e04bd7da6 docs: soap how-to is outdated
`*/*` can now be handled with media type handlers
2023-12-18 23:58:17 -05:00
Taimoor ZaeemandGitHub 0b77098460 feat: add max-affected preference to prefer header (#3083) 2023-12-18 18:35:23 -05:00
Laurence IslaandGitHub 3ec6526467 Add new sponsor 2023-12-18 12:14:52 -05:00
Laurence IslaandGitHub 4a1dee453b chore: add new sponsor 2023-12-18 12:14:01 -05:00
dependabot[bot]andGitHub 93bdae962e build(deps): bump actions/upload-artifact from 3 to 4 (#3123) 2023-12-18 09:09:18 -05:00
dependabot[bot]andGitHub 6a34018cfc build(deps): bump dawidd6/action-download-artifact from 2 to 3 (#3122) 2023-12-18 09:09:10 -05:00
dependabot[bot]andGitHub 2aa5373a27 build(deps): bump actions/download-artifact from 3 to 4 (#3121) 2023-12-18 09:09:02 -05:00
Laurence Isla f95a8e77a6 changelog: update to 12.0.1 2023-12-14 12:10:08 -05:00
steve-chavez a59cfafe10 fix: correct any media type handler 2023-12-12 17:45:14 -05:00
steve-chavez 6b9fde59cd fix: any handler sets a default application/json
Now it sets application/octet-stream as the generic type.
2023-12-12 17:44:07 -05:00
Laurence IslaandSteve Chavez 1e65a72264 Add missing in-db configuration for jwt-cache-max-lifetime 2023-12-12 16:36:24 -05:00
Laurence IslaandGitHub 4fb521cac6 fix: add jwt_cache_max_lifetime as an in-database configuration option (#3102) 2023-12-12 16:29:50 -05:00
steve-chavez cf258ef499 fix: add missing pgrst.server_timing_enabled 2023-12-12 10:58:22 -05:00
Laurence IslaandGitHub 0938e72c5c Add new sponsor (#723) 2023-12-12 10:39:05 -05:00
Laurence IslaandSteve Chavez e31363a325 chore: add new sponsor 2023-12-12 10:38:09 -05:00
Laurence IslaandGitHub 5bd7cda308 Priorize query parameters instead of headers in limits and pagination 2023-12-07 18:47:56 -05:00
Andrei DziahelandGitHub cbfff2804d fix: replace json parser error with generic msg (#3090) 2023-12-07 18:04:51 -05:00
Andrei DziahelandSteve Chavez 41ea4e6db6 ci: cancel in-progress loadtests for PRs as well 2023-12-07 16:01:32 -05:00
Andrei DziahelandSteve Chavez f9294c5e43 ci: test only head of branch 2023-12-07 16:01:32 -05:00
Laurence IslaandGitHub d24a3c82ef Add meta tags using an Open Graph extension 2023-12-07 15:52:57 -05:00
Laurence IslaandGitHub c6b71551f4 changelog: move missplaced unreleased fix (#3094) 2023-12-07 11:00:46 -05:00
Laurence IslaandGitHub d49e3d7132 fix: allow using special characters in json keys (#3081)
* increase memory size test
2023-12-06 17:28:51 -05:00
Laurence IslaandSteve Chavez f80a33cf32 Add missing curl requests in Preferences section 2023-12-06 17:22:31 -05:00
steve-chavez 8fd4fa0d09 fix: any media type should be bytea 2023-12-05 17:48:16 -05:00
Andrei DziahelandGitHub 72083361bc 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-05 13:57:18 -05:00
Laurence IslaandGitHub 48dee75d1a Separate Admin into Observability and Health Check (#716) 2023-12-04 16:32:02 -05:00
dependabot[bot]andGitHub 92573215b9 Bump cachix/install-nix-action from 23 to 24 (#714) 2023-12-04 16:31:07 -05:00
dependabot[bot]andGitHub 6277eb43f7 build(deps): bump cachix/cachix-action in /.github/actions/setup-nix (#3085) 2023-12-04 15:39:40 -05:00
steve-chavez b04a7f0080 fix server-timing header section
Also link it to jwt caching
2023-12-02 01:41:26 -05:00
steve-chavez 0c65bf494d changelog: update to 12.0.0 2023-12-01 19:37:11 -05:00
230 changed files with 8599 additions and 5660 deletions
+31 -5
View File
@@ -2,15 +2,41 @@ freebsd_instance:
image_family: freebsd-14-0
build_task:
# Don't change this name without adjusting .github/workflows/ci.yaml
# Don't change this name without adjusting .github/workflows/build.yaml
name: Build FreeBSD (Stack)
install_script: pkg install -y postgresql13-client hs-stack git
install_script: pkg install -y postgresql16-client hs-stack git
only_if: |
$CIRRUS_TAG != '' || $CIRRUS_BRANCH == 'main' || $CIRRUS_BRANCH =~ 'v*' ||
changesInclude(
'.github/workflows/build.yaml',
'.github/actions/artifact-from-cirrus/**',
'.cirrus.yml',
'postgrest.cabal',
'stack.yaml*',
'**.hs'
)
stack_cache:
folders: /.stack
fingerprint_script: cat postgrest.cabal stack.yaml.lock
reupload_on_changes: false
fingerprint_script:
- echo $CIRRUS_OS
- stack --version
- md5sum postgrest.cabal
- md5sum stack.yaml.lock
stack_work_cache:
folders: .stack-work
fingerprint_script:
- echo $CIRRUS_OS
- stack --version
- md5sum postgrest.cabal
- md5sum stack.yaml.lock
- find main src -type f -iname '*.hs' -exec md5sum "{}" +
build_script: |
stack build -j 1 --local-bin-path . --copy-bins --stack-yaml stack-21.7.yaml
strip postgrest
build_script: stack build -j 1 --local-bin-path . --copy-bins
bin_artifacts:
path: postgrest
+3 -1
View File
@@ -7,12 +7,14 @@ When submitting a new feature or fix:
+ `fix`, bug fixes
+ `feat`, new features added
+ `perf`, performance improvements
+ `docs`, updating the documentation
+ `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.)
+ `changelog`, updating the CHANGELOG
+ `chore`, maintenance (build process, updating sponsors, etc.)
+ Other prefixes may be used if necessary
- If there's a breaking change, add `BREAKING CHANGE` and an explanation to your commit message
-->
+35
View File
@@ -0,0 +1,35 @@
name: Cache on main
description: Stores caches on main and release branches only, but restores them on all branches.
inputs:
path:
description: Path(s) to cache
required: true
save-prs:
description: Whether to additionally store the cache in a pull request, too. Should only be used for very small caches.
type: boolean
prefix:
description: Cache key prefix to be used in both primary key and restore-keys.
required: true
suffix:
description: Cache key suffix to be used only in primary key.
required: true
runs:
using: composite
steps:
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
if: ${{ startsWith(github.ref, 'refs/heads/') || (inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
with:
path: ${{ inputs.path }}
key: ${{ runner.os }}-${{ inputs.prefix }}-${{ inputs.suffix }}
restore-keys: |
${{ runner.os }}-${{ inputs.prefix }}-
- uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
if: ${{ !startsWith(github.ref, 'refs/heads/') && !(inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
with:
path: ${{ inputs.path }}
key: ${{ runner.os }}-${{ inputs.prefix }}-${{ inputs.suffix }}
restore-keys: |
${{ runner.os }}-${{ inputs.prefix }}-
+1 -1
View File
@@ -16,7 +16,7 @@ runs:
nix_conf: |-
always-allow-substitutes = true
max-jobs = auto
- uses: cachix/cachix-action@18cf96c7c98e048e10a83abd92116114cd8504be # v14
- uses: cachix/cachix-action@ad2ddac53f961de1989924296a1f236fcfbaa4fc # v15
with:
name: postgrest
authToken: ${{ inputs.authToken }}
-11
View File
@@ -1,11 +0,0 @@
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
@@ -1,12 +0,0 @@
#!/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"
+25
View File
@@ -0,0 +1,25 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:best-practices"
],
"baseBranches": [
"main",
"/^v[0-9]+/"
],
"rebaseWhen": "conflicted",
"packageRules": [
{
"matchBaseBranches": [ "/^v[0-9]+/" ],
"groupName": "all dependencies"
},
{
"matchPackageNames": ["docutils"],
"allowedVersions": "<0.21.0"
},
{
"matchPackageNames": ["macos"],
"allowedVersions": "<13"
}
]
}
+4 -1
View File
@@ -58,8 +58,11 @@ cd postgrest
git checkout $PGRST_GITHUB_COMMIT
cabal v2-update && cabal v2-build
# Copy the built binary to the Dockerfile directory
# Strip unused symbols from executable
PGRST_BIN=$(cabal exec which postgrest | tail -1)
strip $PGRST_BIN
# Copy the built binary to the Dockerfile directory
cp $PGRST_BIN ~/$DOCKER_BUILD_DIR
# Move and compress the built binary
+203
View File
@@ -0,0 +1,203 @@
name: Build
on:
workflow_call:
secrets:
CACHIX_AUTH_TOKEN:
required: false
pull_request:
branches:
- main
- v[0-9]+
paths:
- .github/workflows/build.yaml
- .github/actions/**
- .github/scripts/**
- .github/*
- '*.nix'
- nix/**
- .cirrus.yml
- cabal.project*
- postgrest.cabal
- stack.yaml*
- '**.hs'
- '!**.md'
concurrency:
# Terminate all previous runs of the same workflow for pull requests
group: build-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
static:
name: Nix - Linux static
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Build static executable
run: nix-build -A postgrestStatic
- 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
macos:
name: Nix - MacOS
runs-on: macos-12
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Install gnu sed
run: brew install gnu-sed
- name: Build everything
run: |
# The --dry-run will give us a list of derivations to download from cachix and
# derivations to build. We only take those that would have to be built and then build
# those explicitly. This has the advantage that pure verification will not include
# a download anymore, making it much faster. If something needs to be built, only
# the dependencies required to do so will be downloaded, but not everything.
nix-build --dry-run 2>&1 \
| gsed -e '1,/derivations will be built:$/d' -e '/paths will be fetched/Q' \
| xargs nix-build
stack:
strategy:
fail-fast: false
matrix:
include:
- name: Linux
runs-on: ubuntu-22.04
cache: |
~/.stack/pantry
~/.stack/snapshots
~/.stack/stack.sqlite3
# no artifact for Linux, because we use the static build
- name: MacOS
runs-on: macos-12
cache: |
~/.stack/pantry
~/.stack/snapshots
~/.stack/stack.sqlite3
artifact: postgrest-macos-x64
- name: Windows
runs-on: windows-2022
cache: |
~\AppData\Roaming\stack\pantry
~\AppData\Local\Programs\stack\pantry
~\AppData\Roaming\stack\snapshots
~\AppData\Local\Programs\stack\snapshots
~\AppData\Roaming\stack\stack.sqlite3
~\AppData\Local\Programs\stack\stack.sqlite3
deps: Add-Content $env:GITHUB_PATH $env:PGBIN
artifact: postgrest-windows-x64
name: Stack - ${{ matrix.name }}
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- uses: haskell-actions/setup@33585e1a16afa5875e124b0ebc89dd0c2f872c21 # v2.7.3
with:
# This must match the version in stack.yaml's resolver
ghc-version: 9.6.5
enable-stack: true
stack-no-global: true
stack-setup-ghc: true
- name: Cache ~/.stack
uses: ./.github/actions/cache-on-main
with:
path: ${{ matrix.cache }}
prefix: stack
suffix: ${{ hashFiles('postgrest.cabal', 'stack.yaml.lock') }}
- name: Cache .stack-work
uses: ./.github/actions/cache-on-main
with:
path: .stack-work
save-prs: true
prefix: stack-work-${{ hashFiles('postgrest.cabal', 'stack.yaml.lock') }}
suffix: ${{ hashFiles('main/**/*.hs', 'src/**/*.hs') }}
- name: Install dependencies
if: matrix.deps
run: ${{ matrix.deps }}
- name: Build with Stack
run: stack build --lock-file error-on-write --local-bin-path result --copy-bins
- name: Strip Executable
run: strip result/postgrest*
- name: Save built executable as artifact
if: matrix.artifact
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: ${{ matrix.artifact }}
path: |
result/postgrest
result/postgrest.exe
if-no-files-found: error
freebsd:
name: Stack - FreeBSD from CirrusCI
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- uses: ./.github/actions/artifact-from-cirrus
with:
token: ${{ github.token }}
task: Build FreeBSD (Stack)
download: bin
upload: postgrest-freebsd-x64
cabal:
strategy:
matrix:
ghc: ['9.6.4', '9.8.2']
fail-fast: false
name: Cabal - Linux GHC ${{ matrix.ghc }}
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- uses: haskell-actions/setup@33585e1a16afa5875e124b0ebc89dd0c2f872c21 # v2.7.3
with:
ghc-version: ${{ matrix.ghc }}
- name: Cache .cabal
uses: ./.github/actions/cache-on-main
with:
path: |
~/.cabal/packages
~/.cabal/store
prefix: cabal-${{ matrix.ghc }}
suffix: ${{ hashFiles('postgrest.cabal', 'cabal.project', 'cabal.project.freeze') }}
- name: Cache dist-newstyle
uses: ./.github/actions/cache-on-main
with:
path: dist-newstyle
save-prs: true
prefix: cabal-${{ matrix.ghc }}-dist-newstyle-${{ hashFiles('postgrest.cabal', 'cabal.project', 'cabal.project.freeze') }}
suffix: ${{ hashFiles('**/*.hs') }}
- name: Install dependencies
run: cabal build --only-dependencies --enable-tests --enable-benchmarks
- name: Build
run: cabal build --enable-tests --enable-benchmarks all
-78
View File
@@ -1,78 +0,0 @@
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
+32
View File
@@ -0,0 +1,32 @@
name: Check
on:
workflow_call:
secrets:
CACHIX_AUTH_TOKEN:
required: false
pull_request:
branches:
- main
- v[0-9]+
concurrency:
# Terminate all previous runs of the same workflow for pull requests
group: style-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
lint-style:
name: Lint & Style
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
tools: style.lint.bin style.styleCheck.bin
- 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
+107 -299
View File
@@ -8,263 +8,58 @@ on:
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' }}"
# Terminate all previous runs of the same workflow for the same tag, but never
# cancel for the main / release branch pipelines.
group: ci-${{ (github.ref_type == 'tag' && github.ref) || github.run_id }}
# TODO: Enable this once https://github.com/orgs/community/discussions/13015 is solved
cancel-in-progress: false
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
check:
name: Check
if: startsWith(github.ref, 'refs/heads/')
uses: ./.github/workflows/check.yaml
secrets:
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
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
docs:
name: Docs
if: startsWith(github.ref, 'refs/heads/')
uses: ./.github/workflows/docs.yaml
secrets:
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
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:
name: Test
if: startsWith(github.ref, 'refs/heads/')
uses: ./.github/workflows/test.yaml
secrets:
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
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:
name: Build
uses: ./.github/workflows/build.yaml
secrets:
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
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' }}"
arm:
name: Build / Cabal - aarch64 GHC 9.4.8
if: vars.SSH_ARM_ENABLED
runs-on: ubuntu-22.04
outputs:
remotepath: ${{ steps.Remote-Dir.outputs.remotepath }}
env:
GITHUB_COMMIT: ${{ github.sha }}
GHC_VERSION: ${{ matrix.ghc }}
GHC_VERSION: '9.4.8'
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- id: Remote-Dir
name: Unique directory name for the remote build
run: echo "remotepath=postgrest-build-$(uuidgen)" >> "$GITHUB_OUTPUT"
@@ -311,23 +106,30 @@ jobs:
if-no-files-found: error
Tag-Release:
name: Tag Release
if: startsWith(github.ref, 'refs/heads/')
tag:
name: Release / Tag
concurrency:
# Never tag outdated commits on the main branch by skipping superseded commits
group: ci-tag-${{ (github.ref == 'refs/heads/main' && github.ref) || github.run_id }}
# TODO: Enable this once https://github.com/orgs/community/discussions/13015 is solved
cancel-in-progress: false
if: |
vars.RELEASE_ENABLED &&
startsWith(github.ref, 'refs/heads/') &&
needs.docs.result == 'success' &&
needs.test.result == 'success' &&
needs.build.result == 'success' &&
(needs.arm.result == 'skipped' || success())
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
- docs
- test
- build
- arm
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with:
ssh-key: ${{ secrets.POSTGREST_SSH_KEY }}
- name: Tag latest commit
@@ -347,21 +149,18 @@ jobs:
fi
Prepare-Release:
name: Prepare release
if: startsWith(github.ref, 'refs/tags/')
prepare:
name: Release / Prepare
if: |
startsWith(github.ref, 'refs/tags/') &&
needs.build.result == 'success' &&
(needs.arm.result == 'skipped' || success())
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
- build
- arm
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Check the version to be released
run: |
cabal_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
@@ -391,14 +190,16 @@ jobs:
if-no-files-found: error
Release-GitHub:
name: Release on GitHub
github:
name: Release / GitHub
permissions:
contents: write
runs-on: ubuntu-22.04
needs: Prepare-Release
needs:
- prepare
if: success() || needs.prepare.result == 'success'
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Download all artifacts
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
@@ -459,17 +260,20 @@ jobs:
fi
Release-Docker:
name: Release on Docker Hub
docker:
name: Release / Docker Hub
runs-on: ubuntu-22.04
needs:
- Prepare-Release
- prepare
if: |
vars.DOCKER_REPO && vars.DOCKER_USER &&
(success() || needs.prepare.result == 'success')
env:
DOCKER_REPO: ${{ vars.DOCKER_REPO }}
DOCKER_USER: ${{ vars.DOCKER_USER }}
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Download Docker image
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
@@ -490,36 +294,42 @@ jobs:
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
docker-description:
name: Release / Docker Hub Description
runs-on: ubuntu-22.04
if: |
vars.DOCKER_REPO && vars.DOCKER_USER &&
github.ref == 'refs/tags/devel'
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae # v4.0.0
with:
username: ${{ vars.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
repository: ${{ vars.DOCKER_REPO }}/postgrest
short-description: ${{ github.event.repository.description }}
readme-filepath: ./docker-hub-readme.md
docker-arm:
name: Release / Docker Hub Arm
runs-on: ubuntu-22.04
needs:
- Build-Cabal-Arm
- Release-Docker
- arm
- 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
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Publish images for ARM builds on Docker Hub
uses: appleboy/ssh-action@master
env:
REMOTE_DIR: ${{ needs.Build-Cabal-Arm.outputs.remotepath }}
REMOTE_DIR: ${{ needs.arm.outputs.remotepath }}
with:
host: ${{ secrets.SSH_ARM_HOST }}
username: ubuntu
@@ -529,20 +339,18 @@ jobs:
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
clean-arm:
name: Build / Cleanup
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'))
- arm
- docker-arm
if: ${{ always() && vars.SSH_ARM_ENABLED }}
runs-on: ubuntu-22.04
env:
REMOTE_DIR: ${{ needs.Build-Cabal-Arm.outputs.remotepath }}
REMOTE_DIR: ${{ needs.arm.outputs.remotepath }}
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Remove uploaded files from server
uses: appleboy/ssh-action@master
with:
+45 -28
View File
@@ -1,50 +1,67 @@
name: Docs
on:
push:
branches:
- main
- v[0-9]+
workflow_call:
secrets:
CACHIX_AUTH_TOKEN:
required: false
pull_request:
branches:
- main
- v[0-9]+
- main
- v[0-9]+
paths:
- .github/workflows/docs.yaml
- .github/actions/setup-nix/**
- default.nix
- nix/**
- docs/**
- '!**.md'
concurrency:
# Terminate all previous runs of the same workflow for pull requests
group: docs-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
name: Build docs
name: Build
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26
- run: nix-env -f docs/default.nix -iA build
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
tools: docs.build.bin
- run: postgrest-docs-build
- run: git diff --exit-code HEAD locales || echo "Please commit changes to the locales/ folder after running postgrest-docs-build."
spellcheck:
name: Run spellcheck
name: Spellcheck
runs-on: ubuntu-22.04
steps:
- 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
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
tools: docs.spellcheck.bin docs.dictcheck.bin
- name: Run spellcheck
run: postgrest-docs-spellcheck
- name: Run dictcheck
run: postgrest-docs-dictcheck
dictcheck:
name: Run dictcheck
runs-on: ubuntu-22.04
steps:
- 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
name: Linkcheck
if: github.base_ref == 'main'
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
- uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26
- run: nix-env -f docs/default.nix -iA linkcheck
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
tools: docs.linkcheck.bin
- run: postgrest-docs-linkcheck
-70
View File
@@ -1,70 +0,0 @@
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
+9 -3
View File
@@ -2,17 +2,23 @@ name: Upload Reports
on:
workflow_run:
workflows: ["Loadtest"]
workflows:
- CI
- Test
branches-ignore:
- devel
- v[0-9]+
- v[0-9]+.[0-9]+.[0-9]+
types:
- completed
jobs:
upload:
loadtest:
name: Loadtest
permissions:
checks: write
runs-on: ubuntu-22.04
if: ${{ github.event.workflow_run.conclusion == 'success' }}
if: github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure'
steps:
- name: Download from Artifacts
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
+138
View File
@@ -0,0 +1,138 @@
name: Test
on:
workflow_call:
secrets:
CACHIX_AUTH_TOKEN:
required: false
CODECOV_TOKEN:
required: false
pull_request:
branches:
- main
- v[0-9]+
paths:
- .github/workflows/test.yaml
- .github/workflows/report.yaml
- .github/actions/setup-nix/**
- default.nix
- nix/**
- .stylish-haskell.yaml
- cabal.project
- postgrest.cabal
- '**.hs'
- test/**
- '!**.md'
concurrency:
# Terminate all previous runs of the same workflow for pull requests
group: test-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
coverage:
name: Coverage
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@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
tools: tests.coverage.bin tests.testDoctests.bin tests.testSpecIdempotence.bin
- name: Run coverage (IO tests and Spec tests against PostgreSQL 15)
run: postgrest-coverage
- name: Upload coverage to codecov
uses: codecov/codecov-action@125fc84a9a348dbcf27191600683ec096ec9021c # v4.4.1
with:
files: ./coverage/codecov.json
token: ${{ secrets.CODECOV_TOKEN }}
- name: Run doctests
if: always()
run: postgrest-test-doctests
- name: Check the spec tests for idempotence
if: always()
run: postgrest-test-spec-idempotence
postgres:
strategy:
fail-fast: false
matrix:
pgVersion: ["9_6", 10, 11, 12, 13, 14, 15, 16]
name: PG ${{ matrix.pgVersion }}
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@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
tools: tests.testSpec.bin tests.testIO.bin tests.testBigSchema.bin withTools.postgresql-${{ matrix.pgVersion }}.bin
- 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 }} postgrest-test-io -vv
- name: Run IO tests on a big schema
if: always()
run: postgrest-with-postgresql-${{ matrix.pgVersion }} postgrest-test-big-schema -vv
memory:
name: Memory
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
tools: memory.test.bin
- name: Run memory tests
run: postgrest-test-memory
loadtest:
name: Loadtest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with:
fetch-depth: 0
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
tools: loadtest.loadtestAgainst.bin loadtest.report.bin
- 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
View File
-68
View File
@@ -1,68 +0,0 @@
# Architecture
This document describes the high-level architecture of PostgREST.
## Bird's Eye View
```haskell
postgrest :: Request -> Either Error SQLStatement -> Response
```
On the highest level, PostgREST processes an HTTP request, if it's accepted it builds a SQL statement for it, executes it, and produces a response.
## Code Map
This section talks briefly about various important modules.
The starting point of the program is `main/Main.hs`, which calls `src/PostgREST/CLI.hs` which then calls `src/PostgREST/App.hs`.
`App.hs` is then in charge of composing the different modules.
### ApiRequest.hs
PostgREST operates over two types of resources: database relations(tables or views) and database functions; providing different representations(depending on the media type)
for them.
This module is in charge of representing the operation over an `ApiRequest` type. It parses the URL querystring following PostgREST syntax, the request headers, and the request body
(if possible it avoids parsing the body and sends it directly to the db).
A request might be rejected at this level if it's invalid, e.g. providing an unknown media type to PostgREST or using an unknown HTTP method.
### Plan.hs
Using the Schema Cache, this module enables more complex functionality(like resource embedding) by enriching the ApiRequest. It generates Plan types(`ReadPlan`, `MutatePlan`)
that then will be used to generate a SQL statement.
A request might be rejected at this level if it's invalid, e.g. by doing resource embedding on a nonexistent resource.
An OPTIONS request doesn't require a plan to be generated.
### Query.hs
This module constructs single SQL statements that can be parametrized and prepared. Only at this stage a PostgreSQL connection from the pool is used.
A query might fail(and be rollbacked) at this level if it doesn't comply to certain conditions, e.g. by not returning a single row when a ``Accept: application/vnd.pgrst.object`` header is specified.
An OPTIONS request doesn't require a query to be executed.
### Response.hs
This module constructs the HTTP response body with the right headers.
It builds the OpenAPI response using the schema cache.
### Auth.hs
This module provides functions to deal with JWT authorization.
### SchemaCache.hs
This queries the PostgreSQL system catalogs and caches the metadata into a SchemaCache type,
### 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.
+4 -4
View File
@@ -9,7 +9,7 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
<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="296px" src="static/cybertec-new.png">
<img width="296px" src="static/cybertec.svg">
</a>
</td>
<td align="center" valign="middle">
@@ -32,7 +32,7 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
</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="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/supabase.png">
<img width="296px" src="static/supabase.png">
</a>
</td>
<td align="center" valign="middle">
@@ -75,12 +75,12 @@ 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="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/timescaledb.png">
<img width="222px" src="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">
<img max-width="222px" height="88" src="static/retool.png">
</a>
</td>
<td align="center" valign="middle">
+55
View File
@@ -5,6 +5,55 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased
## [12.2.0] - 2024-06-11
### Added
- #2887, Add Preference `max-affected` to limit affected resources - @taimoorzaeem
- #3171, Add an ability to dump config via admin API - @skywriter
- #3171, #3046, Log schema cache stats to stderr - @steve-chavez
- #3210, Dump schema cache through admin API - @taimoorzaeem
- #2676, Performance improvement on bulk json inserts, around 10% increase on requests per second by removing `json_typeof` from write queries - @steve-chavez
- #3435, Add log-level=debug, for development purposes - @steve-chavez
- #1526, Add `/metrics` endpoint on admin server - @steve-chavez
- Exposes connection pool metrics, schema cache metrics
- #3404, Show the failed MESSAGE or DETAIL in the `details` field of the `PGRST121` (could not parse RAISE 'PGRST') error - @laurenceisla
- #3404, Show extra information in the `PGRST121` (could not parse RAISE 'PGRST') error - @laurenceisla
+ Shows the failed MESSAGE or DETAIL in the `details` field
+ Shows the correct JSON format in the `hints` field
- #3340, Log when the LISTEN channel gets a notification - @steve-chavez
- #3184, Log full pg version to stderr on connection - @steve-chavez
- #3242. Add config `db-hoisted-tx-settings` to apply only hoisted function settings - @taimoorzaeem
- #3214, #3229 Log connection pool events on log-level=debug - @steve-chavez, @laurenceisla
### Fixed
- #3237, Dump media handlers and timezones with --dump-schema - @wolfgangwalther
- #3323, #3324, Don't hide error on LISTEN channel failure - @steve-chavez
- #3330, Incorrect admin server `/ready` response on slow schema cache loads - @steve-chavez
- #3345, Fix in-database configuration values not loading for `pgrst.server_trace_header` and `pgrst.server_cors_allowed_origins` - @laurenceisla
- #3404, Clarify the `PGRST121` (could not parse RAISE 'PGRST') error message - @laurenceisla
- #3267, Fix wrong `503 Service Unavailable` on pg error `53400` - @taimoorzaeem
- #2985, Fix not adding `application_name` on all connection strings - @steve-chavez
- #3424, Admin `/live` and `/ready` now differentiates a failure as 500 status - @steve-chavez
+ 503 status is still given when postgREST is in a recovering state
- #3478, Media Types are parsed case insensitively - @develop7
- #3533, #3536, Fix listener silently failing on read replica - @steve-chavez
+ If the LISTEN connection fails, it's retried with exponential backoff
- #3414, Force listener to connect to read-write instances using `target_session_attrs` - @steve-chavez
- #3255, Fix incorrect `413 Request Entity Too Large` on pg errors `54*` - @taimoorzaeem
- #3549, Remove verbosity from error logs starting with "An error occurred..." and replacing it with "Failed to..." - @laurenceisla
### Deprecated
- Support for PostgreSQL versions 9.6, 10 and 11 is deprecated. From this on version onwards, PostgREST will only support non-end-of-life PostgreSQL versions. See https://www.postgresql.org/support/versioning/.
- `Prefer: params=single-object` is deprecated. Use [a function with a single unnamed JSON parameter](https://postgrest.org/en/latest/references/api/functions.html#function-single-json) instead. - @steve-chavez
### Documentation
- #3289, Add dark mode. Can be toggled by a button in the bottom right corner. - @laurenceisla
- #3384, Add architecture diagram and documentation - @steve-chavez
## [12.0.3] - 2024-05-09
### Fixed
@@ -72,6 +121,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- 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
+ All PostgreSQL versions now use GUCs in JSON format for [Headers, Cookies and JWT claims](https://postgrest.org/en/v12/references/transactions.html#request-headers-cookies-and-jwt-claims).
## [11.2.2] - 2023-10-25
@@ -375,6 +425,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- #2312, Using `Prefer: return=representation` no longer returns a `Location` header - @laurenceisla
- #1984, For the cases where one to one relationships are detected, json objects will be returned instead of json arrays of length 1
+ If you wish to override this behavior, you can use computed relationships to return arrays again
+ You can get the newly detected one-to-one relationships by using the `--dump-schema` option and filtering with [jq](https://github.com/jqlang/jq).
```
./postgrest --dump-schema \
| jq '[.dbRelationships | .[] | .[1] | .[] | select(.relCardinality.tag == "O2O" and .relFTableIsView == false and .relTableIsView == false) | del(.relFTableIsView,.relTableIsView,.tag,.relIsSelf)]'
```
## [9.0.1] - 2022-06-03
+4 -4
View File
@@ -1,4 +1,4 @@
![Logo](static/bigger-logo.png "Logo")
![Logo](static/postgrest.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)
@@ -20,7 +20,7 @@ API than you are likely to write from scratch.
<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="296px" src="static/cybertec-new.png">
<img width="296px" src="static/cybertec.svg">
</a>
</td>
<td align="center" valign="middle">
@@ -43,7 +43,7 @@ API than you are likely to write from scratch.
</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="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/supabase.png">
<img width="296px" src="static/supabase.png">
</a>
</td>
<td align="center" valign="middle">
@@ -68,7 +68,7 @@ Big thanks to our sponsors! You can join them by supporting PostgREST on [Patreo
```
## [Documentation](http://postgrest.org)
Latest documentation is at [postgrest.org](http://postgrest.org). You can contribute to the docs in [PostgREST/postgrest-docs](https://github.com/PostgREST/postgrest-docs).
Latest documentation is at [postgrest.org](http://postgrest.org). You can contribute to the docs in [PostgREST/postgrest/docs](https://github.com/PostgREST/postgrest/tree/main/docs).
## Performance
+4
View File
@@ -0,0 +1,4 @@
packages: postgrest.cabal
tests: true
package *
ghc-options: -split-sections
+1 -1
View File
@@ -1 +1 @@
index-state: hackage.haskell.org 2023-10-13T13:54:33Z
index-state: hackage.haskell.org 2024-05-17T23:41:49Z
-20
View File
@@ -1,20 +0,0 @@
-- Settings to allow building with plain cabal. If this was
-- named just cabal.project, it would interfere with the default
-- nix build.
packages: .
-- Example of depending on a forked repository (the same dependency
-- would be mentioned in nix/overlays/haskell-packages.nix and
-- stack.yaml, and should refer to a main branch commit of the
-- repository.
--
-- source-repository-package
-- type: git
-- location: https://github.com/PostgREST/hasql-pool.git
-- tag: 4d462c4d47d762effefc7de6c85eaed55f144f1d
source-repository-package
type: git
location: https://github.com/PostgREST/postgresql-libpq.git
tag: 890a0a16cf57dd401420fdc6c7d576fb696003bc
+28 -49
View File
@@ -1,12 +1,23 @@
{ system ? builtins.currentSystem }:
{ system ? builtins.currentSystem
, compiler ? "ghc948"
, # Commit of the Nixpkgs repository that we want to use.
nixpkgsVersion ? import nix/nixpkgs-version.nix
, # Nix files that describe the Nixpkgs repository. We evaluate the expression
# using `import` below.
nixpkgs ? let inherit (nixpkgsVersion) owner repo rev tarballHash; in
builtins.fetchTarball {
url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz";
sha256 = tarballHash;
}
}:
let
name =
"postgrest";
compiler =
"ghc924";
# PostgREST source files, filtered based on the rules in the .gitignore files
# and file extensions. We want to include as litte as possible, as the files
# added here will increase the space used in the Nix store and trigger the
@@ -16,18 +27,6 @@ let
(pkgs.gitignoreSource ./.)
[ ".cabal" ".hs" ".lhs" "LICENSE" ];
# Commit of the Nixpkgs repository that we want to use.
nixpkgsVersion =
import nix/nixpkgs-version.nix;
# Nix files that describe the Nixpkgs repository. We evaluate the expression
# using `import` below.
nixpkgs =
builtins.fetchTarball {
url = "https://github.com/nixos/nixpkgs/archive/${nixpkgsVersion.rev}.tar.gz";
sha256 = nixpkgsVersion.tarballHash;
};
allOverlays =
import nix/overlays;
@@ -36,10 +35,10 @@ let
allOverlays.build-toolbox
allOverlays.checked-shell-script
allOverlays.gitignore
allOverlays.postgis
(allOverlays.postgresql-default { inherit patches; })
allOverlays.postgresql-libpq
allOverlays.postgresql-legacy
allOverlays.postgresql-future
allOverlays.postgis
(allOverlays.haskell-packages { inherit compiler; })
allOverlays.slocat
];
@@ -50,52 +49,28 @@ 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-16"; postgresql = pkgs.postgresql_16.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
{ 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 ]); }
{ name = "postgresql-12"; postgresql = pkgs.postgresql_12.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
{ name = "postgresql-11"; postgresql = pkgs.postgresql_11.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
{ name = "postgresql-10"; postgresql = pkgs.postgresql_10.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
{ name = "postgresql-9.6"; postgresql = pkgs.postgresql_9_6.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
{ name = "postgresql-9_6"; postgresql = pkgs.postgresql_9_6.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
];
patches =
pkgs.callPackage nix/patches { };
# Dynamic derivation for PostgREST
postgrest =
pkgs.haskell.packages."${compiler}".callCabal2nix name src { };
# 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;
staticHaskellPackage = import nix/static.nix { inherit compiler name pkgs src; };
# Options passed to cabal in dev tools and tests
devCabalOptions =
"-f dev --test-show-detail=direct";
profiledHaskellPackages =
pkgs.haskell.packages."${compiler}".extend (self: super:
pkgs.haskell.packages."${compiler}".extend (_: super:
{
mkDerivation =
args:
@@ -136,12 +111,16 @@ rec {
pkgs.callPackage nix/tools/cabalTools.nix { inherit devCabalOptions postgrest; };
withTools =
pkgs.callPackage nix/tools/withTools.nix { inherit cabalTools devCabalOptions postgresqlVersions postgrest; };
pkgs.callPackage nix/tools/withTools.nix { inherit postgresqlVersions postgrest; };
# Development tools.
devTools =
pkgs.callPackage nix/tools/devTools.nix { inherit tests style devCabalOptions hsie withTools; };
# Documentation tools.
docs =
pkgs.callPackage nix/tools/docs.nix { };
# Load testing tools.
loadtest =
pkgs.callPackage nix/tools/loadtest.nix { inherit withTools; };
@@ -172,8 +151,8 @@ rec {
};
} // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux rec {
# Static executable.
inherit postgrestStatic;
inherit packagesStatic;
inherit (staticHaskellPackage) postgrestStatic;
inherit (staticHaskellPackage) packagesStatic;
# Docker images and loading script.
docker =
@@ -17,7 +17,7 @@ write from scratch.
<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="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/cybertec-new.png">
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/cybertec.svg">
</a>
</td>
<td align="center" valign="middle">
@@ -68,3 +68,5 @@ The image is built from scratch using
no commands are listed in the image history. See the [PostgREST
respository](https://github.com/PostgREST/postgrest/tree/main/nix/tools/docker) for
details on the build process and how to inspect the image.
This does not apply to the arm64 variant, which is based on Ubuntu.
+1 -1
View File
@@ -5,4 +5,4 @@ Pipfile.lock
_diagrams/db.pdf
misspellings
unuseddict
.history
*.mo
+19 -11
View File
@@ -2,19 +2,27 @@
PostgREST docs use the reStructuredText format, check this [cheatsheet](https://github.com/ralsina/rst-cheatsheet/blob/master/rst-cheatsheet.rst) to get acquainted with it.
To build the docs locally, use [nix](https://nixos.org/nix/):
```bash
nix-shell
```
Once in the nix-shell you have the following commands available:
- `postgrest-docs-build`: Build the docs.
- `postgrest-docs-serve`: Build the docs and start a livereload server on `http://localhost:5500`.
- `postgrest-docs-spellcheck`: Run aspell.
To build the docs locally, see [the Nix development readme](/nix/README.md#documentation).
## Documentation structure
This documentation is structured according to tutorials-howtos-topics-references. For more details on the rationale of this structure,
see https://www.divio.com/blog/documentation.
## Translating
To create `.po` files for translation into a new language pass the language code as the first argument to `postgrest-docs-build`.
Example to add German/de:
```
postgrest-docs-build de
```
The livereload server also supports a language/locale argument to show the translated docs during translation:
```
postgrest-docs-serve de
```
Spellcheck is currently only available for the default language.
+1 -4
View File
@@ -17,10 +17,7 @@ The schema structure diagram is done with LaTeX. You can use a GUI like https://
Then use this command to generate the png file.
```bash
pdflatex --shell-escape -halt-on-error db.tex
## and move it to the static folder(it's not easy to do it in one go with the pdflatex)
mv db.png ../_static/
postgrest-docs-render
```
LaTeX is used because it's a tweakable plain text format.
+70
View File
@@ -0,0 +1,70 @@
@startuml
package "PostgREST" {
() HTTP as HTTPAPI
HTTPAPI - [Auth]
[Auth] -r.> [ApiRequest]
[ApiRequest] -r.> [Plan]
[Plan] -r.> [Query]
[Query] - () "Connection Pool" : "\t"
[Plan] -u-> [Schema Cache]:uses
[Schema Cache] <- () Listener : reloads
() HTTP as HTTPADMIN
[Admin] -r- () HTTPADMIN
[Config] -l- () CLI
[Config] <-l~ Listener
HTTPADMIN -[hidden]r- CLI
Listener -[hidden]r- [Schema Cache]
Listener -[hidden]r- [Config]
[Schema Cache] -l[hidden]- [Config]
[Schema Cache] -l[hidden]- [Admin]
[Schema Cache] -l[hidden]- CLI
}
database "PostgreSQL" {
node "API schema" as API {
rectangle "Functions, Views"
}
node Authorization {
rectangle "Roles, GRANT, RLS"
}
rectangle "Tables, extensions" as tbs
API -d- tbs
API -[hidden]l- Authorization
}
:user:
hexagon Proxy
:user: .r-> Proxy
HTTPAPI <.l- Proxy
:operator: .d-> HTTPADMIN
:operator: .d-> CLI
PostgreSQL <.developer : "\t"
Listener -r.> "PostgreSQL"
"Connection Pool" -r.> "PostgreSQL" : "\t\t"
note bottom of Auth
Authenticates the user request
end note
note bottom of ApiRequest
Parses the URL syntax
end note
note bottom of Query
Generates the SQL
end note
note top of Listener
LISTEN session
end note
@enduml
+1 -1
View File
@@ -52,7 +52,7 @@
% Text Node
\draw (414,127) node [anchor=north west][inner sep=0.75pt] [align=left] {tables};
% Text Node
\draw (272,203) node [anchor=north west][inner sep=0.75pt] [color={rgb, 255:red, 0; green, 0; blue, 0 } ,opacity=1 ] [align=center] { \\ views \\ + \\ \ \ stored procedures};
\draw (300,203) node [anchor=north west][inner sep=0.75pt] [color={rgb, 255:red, 0; green, 0; blue, 0 } ,opacity=1 ] [align=center] { \\ views \\ + \\ \ \ functions};
% Text Node
\draw (322,178) node [anchor=north west][inner sep=0.75pt] [color={rgb, 255:red, 255; green, 255; blue, 255 } ,opacity=1 ] [align=left] {\large\textbf{api}};
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

+56
View File
@@ -28,6 +28,7 @@ div.line-block {
#sponsors img{
margin: 10px;
width: 13em; /* ".. image::" does not apply width properly to SVGs */
}
#thanks{
@@ -93,3 +94,58 @@ div.line-block {
#api span.caption-text {
display: none;
}
/* Tweaks for dark mode from extension: sphinx-rtd-dark-theme */
html[data-theme="dark"] .highlight {
background-color: #17181c !important;
}
html[data-theme="dark"] .sphinx-tabs-tab {
color: var(--dark-link-color);
}
html[data-theme="dark"] .sphinx-tabs-panel {
border: 1px solid #404040;
border-top: 0;
background: #141414;
}
html[data-theme="dark"] .sphinx-tabs-tab[aria-selected="true"] {
border: 1px solid #404040;
border-bottom: 1px solid #141414;
background-color: #141414;
}
html[data-theme="dark"] [role="tablist"] {
border-bottom: 1px solid #404040;
}
html[data-theme="dark"] .btn-neutral {
color: white !important;
}
html[data-theme="dark"] .img-dark {
display: inline;
}
html:not([data-theme="dark"]) .img-dark {
display: none;
}
html[data-theme="dark"] .img-light {
display: none;
}
html:not([data-theme="dark"]) .img-light {
display: inline;
}
html[data-theme="dark"] .img-translucent img {
background-color: #cccccc;
}
.img-translucent img {
transition: background-color 0.3s;
margin-bottom: 24px;
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

+13 -1
View File
@@ -32,6 +32,7 @@ extensions = [
"sphinx_tabs.tabs",
"sphinx_copybutton",
"sphinxext.opengraph",
"sphinx_rtd_dark_mode",
]
# Add any paths that contain templates here, relative to this directory.
@@ -49,7 +50,7 @@ source_suffix = ".rst"
master_doc = "index"
# This is overriden by readthedocs with the version tag anyway
version = "12.0"
version = "devel"
# To avoid repetition in <title> we set this to an empty string.
release = ""
@@ -294,9 +295,20 @@ def setup(app):
# 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"
# TODO: these pages are returning 403 right now. Verify if they can be fixed by addressing the issue:
# https://github.com/PostgREST/postgrest/issues/3544
linkcheck_ignore = [
r"https://stackoverflow.com/",
r"https://www.patreon.com/postgrest",
r"https://blog.frankel.ch/poor-man-api",
]
# sphinx-tabs configuration
sphinx_tabs_disable_tab_closing = True
# sphinx_rtd_dark_mode configuration
default_dark_mode = False
# sphinxext-opengraph configuration
ogp_image = "_images/logo.png"
-100
View File
@@ -1,100 +0,0 @@
let
# Commit of the Nixpkgs repository that we want to use.
nixpkgsVersion = {
date = "2023-03-25";
rev = "dbf5322e93bcc6cfc52268367a8ad21c09d76fea";
tarballHash = "0lwk4v9dkvd28xpqch0b0jrac4xl9lwm6snrnzx8k5lby72kmkng";
};
# Nix files that describe the Nixpkgs repository. We evaluate the expression
# using `import` below.
pkgs = import
(fetchTarball {
url = "https://github.com/nixos/nixpkgs/archive/${nixpkgsVersion.rev}.tar.gz";
sha256 = nixpkgsVersion.tarballHash;
})
{ };
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 =
pkgs.writeShellScriptBin "postgrest-docs-build"
''
set -euo pipefail
cd "$(${pkgs.git}/bin/git rev-parse --show-toplevel)/docs"
# clean previous build, otherwise some errors might be supressed
rm -rf _build
${python}/bin/sphinx-build --color -W -b html -a -n . _build
'';
serve =
pkgs.writeShellScriptBin "postgrest-docs-serve"
''
set -euo pipefail
cd "$(${pkgs.git}/bin/git rev-parse --show-toplevel)/docs"
# livereload_docs.py needs to find "sphinx-build"
PATH=${python}/bin:$PATH
${python}/bin/python livereload_docs.py
'';
spellcheck =
pkgs.writeShellScriptBin "postgrest-docs-spellcheck"
''
set -euo pipefail
cd "$(${pkgs.git}/bin/git rev-parse --show-toplevel)/docs"
FILES=$(find . -type f -iname '*.rst' | tr '\n' ' ')
cat $FILES \
| grep -v '^\(\.\.\| \)' \
| sed 's/`.*`//g' \
| ${pkgs.aspell}/bin/aspell -d ${pkgs.aspellDicts.en}/lib/aspell/en_US -p ./postgrest.dict list \
| sort -f \
| tee misspellings
test ! -s misspellings
'';
# dictcheck detects obsolete entries in postgrest.dict, that are not used anymore
dictcheck =
pkgs.writeShellScriptBin "postgrest-docs-dictcheck"
''
set -euo pipefail
cd "$(${pkgs.git}/bin/git rev-parse --show-toplevel)/docs"
FILES=$(find . -type f -iname '*.rst' | tr '\n' ' ')
cat postgrest.dict \
| tail -n+2 \
| tr '\n' '\0' \
| xargs -0 -n 1 -i \
sh -c "grep \"{}\" $FILES > /dev/null || echo \"{}\"" \
| tee unuseddict
test ! -s unuseddict
'';
linkcheck =
pkgs.writeShellScriptBin "postgrest-docs-linkcheck"
''
set -euo pipefail
cd "$(${pkgs.git}/bin/git rev-parse --show-toplevel)/docs"
${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 -7
View File
@@ -9,7 +9,7 @@ Community Tutorials
* `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.
In this tutorial, GIS • OPS shows how to perform PostGIS calculations through PostgREST :ref:`functions` interface.
* `"CodeLess" backend using postgres, postgrest and oauth2 authentication with keycloak <https://www.mathieupassenaud.fr/codeless_backend/>`_ -
A step-by-step tutorial for using PostgREST with KeyCloak(hosted on a managed service).
@@ -20,7 +20,9 @@ Community Tutorials
* `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.
* `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.
.. * `Accessing a PostgreSQL database in Godot 4 via PostgREST <https://peterkingsbury.com/2022/08/16/godot-postgresql-postgrest/>`_
.. _templates:
@@ -49,16 +51,16 @@ DevOps
* `cloudgov-demo-postgrest <https://github.com/GSA/cloudgov-demo-postgrest>`_ - demo for a federally-compliant REST API on cloud.gov
* `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
* `cyril-sabourault/postgrest-cloud-run <https://github.com/cyril-sabourault/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
* `jbkarle/postgrest <https://github.com/jbkarle/postgrest>`_ - helm chart with a demo database for development and test purposes
.. _eco_external_notification:
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.
These are PostgreSQL bridges that propagate LISTEN/NOTIFY to external queues for further processing. This allows functions to initiate actions outside the database such as sending emails.
* `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
@@ -82,8 +84,8 @@ Client-Side Libraries
---------------------
* `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-dart <https://github.com/supabase/postgrest-dart>`_ - Dart
* `postgrest-ex <https://github.com/supabase-community/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
+81
View File
@@ -0,0 +1,81 @@
Architecture
############
This page describes the architecture of PostgREST.
Bird's Eye View
===============
.. image:: ../_static/arch.png
Code Map
========
This section talks briefly about various important modules.
Main
----
The starting point of the program is `Main.hs <https://github.com/PostgREST/postgrest/blob/main/main/Main.hs>`_.
CLI
---
Main then calls `CLI.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/CLI.hs>`_, which is in charge of :ref:`cli`.
App
---
`App.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/App.hs>`_ is then in charge of composing the different modules.
Auth
----
`Auth.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Auth.hs>`_ is in charge of :ref:`authn`.
Api Request
-----------
`ApiRequest.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/ApiRequest.hs>`_ is in charge of parsing the URL query string (following PostgREST syntax), the request headers, and the request body.
A request might be rejected at this level if it's invalid. For example when providing an unknown media type to PostgREST or using an unknown HTTP method.
Plan
----
Using the Schema Cache, `Plan.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Plan.hs>`_ fills in out-of-band SQL details (like an ``ON CONFLICT (pk)`` clause) required to complete the user request.
A request might be rejected at this level if it's invalid. For example when doing resource embedding on a nonexistent resource.
Query
-----
`Query.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Query.hs>`_ generates the SQL queries (parametrized and prepared) required to satisfy the user request.
Only at this stage a connection from the pool might be used.
Schema Cache
------------
`SchemaCache.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/SchemaCache.hs>`_ is in charge of :ref:`schema_cache`.
Config
------
`Config.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Config.hs>`_ is in charge of :ref:`configuration`.
Admin
-----
`Admin.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Admin.hs>`_ is in charge of the :ref:`admin_server`.
HTTP
----
The HTTP server is provided by `Warp <https://aosabook.org/en/posa/warp.html>`_.
Listener
--------
`Listener.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Listener.hs>`_ is in charge of maintaining a `LISTEN session <https://www.postgresql.org/docs/current/sql-listen.html>`_
that keeps the :ref:`schema_cache` and the :ref:`in_db_config` up to date.
+3 -3
View File
@@ -156,7 +156,7 @@ You can also grant execute on all functions in a schema to a higher privileged r
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.
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 function 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.
@@ -166,7 +166,7 @@ Another option is to define the function with the :code:`SECURITY DEFINER` optio
-- 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 $$
create or replace function login(email text, pass text, out token text) as $$
begin
-- access to a private schema called 'auth'
select auth.user_role(email, pass) into _role;
@@ -180,7 +180,7 @@ Note the ``SECURITY DEFINER`` keywords at the end of the function. See `PostgreS
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.
Views are invoked with the privileges of the view owner, much like functions 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.
+15 -12
View File
@@ -21,6 +21,12 @@ Supported PostgreSQL versions
PostgREST works with all PostgreSQL versions starting from 9.6.
.. note::
Support for PostgreSQL versions 9.6, 10 and 11 is deprecated. From this on version onwards, PostgREST will only support non-end-of-life PostgreSQL versions.
See https://www.postgresql.org/support/versioning/.
Running PostgREST
=================
@@ -163,14 +169,15 @@ If you want to have a visual overview of your API in your browser you can add sw
.. code-block:: yaml
swagger:
image: swaggerapi/swagger-ui
ports:
- "8080:8080"
expose:
- "8080"
environment:
API_URL: http://localhost:3000/
# in services:
swagger:
image: swaggerapi/swagger-ui
ports:
- "8080:8080"
expose:
- "8080"
environment:
API_URL: http://localhost:3000/
With this you can see the swagger-ui in your browser on port 8080.
@@ -181,10 +188,6 @@ Building from Source
When a pre-built binary does not exist for your system you can build the project from source.
.. note::
We discourage building and using PostgREST on **Alpine Linux** because of a reported GHC memory leak on that platform.
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/README/#how-to-install-stack>`_ for your platform
+5 -12
View File
@@ -42,7 +42,7 @@ The first step is to create an Nginx configuration file that proxies requests to
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.
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>`_.
Rate Limiting
-------------
@@ -55,7 +55,7 @@ Nginx supports "leaky bucket" rate limiting (see `official docs <https://nginx.o
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`.
Next we apply the zone to certain routes, like a hypothetical function called :code:`login`.
.. code-block:: nginx
@@ -73,17 +73,10 @@ 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-block:: bash
.. 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"
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.
+5 -3
View File
@@ -7,9 +7,11 @@
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.
A PostgREST instance exposes all the tables, views, and functions 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.
It is recommended that you don't expose tables on your API schema. Instead expose views and functions 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
.. container:: img-translucent
.. image:: ../_static/db.png
+1 -1
View File
@@ -191,7 +191,7 @@ References
For more information concerning PostgREST, cf.
- :ref:`s_proc_single_unnamed`
- :ref:`function_single_unnamed`
- :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>`
+112 -107
View File
@@ -38,7 +38,8 @@ and return a raw HTML document file.
Creating an HTML Response
-------------------------
Let's create a function that returns a basic HTML file, using `Tailwind CSS <https://v2.tailwindcss.com/>`_ for styling.
Let's create a function that returns a basic HTML file, using `Pico CSS <https://picocss.com>`_ for styling and
`Ionicons <https://ionic.io/ionicons>`_ to show some icons later.
.. code-block:: postgres
@@ -50,15 +51,20 @@ Let's create a function that returns a basic HTML file, using `Tailwind CSS <htt
<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">
<!-- Pico CSS for CSS styling -->
<link href="https://cdn.jsdelivr.net/npm/@picocss/pico@next/css/pico.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>
<main class="container">
<article>
<h5 style="text-align: center;">
PostgREST + HTMX To-Do List
</h5>
</article>
</main>
<!-- Script for Ionicons icons -->
<script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script>
<script nomodule src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script>
</body>
</html>
$html$;
@@ -84,24 +90,22 @@ For that, we'll also need a function to help us sanitize the HTML content that m
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">
<div>
<%2$s>
%3$s
</span>
</li>
</%2$s>
</div>
$html$,
$1.id,
case when $1.done then 'line-through text-gray-400' else '' end,
case when $1.done then 's' else 'span' 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>'
string_agg(api.html_todo(t), '<hr/>' order by t.id),
'<p><em>There is nothing else to do.</em></p>'
)
from api.todos t;
$$ language sql;
@@ -126,39 +130,44 @@ Next, let's add an endpoint to register a to-do in the database and modify the `
$$ 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>
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>
<!-- Pico CSS for CSS styling -->
<link href="https://cdn.jsdelivr.net/npm/@picocss/pico@next/css/pico.min.css" rel="stylesheet"/>
<!-- htmx for AJAX requests -->
<script src="https://unpkg.com/htmx.org"></script>
</head>
<body>
<main class="container"
style="max-width: 600px"
hx-headers='{"Accept": "text/html"}'>
<article>
<h5 style="text-align: center;">
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 type="text" name="_task" placeholder="Add a todo...">
</form>
<div id="todo-list-area">
$html$
|| api.html_all_todos() ||
$html$
<div>
</article>
</main>
<!-- Script for Ionicons icons -->
<script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script>
<script nomodule src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script>
</body>
</html>
$html$;
$$ language sql;
@@ -190,49 +199,46 @@ 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}'
select format($html$
<div class="grid">
<div id="todo-edit-area-%1$s">
<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">
<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>
<%2$s style="cursor: pointer">
%3$s
</%2$s>
</form>
</div>
<div style="text-align: right">
<button class="outline"
hx-get="/rpc/html_editable_task"
hx-vals='{"_id": "%1$s"}'
hx-target="#todo-edit-area-%1$s"
hx-trigger="click">
<span>
<ion-icon name="create"></ion-icon>
</span>
</button>
<button class="outline contrast"
hx-post="/rpc/delete_todo"
hx-vals='{"_id": %1$s}'
hx-target="#todo-list-area"
hx-trigger="click">
<span>
<ion-icon name="trash" style="color: #f87171"></ion-icon>
</span>
</button>
</div>
</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
);
$html$,
$1.id,
case when $1.done then 's' else 'span' end,
api.sanitize_html($1.task),
(not $1.done)::text
);
$$ language sql stable;
Let's deconstruct the new htmx features added:
@@ -267,22 +273,21 @@ 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;
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 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.
+4 -2
View File
@@ -81,12 +81,14 @@ First, in addition to the minimal example, we need to store the media types and
.. code-block:: postgres
alter table files
add column type text,
add column type text generated always as (byteamagic_mime(substr(blob, 0, 4100))) stored,
add column name text;
This uses the :code:`byteamagic_mime()` function from the `pg_byteamagic extension <https://github.com/nmandery/pg_byteamagic>`_ to automatically generate the type in the :code:`files` table. To guess the type of a file, it's generally enough to look at the beginning of the file, which is more efficient.
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>`.
For production, you probably want to configure additional caches, e.g. on the :ref:`reverse proxy <nginx>`.
.. code-block:: postgres
@@ -31,7 +31,7 @@ As in :ref:`sql_user_management`, we create a :code:`basic_auth` schema:
-- 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 SCHEMA basic_auth;
As in :ref:`sql_user_management`, we create the :code:`pgcrypto` and :code:`pgjwt` extensions. Here we prefer to put the extensions in its own schemas:
@@ -40,7 +40,7 @@ As in :ref:`sql_user_management`, we create the :code:`pgcrypto` and :code:`pgjw
CREATE SCHEMA ext_pgcrypto;
ALTER SCHEMA ext_pgcrypto OWNER TO postgres;
CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA ext_pgcrypto;
CREATE EXTENSION pgcrypto WITH SCHEMA ext_pgcrypto;
Concerning the `pgjwt extension <https://github.com/michelp/pgjwt>`_, please cf. to :ref:`client_auth`.
@@ -49,12 +49,12 @@ Concerning the `pgjwt extension <https://github.com/michelp/pgjwt>`_, please cf.
CREATE SCHEMA ext_pgjwt;
ALTER SCHEMA ext_pgjwt OWNER TO postgres;
CREATE EXTENSION IF NOT EXISTS pgjwt WITH SCHEMA ext_pgjwt;
CREATE EXTENSION pgjwt WITH SCHEMA ext_pgjwt;
In order to be able to work with postgres' SCRAM-SHA-256 password hashes, we also need the PBKDF2 key derivation function. Luckily there is `a PL/pgSQL implementation on stackoverflow <https://stackoverflow.com/q/47162200/2337147>`_:
In order to be able to work with postgres' SCRAM-SHA-256 password hashes, we also need the PBKDF2 key derivation function. Luckily there is `a PL/pgSQL implementation on stackoverflow <https://stackoverflow.com/a/72805848>`_:
.. code-block:: plpgsql
.. code-block:: postgres
CREATE FUNCTION basic_auth.pbkdf2(salt bytea, pw text, count integer, desired_length integer, algorithm text) RETURNS bytea
LANGUAGE plpgsql IMMUTABLE
@@ -117,10 +117,10 @@ In order to be able to work with postgres' SCRAM-SHA-256 password hashes, we als
ALTER FUNCTION basic_auth.pbkdf2(salt bytea, pw text, count integer, desired_length integer, algorithm text) OWNER TO postgres;
Analogous to :ref:`sql_user_management` creates the function :code:`basic_auth.user_role`, we create a helper function to check the user's password, here with another name and signature (since we want the username, not an email address).
Analogous to how :ref:`sql_user_management` creates the function :code:`basic_auth.user_role`, we create a helper function to check the user's password, here with another name and signature (since we want the username, not an email address).
But contrary to :ref:`sql_user_management`, this function does not use a dedicated :code:`users` table with passwords, but instead utilizes the built-in table `pg_catalog.pg_authid <https://www.postgresql.org/docs/current/catalog-pg-authid.html>`_:
.. code-block:: plpgsql
.. code-block:: postgres
CREATE FUNCTION basic_auth.check_user_pass(username text, password text) RETURNS name
LANGUAGE sql
@@ -160,22 +160,17 @@ Logins
As described in :ref:`client_auth`, we'll create a JWT token 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:: plpgsql
CREATE TYPE basic_auth.jwt_token AS (
token text
);
.. code-block:: postgres
-- if you are not using psql, you need to replace :DBNAME with the current database's name.
ALTER DATABASE :DBNAME SET "app.jwt_secret" to 'reallyreallyreallyreallyverysafe';
CREATE FUNCTION public.login(username text, password text) RETURNS basic_auth.jwt_token
CREATE FUNCTION public.login(username text, password text, OUT token text)
LANGUAGE plpgsql security definer
AS $$
DECLARE
_role name;
result basic_auth.jwt_token;
BEGIN
-- check email and password
SELECT basic_auth.check_user_pass(username, password) INTO _role;
@@ -190,8 +185,7 @@ As described in :ref:`client_auth`, we'll create a JWT token inside our login fu
SELECT login.username as role,
extract(epoch FROM now())::integer + 60*60 AS exp
) r
INTO result;
RETURN result;
INTO token;
END;
$$;
@@ -259,19 +253,11 @@ Test at the REST level
~~~~~~~~~~~~~~~~~~~~~~
An API request to call this function would look like:
.. tabs::
.. code-block:: bash
.. code-tab:: http
POST /rpc/login HTTP/1.1
{ "username": "foo", "password": "bar" }
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/login" \
-X POST -H "Content-Type: application/json" \
-d '{ "username": "foo", "password": "bar" }'
curl "http://localhost:3000/rpc/login" \
-X POST -H "Content-Type: application/json" \
-d '{ "username": "foo", "password": "bar" }'
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!)
@@ -296,31 +282,18 @@ Let's add a table, intended for the :code:`foo` user:
Now try to get the table's contents with:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /foobar HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/foobar"
curl "http://localhost:3000/foobar"
This should fail --- of course, we haven't specified the user, thus PostgREST falls back to the :code:`anon` user and denies access.
Add an :code:`Authorization` header. Please use the token value from the login function call above instead of the one provided below.
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /foobar HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiZm9vIiwiZXhwIjoxNjY4MTkyMjAyfQ.zzdHCBjfkqDQLQ8D7CHO3cIALF6KBCsfPTWgwhCiHCY
.. code-tab:: bash Curl
curl "http://localhost:3000/foobar" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiZm9vIiwiZXhwIjoxNjY4MTkyMjAyfQ.zzdHCBjfkqDQLQ8D7CHO3cIALF6KBCsfPTWgwhCiHCY"
curl "http://localhost:3000/foobar" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiZm9vIiwiZXhwIjoxNjY4MTkyMjAyfQ.zzdHCBjfkqDQLQ8D7CHO3cIALF6KBCsfPTWgwhCiHCY"
This will fail again --- we get :code:`Permission denied to set role`. We forgot to allow the authenticator role to switch into this user by executing:
+18 -40
View File
@@ -17,9 +17,8 @@ First we'll need a table to keep track of our users:
-- 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
create table
basic_auth.users (
email text primary key check ( email ~* '^.+@.+\..+$' ),
pass text not null check (length(pass) < 512),
@@ -28,9 +27,9 @@ First we'll need a table to keep track of our users:
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
.. code-block:: postgres
create or replace function
create 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
@@ -42,7 +41,6 @@ We would like the role to be a foreign key to actual database roles, however Pos
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
@@ -50,11 +48,11 @@ We would like the role to be a foreign key to actual database roles, however Pos
Next we'll use the pgcrypto extension and a trigger to keep passwords safe in the :code:`users` table.
.. code-block:: plpgsql
.. code-block:: postgres
create extension if not exists pgcrypto;
create extension pgcrypto;
create or replace function
create function
basic_auth.encrypt_pass() returns trigger as $$
begin
if tg_op = 'INSERT' or new.pass <> old.pass then
@@ -64,7 +62,6 @@ Next we'll use the pgcrypto extension and a trigger to keep passwords safe in th
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
@@ -72,9 +69,9 @@ Next we'll use the pgcrypto extension and a trigger to keep passwords safe in th
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
.. code-block:: postgres
create or replace function
create function
basic_auth.user_role(email text, pass text) returns name
language plpgsql
as $$
@@ -118,15 +115,11 @@ 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.
Next write a function 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 $$
CREATE FUNCTION jwt_test(OUT token text) AS $$
SELECT public.sign(
row_to_json(r), 'reallyreallyreallyreallyverysafe'
) AS token
@@ -141,7 +134,7 @@ 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.
To avoid hard-coding the secret in functions, save it as a property of the database.
.. code-block:: postgres
@@ -161,17 +154,11 @@ As described in `JWT from SQL`_, we'll create a JWT inside our login function. N
.. 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 $$
create function
login(email text, pass text, out token text) as $$
declare
_role name;
result basic_auth.jwt_token;
begin
-- check email and password
select basic_auth.user_role(email, pass) into _role;
@@ -186,8 +173,7 @@ As described in `JWT from SQL`_, we'll create a JWT inside our login function. N
select _role as role, login.email as email,
extract(epoch from now())::integer + 60*60 as exp
) r
into result;
return result;
into token;
end;
$$ language plpgsql security definer;
@@ -199,19 +185,11 @@ the anonymous user :code:`anon` doesn't need permission to read the :code:`basic
An API request to call this function would look like:
.. tabs::
.. code-block:: bash
.. 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" }'
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!)
@@ -5,133 +5,13 @@ Working with PostgreSQL data types
:author: `Laurence Isla <https://github.com/laurenceisla>`_
PostgREST makes use of PostgreSQL string representations to work with data types. Thanks to this, you can use special values, such as ``now`` for timestamps, ``yes`` for booleans or time values including the time zones. This page describes how you can take advantage of these string representations to perform operations on different PostgreSQL data types.
PostgREST makes use of PostgreSQL string representations to work with data types. Thanks to this, you can use special values, such as ``now`` for timestamps, ``yes`` for booleans or time values including the time zones. This page describes how you can take advantage of these string representations and some alternatives to perform operations on different PostgreSQL data types.
.. contents::
:local:
:depth: 1
Timestamps
----------
You can use the **time zone** to filter or send data if needed.
.. code-block:: postgres
create table reports (
id int primary key
, due_date timestamptz
);
Suppose you are located in Sydney and want create a report with the date in the local time zone. Your request should look like this:
.. tabs::
.. code-tab:: http
POST /reports HTTP/1.1
Content-Type: application/json
[{ "id": 1, "due_date": "2022-02-24 11:10:15 Australia/Sydney" },
{ "id": 2, "due_date": "2022-02-27 22:00:00 Australia/Sydney" }]
.. code-tab:: bash Curl
curl "http://localhost:3000/reports" \
-X POST -H "Content-Type: application/json" \
-d '[{ "id": 1, "due_date": "2022-02-24 11:10:15 Australia/Sydney" },{ "id": 2, "due_date": "2022-02-27 22:00:00 Australia/Sydney" }]'
Someone located in Cairo can retrieve the data using their local time, too:
.. tabs::
.. code-tab:: http
GET /reports?due_date=eq.2022-02-24+02:10:15+Africa/Cairo HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/reports?due_date=eq.2022-02-24+02:10:15+Africa/Cairo"
.. code-block:: json
[
{
"id": 1,
"due_date": "2022-02-23T19:10:15-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:
.. tabs::
.. code-tab:: http
GET /reports?or=(and(due_date.gte.today,due_date.lte.tomorrow),and(due_date.gt.-infinity,due_date.lte.epoch)) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/reports?or=(and(due_date.gte.today,due_date.lte.tomorrow),and(due_date.gt.-infinity,due_date.lte.epoch))"
.. code-block:: json
[
{
"id": 2,
"due_date": "2022-02-27T06:00:00-05:00"
}
]
JSON
----
To work with a ``json`` type column, you can handle the value as a JSON object.
.. code-block:: postgres
create table products (
id int primary key,
name text unique,
extra_info json
);
You can insert a new product using a JSON object for the ``extra_info`` column:
.. tabs::
.. code-tab:: http
POST /products HTTP/1.1
Content-Type: application/json
{
"id": 1,
"name": "Canned fish",
"extra_info": {
"expiry_date": "2025-12-31",
"exportable": true
}
}
.. code-tab:: bash Curl
curl "http://localhost:3000/products" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
{
"id": 1,
"name": "Canned fish",
"extra_info": {
"expiry_date": "2025-12-31",
"exportable": true
}
}
EOF
To query and filter the data see :ref:`json_columns` for a complete reference.
.. NOTE: Titles are ordered alphabetically. New entries should respect this order.
Arrays
------
@@ -149,61 +29,33 @@ To handle `array types <https://www.postgresql.org/docs/current/arrays.html>`_ y
You can insert a new value using string representation.
.. tabs::
.. code-tab:: http
POST /movies HTTP/1.1
Content-Type: application/json
.. code-block:: bash
curl "http://localhost:3000/movies" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
{
"id": 1,
"title": "Paddington",
"tags": "{family,comedy,not streamable}",
"performance_times": "{12:40,15:00,20:00}"
}
.. code-tab:: bash Curl
curl "http://localhost:3000/movies" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
{
"id": 1,
"title": "Paddington",
"tags": "{family,comedy,not streamable}",
"performance_times": "{12:40,15:00,20:00}"
}
EOF
EOF
Or you could send the same data using JSON array format:
.. tabs::
.. code-tab:: http
POST /movies HTTP/1.1
Content-Type: application/json
.. code-block:: bash
curl "http://localhost:3000/movies" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
{
"id": 1,
"title": "Paddington",
"tags": ["family", "comedy", "not streamable"],
"performance_times": ["12:40", "15:00", "20:00"]
}
.. code-tab:: bash Curl
curl "http://localhost:3000/movies" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
{
"id": 1,
"title": "Paddington",
"tags": ["family", "comedy", "not streamable"],
"performance_times": ["12:40", "15:00", "20:00"]
}
EOF
EOF
To query the data you can use arrow operators. See :ref:`composite_array_columns`.
@@ -220,38 +72,21 @@ Similarly to one-dimensional arrays, both the string representation and JSON arr
You can now update the item using JSON array format:
.. tabs::
.. code-tab:: http
PATCH /movies?id=eq.1 HTTP/1.1
Content-Type: application/json
.. code-block:: bash
curl "http://localhost:3000/movies?id=eq.1" \
-X PATCH -H "Content-Type: application/json" \
-d @- << EOF
{
"cinema_floor_auditorium": [ [ [1,2], [6,7] ], [ [3,5], [8,9] ] ]
}
.. code-tab:: bash Curl
curl "http://localhost:3000/movies?id=eq.1" \
-X PATCH -H "Content-Type: application/json" \
-d @- << EOF
{
"cinema_floor_auditorium": [ [ [1,2], [6,7] ], [ [3,5], [8,9] ] ]
}
EOF
EOF
Then, for example, to query the auditoriums that are located in the first cinema (position 0 in the array) and on the second floor (position 1 in the next inner array), we can use the arrow operators this way:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /movies?select=title,auditorium:cinema_floor_auditorium->0->1&id=eq.1 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/movies?select=title,auditorium:cinema_floor_auditorium->0->1&id=eq.1"
curl "http://localhost:3000/movies?select=title,auditorium:cinema_floor_auditorium->0->1&id=eq.1"
.. code-block:: json
@@ -262,211 +97,6 @@ Then, for example, to query the auditoriums that are located in the first cinema
}
]
Composite Types
---------------
With PostgREST, you have two options to handle `composite type columns <https://www.postgresql.org/docs/current/rowtypes.html>`_.
.. code-block:: postgres
create type dimension as (
length decimal(6,2),
width decimal (6,2),
height decimal (6,2),
unit text
);
create table products (
id int primary key,
size dimension
);
insert into products (id, size)
values (1, '(5.0,5.0,10.0,"cm")');
On one hand you can insert values using string representation.
.. tabs::
.. code-tab:: http
POST /products HTTP/1.1
Content-Type: application/json
{ "id": 2, "size": "(0.7,0.5,1.8,\"m\")" }
.. code-tab:: bash Curl
curl "http://localhost:3000/products" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
{ "id": 2, "size": "(0.7,0.5,1.8,\"m\")" }
EOF
Or you could insert the same data in JSON format.
.. tabs::
.. code-tab:: http
POST /products HTTP/1.1
Content-Type: application/json
{
"id": 2,
"size": {
"length": 0.7,
"width": 0.5,
"height": 1.8,
"unit": "m"
}
}
.. code-tab:: bash Curl
curl "http://localhost:3000/products" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
{
"id": 2,
"size": {
"length": 0.7,
"width": 0.5,
"height": 1.8,
"unit": "m"
}
}
EOF
You can also query the data using arrow operators. See :ref:`composite_array_columns`.
Ranges
------
PostgREST allows you to handle `ranges <https://www.postgresql.org/docs/current/rangetypes.html>`_.
.. code-block:: postgres
create table events (
id int primary key,
name text unique,
duration tsrange
);
To insert a new event, specify the ``duration`` value as a string representation of the ``tsrange`` type:
.. tabs::
.. code-tab:: http
POST /events HTTP/1.1
Content-Type: application/json
{
"id": 1,
"name": "New Year's Party",
"duration": "['2022-12-31 11:00','2023-01-01 06:00']"
}
.. code-tab:: bash Curl
curl "http://localhost:3000/events" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
{
"id": 1,
"name": "New Year's Party",
"duration": "['2022-12-31 11:00','2023-01-01 06:00']"
}
EOF
You can use range :ref:`operators <operators>` to filter the data. But, in this case, requesting a filter like ``events?duration=cs.2023-01-01`` will return an error, because PostgreSQL needs an explicit cast from string to timestamp. A workaround is to use a range starting and ending in the same date:
.. tabs::
.. code-tab:: http
GET /events?duration=cs.[2023-01-01,2023-01-01] HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/events?duration=cs.\[2023-01-01,2023-01-01\]"
.. code-block:: json
[
{
"id": 1,
"name": "New Year's Party",
"duration": "[\"2022-12-31 11:00:00\",\"2023-01-01 06:00:00\"]"
}
]
.. _casting_range_to_json:
Casting a Range to a JSON Object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As you may have noticed, the ``tsrange`` value is returned as a string literal. To return it as a JSON value, first you need to create a function that will do the conversion from a ``tsrange`` type:
.. code-block:: postgres
create or replace function tsrange_to_json(tsrange) returns json as $$
select json_build_object(
'lower', lower($1)
, 'upper', upper($1)
, 'lower_inc', lower_inc($1)
, 'upper_inc', upper_inc($1)
);
$$ language sql;
Then, create the cast using this function:
.. code-block:: postgres
create cast (tsrange as json) with function tsrange_to_json(tsrange) as assignment;
Finally, do the request :ref:`casting the range column <casting_columns>`:
.. tabs::
.. code-tab:: http
GET /events?select=id,name,duration::json HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/events?select=id,name,duration::json"
.. code-block:: json
[
{
"id": 1,
"name": "New Year's Party",
"duration": {
"lower": "2022-12-31T11:00:00",
"upper": "2023-01-01T06:00:00",
"lower_inc": true,
"upper_inc": true
}
}
]
.. note::
If you don't want to modify casts for built-in types, an option would be to `create a custom type <https://www.postgresql.org/docs/current/sql-createtype.html>`_
for your own ``tsrange`` and add its own cast.
.. code-block:: postgres
create type mytsrange as range (subtype = timestamp, subtype_diff = tsrange_subdiff);
-- define column types and casting function analogously to the above example
-- ...
create cast (mytsrange as json) with function mytsrange_to_json(mytsrange) as assignment;
Bytea
-----
@@ -491,20 +121,11 @@ Let's download the PostgREST logo for our test.
Now, to send the file ``postgrest-logo.png`` we need to set the ``Content-Type: application/octet-stream`` header in the request:
.. tabs::
.. code-block:: bash
.. code-tab:: http
POST /rpc/upload_binary HTTP/1.1
Content-Type: application/octet-stream
postgrest-logo.png
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/upload_binary" \
-X POST -H "Content-Type: application/octet-stream" \
--data-binary "@postgrest-logo.png"
curl "http://localhost:3000/rpc/upload_binary" \
-X POST -H "Content-Type: application/octet-stream" \
--data-binary "@postgrest-logo.png"
To get the image from the database, use :ref:`custom_media` like so:
@@ -516,17 +137,10 @@ To get the image from the database, use :ref:`custom_media` like so:
select file from files where id = $1;
$$ language sql;
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /get_image?id=1 HTTP/1.1
Accept: image/png
.. code-tab:: bash Curl
curl "http://localhost:3000/get_image?id=1" \
-H "Accept: image/png"
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.
@@ -534,6 +148,104 @@ See :ref:`providing_img` for a step-by-step example on how to handle images in H
Be careful when saving binaries in the database, having a separate storage service for these is preferable in most cases. See `Storing Binary files in the Database <https://wiki.postgresql.org/wiki/BinaryFilesInDB>`_.
Composite Types
---------------
With PostgREST, you have two options to handle `composite type columns <https://www.postgresql.org/docs/current/rowtypes.html>`_.
.. code-block:: postgres
create type dimension as (
length decimal(6,2),
width decimal (6,2),
height decimal (6,2),
unit text
);
create table products (
id int primary key,
size dimension
);
insert into products (id, size)
values (1, '(5.0,5.0,10.0,"cm")');
On one hand you can insert values using string representation.
.. code-block:: bash
curl "http://localhost:3000/products" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
{ "id": 2, "size": "(0.7,0.5,1.8,\"m\")" }
EOF
Or you could insert the same data in JSON format.
.. code-block:: bash
curl "http://localhost:3000/products" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
{
"id": 2,
"size": {
"length": 0.7,
"width": 0.5,
"height": 1.8,
"unit": "m"
}
}
EOF
You can also query the data using arrow operators. See :ref:`composite_array_columns`.
Enums
-----
You can handle `Enumerated Types <https://www.postgresql.org/docs/current/datatype-enum.html>`_ using string representations:
.. code-block:: postgres
create type letter_size as enum ('s','m','l','xl');
create table products (
id int primary key generated always as identity,
name text,
size letter_size
);
To insert or update the value use a string:
.. code-block:: bash
curl -X POST "http://localhost:3000/products" \
-H "Content-Type: application/json" \
-d @- << EOF
{ "name": "t-shirt", "size": "l" }
EOF
You can then query and filter the enum using the compatible :ref:`operators <operators>`.
For example, to get all the products larger than `m` and ordering them by their size:
.. code-block:: bash
curl "http://localhost:3000/products?select=name,size&size=gt.m&order=size"
.. code-block:: json
[
{
"name": "t-shirt",
"size": "l"
},
{
"name": "hoodie",
"size": "xl"
}
]
hstore
------
@@ -551,53 +263,67 @@ You can work with data types belonging to additional supplied modules such as `h
The ``name`` column will have the name of the country in different formats. You can insert values using the string representation for that data type:
.. tabs::
.. code-tab:: http
POST /countries HTTP/1.1
Content-Type: application/json
.. code-block:: bash
curl "http://localhost:3000/countries" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
[
{ "id": 1, "name": "common => Egypt, official => \"Arab Republic of Egypt\", native => مصر" },
{ "id": 2, "name": "common => Germany, official => \"Federal Republic of Germany\", native => Deutschland" }
]
.. code-tab:: bash Curl
curl "http://localhost:3000/countries" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
[
{ "id": 1, "name": "common => Egypt, official => \"Arab Republic of Egypt\", native => مصر" },
{ "id": 2, "name": "common => Germany, official => \"Federal Republic of Germany\", native => Deutschland" }
]
EOF
EOF
Notice that the use of ``"`` in the value of the ``name`` column needs to be escaped using a backslash ``\``.
You can also query and filter the value of a ``hstore`` column using the arrow operators, as you would do for a :ref:`JSON column<json_columns>`. For example, if you want to get the native name of Egypt:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /countries?select=name->>native&name->>common=like.Egypt HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/countries?select=name->>native&name->>common=like.Egypt"
curl "http://localhost:3000/countries?select=name->>native&name->>common=like.Egypt"
.. code-block:: json
[{ "native": "مصر" }]
JSON
----
To work with a ``json`` type column, you can handle the value as a JSON object.
.. code-block:: postgres
create table products (
id int primary key,
name text unique,
extra_info json
);
You can insert a new product using a JSON object for the ``extra_info`` column:
.. code-block:: bash
curl "http://localhost:3000/products" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
{
"id": 1,
"name": "Canned fish",
"extra_info": {
"expiry_date": "2025-12-31",
"exportable": true
}
}
EOF
To query and filter the data see :ref:`json_columns` for a complete reference.
.. _ww_postgis:
PostGIS
-------
You can use the string representation for `PostGIS <https://postgis.net/>`_ data types such as ``geometry`` or ``geography`` (you need to `install PostGIS <https://postgis.net/install/>`_ first).
You can use the string representation for `PostGIS <https://postgis.net/>`_ data types such as ``geometry`` or ``geography`` (you need to `install PostGIS <https://postgis.net/documentation/getting_started/>`_ first).
.. code-block:: postgres
@@ -612,42 +338,23 @@ You can use the string representation for `PostGIS <https://postgis.net/>`_ data
To add areas in polygon format, you can use string representation:
.. tabs::
.. code-tab:: http
POST /coverage HTTP/1.1
Content-Type: application/json
.. code-block:: bash
curl "http://localhost:3000/coverage" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
[
{ "id": 1, "name": "small", "area": "SRID=4326;POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))" },
{ "id": 2, "name": "big", "area": "SRID=4326;POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))" }
]
.. code-tab:: bash Curl
curl "http://localhost:3000/coverage" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
[
{ "id": 1, "name": "small", "area": "SRID=4326;POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))" },
{ "id": 2, "name": "big", "area": "SRID=4326;POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))" }
]
EOF
EOF
Now, when you request the information, PostgREST will automatically cast the ``area`` column into a ``Polygon`` geometry type. Although this is useful, you may need the whole output to be in `GeoJSON <https://geojson.org/>`_ format out of the box, which can be done by including the ``Accept: application/geo+json`` in the request. This will work for PostGIS versions 3.0.0 and up and will return the output as a `FeatureCollection Object <https://www.rfc-editor.org/rfc/rfc7946#section-3.3>`_:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /coverage HTTP/1.1
Accept: application/geo+json
.. code-tab:: bash Curl
curl "http://localhost:3000/coverage" \
-H "Accept: application/geo+json"
curl "http://localhost:3000/coverage" \
-H "Accept: application/geo+json"
.. code-block:: json
@@ -711,15 +418,9 @@ In the case that you are using older PostGIS versions, then creating a function
Now this query will return the same results:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /rpc/coverage_geo_collection HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/coverage_geo_collection"
curl "http://localhost:3000/rpc/coverage_geo_collection"
.. code-block:: json
@@ -754,3 +455,157 @@ Now this query will return the same results:
}
]
}
Ranges
------
PostgREST allows you to handle `ranges <https://www.postgresql.org/docs/current/rangetypes.html>`_.
.. code-block:: postgres
create table events (
id int primary key,
name text unique,
duration tsrange
);
To insert a new event, specify the ``duration`` value as a string representation of the ``tsrange`` type:
.. code-block:: bash
curl "http://localhost:3000/events" \
-X POST -H "Content-Type: application/json" \
-d @- << EOF
{
"id": 1,
"name": "New Year's Party",
"duration": "['2022-12-31 11:00','2023-01-01 06:00']"
}
EOF
You can use range :ref:`operators <operators>` to filter the data. But, in this case, requesting a filter like ``events?duration=cs.2023-01-01`` will return an error, because PostgreSQL needs an explicit cast from string to timestamp. A workaround is to use a range starting and ending in the same date:
.. code-block:: bash
curl "http://localhost:3000/events?duration=cs.\[2023-01-01,2023-01-01\]"
.. code-block:: json
[
{
"id": 1,
"name": "New Year's Party",
"duration": "[\"2022-12-31 11:00:00\",\"2023-01-01 06:00:00\"]"
}
]
.. _casting_range_to_json:
Casting a Range to a JSON Object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As you may have noticed, the ``tsrange`` value is returned as a string literal. To return it as a JSON value, first you need to create a function that will do the conversion from a ``tsrange`` type:
.. code-block:: postgres
create or replace function tsrange_to_json(tsrange) returns json as $$
select json_build_object(
'lower', lower($1)
, 'upper', upper($1)
, 'lower_inc', lower_inc($1)
, 'upper_inc', upper_inc($1)
);
$$ language sql;
Then, create the cast using this function:
.. code-block:: postgres
create cast (tsrange as json) with function tsrange_to_json(tsrange) as assignment;
Finally, do the request :ref:`casting the range column <casting_columns>`:
.. code-block:: bash
curl "http://localhost:3000/events?select=id,name,duration::json"
.. code-block:: json
[
{
"id": 1,
"name": "New Year's Party",
"duration": {
"lower": "2022-12-31T11:00:00",
"upper": "2023-01-01T06:00:00",
"lower_inc": true,
"upper_inc": true
}
}
]
.. note::
If you don't want to modify casts for built-in types, an option would be to `create a custom type <https://www.postgresql.org/docs/current/sql-createtype.html>`_
for your own ``tsrange`` and add its own cast.
.. code-block:: postgres
create type mytsrange as range (subtype = timestamp, subtype_diff = tsrange_subdiff);
-- define column types and casting function analogously to the above example
-- ...
create cast (mytsrange as json) with function mytsrange_to_json(mytsrange) as assignment;
Timestamps
----------
You can use the **time zone** to filter or send data if needed.
.. code-block:: postgres
create table reports (
id int primary key
, due_date timestamptz
);
Suppose you are located in Sydney and want create a report with the date in the local time zone. Your request should look like this:
.. code-block:: bash
curl "http://localhost:3000/reports" \
-X POST -H "Content-Type: application/json" \
-d '[{ "id": 1, "due_date": "2022-02-24 11:10:15 Australia/Sydney" },{ "id": 2, "due_date": "2022-02-27 22:00:00 Australia/Sydney" }]'
Someone located in Cairo can retrieve the data using their local time, too:
.. code-block:: bash
curl "http://localhost:3000/reports?due_date=eq.2022-02-24+02:10:15+Africa/Cairo"
.. code-block:: json
[
{
"id": 1,
"due_date": "2022-02-23T19:10:15-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:
.. code-block:: bash
curl "http://localhost:3000/reports?or=(and(due_date.gte.today,due_date.lte.tomorrow),and(due_date.gt.-infinity,due_date.lte.epoch))"
.. code-block:: json
[
{
"id": 2,
"due_date": "2022-02-27T06:00:00-05:00"
}
]
+44 -21
View File
@@ -5,7 +5,7 @@ PostgREST Documentation
.. container:: image-container
.. figure:: _static/logo.png
.. figure:: ../static/postgrest.png
.. image:: https://img.shields.io/github/stars/postgrest/postgrest.svg?style=social
:target: https://github.com/PostgREST/postgrest
@@ -34,38 +34,59 @@ 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
.. container:: img-dark
.. image:: _static/gnuhost.png
.. image:: ../static/cybertec-dark.svg
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
.. container:: img-light
.. image:: ../static/cybertec.svg
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
.. image:: ../static/gnuhost.png
: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
.. container:: img-dark
.. image:: ../static/neon-dark.jpg
:target: https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest
.. container:: img-light
.. image:: ../static/neon.jpg
:target: https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest
|
.. image:: _static/code-build.webp
:target: https://code.build/?utm_source=sponsor&utm_campaign=postgrest
:width: 13em
.. container:: img-dark
.. 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/code-build-dark.png
:target: https://code.build/?utm_source=sponsor&utm_campaign=postgrest
.. image:: _static/tembo.png
.. container:: img-light
.. image:: ../static/code-build.png
:target: https://code.build/?utm_source=sponsor&utm_campaign=postgrest
.. container:: img-dark
.. image:: ../static/supabase-dark.png
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
.. container:: img-light
.. 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
.. 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
to create space and center the logos. It's not easy to layout with restructuredText.
.. .. image:: _static/empty.png
:target: #sponsors
:width: 13em
|
@@ -126,11 +147,13 @@ Technical references for PostgREST's functionality.
references/auth.rst
references/api.rst
references/cli.rst
references/transactions.rst
references/connection_pool.rst
references/schema_cache.rst
references/errors.rst
references/configuration.rst
references/observability.rst
references/*
Explanations
@@ -195,11 +218,11 @@ 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>`_
* `Moat <https://www.oracle.com/advertising/measurement/>`_
* `Netwo <https://www.netwo.io>`_
* `Nimbus <https://www.nimbusforwork.com>`_
* `Nimbus <https://www.nimbusfacility.com/sg/home>`_
- 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>`_
* `OpenBooking <https://openbooking.ch>`_
* `Redsmin <https://www.redsmin.com>`_
* `Sompani <https://www.sompani.com>`_
* `Supabase <https://supabase.com>`_
-122
View File
@@ -1,122 +0,0 @@
.. _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"}'
-20
View File
@@ -9,23 +9,3 @@ 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);
}
+4 -16
View File
@@ -8,27 +8,15 @@ 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-block:: bash
.. 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
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-block:: bash
.. code-tab:: http
DELETE /logs HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/logs" -X DELETE
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:
+1 -1
View File
@@ -1,7 +1,7 @@
systemd
=======
For Linux distributions that use **systemd** (Ubuntu, Debian, Archlinux) you can create a daemon in the following way.
For Linux distributions that use **systemd** (Ubuntu, Debian, Arch Linux) you can create a daemon in the following way.
First, create postgrest configuration in ``/etc/postgrest/config``
-11
View File
@@ -1,11 +0,0 @@
#!/usr/bin/env python
from livereload import Server, shell
from subprocess import call
## Build docs at startup
call(["sphinx-build", "-b", "html", "-a", "-n", ".", "_build"])
server = Server()
server.watch("**/*.rst", shell("sphinx-build -b html -a -n . _build"))
# For custom port and host
# server.serve(root='_build/', host='192.168.1.2')
server.serve(root="_build/")
+7 -6
View File
@@ -1,22 +1,21 @@
personal_ws-1.1 en 0 utf-8
api
API's
APIs
APISIX
Archlinux
aud
Auth
auth
authenticator
backoff
balancer
booleans
buildpack
BOM
Bytea
Cardano
casted
cd
centric
CLI
coercible
conf
Cloudflare
@@ -30,6 +29,8 @@ DDL
DOM
DevOps
dockerize
enum
Enums
eq
ETH
Ethereum
@@ -38,7 +39,6 @@ filename
FreeBSD
fts
GeoJSON
GHC
Github
Google
grantor
@@ -47,7 +47,6 @@ Greenplum
gte
GUC
Haskell
Heroku
HMAC
htmx
Htmx
@@ -95,10 +94,12 @@ npm
nxl
nxr
OAuth
Observability
OpenAPI
openapi
ORM
ov
parametrized
passphrase
PBKDF
PgBouncer
@@ -149,13 +150,13 @@ spreaded
Spreaded
SQL
sql
SQLSTATE
sr
SSL
stateful
stdout
supervisees
SvelteKit
syslog
systemd
todo
todos
+93
View File
@@ -0,0 +1,93 @@
.. _admin_server:
Admin Server
############
PostgREST provides an admin server that can be enabled by setting :ref:`admin-server-port`.
.. _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.
Two endpoints ``live`` and ``ready`` will then be available.
.. important::
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.
Live
----
The ``live`` endpoint verifies if PostgREST is running on its configured port. A request will return ``200 OK`` if PostgREST is alive or ``500`` otherwise.
For instance, to verify if PostgREST is running while the ``admin-server-port`` is set to ``3001``:
.. code-block:: bash
curl -I "http://localhost:3001/live"
.. code-block:: http
HTTP/1.1 200 OK
Ready
-----
Additionally to the ``live`` check, the ``ready`` endpoint checks the state of the :ref:`connection_pool` and the :ref:`schema_cache`. A request will return ``200 OK`` if both are good or ``503`` if not.
.. code-block:: bash
curl -I "http://localhost:3001/ready"
.. code-block:: http
HTTP/1.1 200 OK
PostgREST will try to recover from the ``503`` state with :ref:`automatic_recovery`.
Metrics
=======
Provides :ref:`metrics`.
Runtime Configuration
=====================
Provides a ``config`` endpoint that returns the runtime :ref:`configuration`.
.. code-block:: bash
curl "http://localhost:3001/config"
.. code-block::
db-aggregates-enabled = false
db-anon-role = "web_anon"
db-channel = "pgrst"
db-channel-enabled = false
...
Runtime Schema Cache
====================
Provides the ``schema_cache`` endpoint that prints the runtime :ref:`schema_cache`.
.. code-block:: bash
curl "http://localhost:3001/schema_cache"
.. code-block:: json
{
"dbMediaHandlers": ["..."],
"dbRelationships": ["..."],
"dbRepresentations": ["..."],
"dbRoutines": ["..."],
"dbTables": ["..."],
"dbTimezones": ["..."]
}
+29 -29
View File
@@ -3,14 +3,14 @@
API
###
PostgREST exposes three database objects of a schema as resources: tables, views and stored procedures.
PostgREST exposes three database objects of a schema as resources: tables, views and functions.
.. toctree::
:glob:
:maxdepth: 1
api/tables_views.rst
api/stored_procedures.rst
api/functions.rst
api/schemas.rst
api/computed_fields.rst
api/domain_representations.rst
@@ -30,22 +30,22 @@ PostgREST exposes three database objects of a schema as resources: tables, views
const redirects = {
// Tables and Views
'#horizontal-filtering-rows': 'api/tables_views.html#horizontal-filtering-rows',
'#horizontal-filtering-rows': 'api/tables_views.html#horizontal-filtering',
'#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',
'#vertical-filtering-columns': 'api/tables_views.html#vertical-filtering',
'#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',
'#computed-virtual-columns': 'api/computed_fields.html',
'#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',
'#limits-and-pagination': 'api/pagination_count.html',
'#exact-count': 'api/pagination_count.html#exact-count',
'#planned-count': 'api/pagination_count.html#planned-count',
'#estimated-count': 'api/pagination_count.html#estimated-count',
'#updates': 'api/tables_views.html#update',
'#insertions': 'api/tables_views.html#insert',
'#bulk-insert': 'api/tables_views.html#bulk-insert',
@@ -55,15 +55,15 @@ PostgREST exposes three database objects of a schema as resources: tables, views
'#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',
// Functions
'#stored-procedures': 'api/functions.html',
'#calling-functions-with-a-single-json-parameter': 'api/functions.html#functions-with-a-single-json-parameter',
'#calling-functions-with-a-single-unnamed-parameter': 'api/functions.html#functions-with-a-single-unnamed-parameter',
'#calling-functions-with-array-parameters': 'api/functions.html#functions-with-array-parameters',
'#calling-variadic-functions': 'api/functions.html#variadic-functions',
'#scalar-functions': 'api/functions.html#scalar-functions',
'#function-filters': 'api/functions.html#table-valued-functions',
'#overloaded-functions': 'api/functions.html#overloaded-functions',
// Schemas
'#switching-schemas': 'api/schemas.html',
// Resource Embedding
@@ -76,21 +76,21 @@ PostgREST exposes three database objects of a schema as resources: tables, views
'#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-partitioned-tables': 'api/resource_embedding.html#foreign-key-joins-on-partitioned-tables',
'#embedding-views': 'api/resource_embedding.html#foreign-key-joins-on-views',
'#embedding-chains-of-views': 'api/resource_embedding.html#foreign-key-joins-on-chains-of-views',
'#embedding-on-stored-procedures': 'api/resource_embedding.html#foreign-key-joins-on-table-valued-functions',
'#embedding-after-insertions-updates-deletions': 'api/resource_embedding.html#foreign-key-joins-on-writes',
'#embedding-disambiguation': 'api/resource_embedding.html#foreign-key-joins-on-multiple-foreign-key-relationships',
'#target-disambiguation': 'api/resource_embedding.html#foreign-key-joins-on-multiple-foreign-key-relationships',
'#hint-disambiguation': 'api/resource_embedding.html#foreign-key-joins-on-multiple-foreign-key-relationships',
"#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',
'#response-formats-for-scalar-responses': 'api/functions.html#scalar-functions',
// CORS
'#cors': 'api/cors.html',
// OPTIONS
@@ -109,9 +109,9 @@ PostgREST exposes three database objects of a schema as resources: tables, views
'#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',
'#raise-errors-with-http-status-codes': 'errors.html#raise-errors-with-http-status-codes',
// Admin
'#execution-plan': 'admin.html#execution-plan',
'#execution-plan': 'observability.html#execution-plan',
// Deprecated
'#bulk-call': '../releases/v11.0.1.html#breaking-changes',
};
+19 -73
View File
@@ -12,15 +12,9 @@ PostgREST supports the following aggregate functions: ``avg()``, ``count()``, ``
To use an aggregate function, you append the function to a value in the ``select`` parameter, like so:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /orders?select=amount.sum() HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/orders?select=amount.sum()"
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:
@@ -36,15 +30,9 @@ You can use multiple aggregate functions by just adding more columns with aggreg
To group by other columns, you simply add those columns to the ``select`` parameter. For instance:
.. tabs::
.. code-block:: bash
.. 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"
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``:
@@ -75,15 +63,9 @@ The Case of ``count()``
``count()`` is treated specially, as it can be used without an associated column. Take for example the following query:
.. tabs::
.. code-block:: bash
.. 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"
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``:
@@ -100,7 +82,7 @@ This would return a row for each unique value in the ``order_date`` column, with
}
]
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.
When ``count()`` is used with an associated column, its behavior 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
====================
@@ -124,15 +106,9 @@ For instance, imagine that the ``orders`` table has a JSON column, ``order_detai
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-block:: bash
.. 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()"
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:
@@ -149,15 +125,9 @@ 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-block:: bash
.. 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"
curl "http://localhost:3000/orders?select=amount.avg()::int"
You will then receive the rounded average as the result:
@@ -184,15 +154,9 @@ Using an embedded resource as a grouping column allows you to use data from an a
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-block:: bash
.. 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)"
curl "http://localhost:3000/orders?select=amount.sum(),customers(name)"
You will then get the summed amount, along with the embedded customer resource:
@@ -223,15 +187,9 @@ When embedding a resource, you can apply aggregate functions to columns from the
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-block:: bash
.. 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)"
curl "http://localhost:3000/customers?select=name,city,state,orders(amount.sum(),order_date)"
.. code-block:: json
@@ -280,15 +238,9 @@ 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-block:: bash
.. 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)
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:
@@ -312,15 +264,9 @@ 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-block:: bash
.. 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())
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.
+7 -25
View File
@@ -30,15 +30,9 @@ Horizontal Filtering on Computed Fields
CREATE INDEX people_full_name_idx ON people
USING GIN (to_tsvector('english', full_name(people)));
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/people?full_name=fts.Beckett"
.. code-block:: json
@@ -51,15 +45,9 @@ 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-block:: bash
.. 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"
curl "http://localhost:3000/people?select=full_name,job"
.. code-block:: json
@@ -72,19 +60,13 @@ Ordering on Computed Fields
:ref:`ordering` on computed fields is also possible:
.. tabs::
.. code-block:: bash
.. 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"
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``.
Computed fields 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 field 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 <functions>` under ``/rpc``.
.. note::
+6 -16
View File
@@ -10,22 +10,13 @@ It also handles `preflight requests <https://developer.mozilla.org/en-US/docs/Gl
A ``POST`` preflight request would look like this:
.. tabs::
.. code-block:: bash
.. 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"
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
@@ -47,4 +38,3 @@ With the following config setting, PostgREST will accept CORS requests from doma
.. code-block::
server-cors-allowed-origins="http://example.com, http://example2.com"
+13 -37
View File
@@ -58,17 +58,10 @@ Then create a CAST to tell PostgREST to convert it automatically whenever a JSON
With this you can obtain the data in the shortened format.
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /profiles HTTP/1.1
Accept: application/json
.. code-tab:: bash Curl
curl "http://localhost:3000/profiles" \
-H "Accept: application/json"
curl "http://localhost:3000/profiles" \
-H "Accept: application/json"
.. code-block:: json
@@ -102,17 +95,10 @@ PostgREST considers the URL query string to be, in the most generic sense, ``tex
Now you can filter as usual.
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/profiles?id=eq.ZLOTeeNEY4pkp9OxA==" \
-H "Accept: application/json"
.. code-block:: json
@@ -139,26 +125,16 @@ To accept the shortened format in a JSON request body, for example when creating
Now we can :ref:`insert` (or :ref:`update`) as usual.
.. tabs::
.. code-block:: bash
.. code-tab:: http
curl "http://localhost:3000/profiles" \
-H "Prefer: return=representation" \
-H "Content-Type: application/json" \
-d @- <<JSON
POST /profiles HTTP/1.1
Content-Type: application/json
Prefer: return=representation
{"id":"zH7HbFJUTfy/GZpwuirpuQ==","name":"Jane Doe"}
{"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
JSON
The response:
@@ -1,21 +1,25 @@
.. _s_procs:
.. _functions:
Stored Procedures
=================
Functions as RPC
================
*"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>`_
*"A single resource can be the equivalent of a database function, 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.
Functions can perform any operation allowed by PostgreSQL (read data, modify data, :ref:`raise errors <raise_error>`, and even DDL operations). Every function 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:
If they return table types, functions 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.
- Use :ref:`Resource Embedding <function_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.
.. warning::
`Stored Procedures <https://www.postgresql.org/docs/current/xproc.html>`_ are not supported.
Calling with POST
-----------------
@@ -23,7 +27,7 @@ To supply arguments in an API call, include a JSON object in the request payload
For instance, assume we have created this function in the database.
.. code-block:: plpgsql
.. code-block:: postgres
CREATE FUNCTION add_them(a integer, b integer)
RETURNS integer AS $$
@@ -36,19 +40,11 @@ For instance, assume we have created this function in the database.
The client can call it by posting an object like
.. tabs::
.. code-block:: bash
.. 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 }'
curl "http://localhost:3000/rpc/add_them" \
-X POST -H "Content-Type: application/json" \
-d '{ "a": 1, "b": 2 }'
.. code-block:: json
@@ -67,80 +63,31 @@ 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-block:: bash
.. 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"
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:
.. _function_single_json:
Functions with a single JSON parameter
--------------------------------------
Functions with a single unnamed 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.
If you want the JSON request body to be sent as a single argument, you can create a function with a single unnamed ``json`` or ``jsonb`` parameter.
For this the ``Content-Type: application/json`` header must be included in the request.
.. 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
.. code-block:: postgres
CREATE FUNCTION mult_them(json) RETURNS int AS $$
SELECT ($1->>'x')::int * ($1->>'y')::int
$$ LANGUAGE SQL;
.. tabs::
.. code-block:: bash
.. 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 }'
curl "http://localhost:3000/rpc/mult_them" \
-X POST -H "Content-Type: application/json" \
-d '{ "x": 4, "y": 2 }'
.. code-block:: json
@@ -148,13 +95,24 @@ To send raw JSON, the function must have a single unnamed ``json`` or ``jsonb``
.. 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.
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.
.. warning::
Sending the JSON request body as a single argument is also possible with :ref:`Prefer: params=single-object <prefer_params>` but this method is **deprecated**.
.. _function_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 ``bytea``, ``text`` or ``xml`` data.
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
.. code-block:: postgres
CREATE TABLE files(blob bytea);
@@ -162,20 +120,11 @@ To send raw binary, the parameter type must be ``bytea`` and the header ``Conten
INSERT INTO files(blob) VALUES ($1);
$$ LANGUAGE SQL;
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/rpc/upload_binary" \
-X POST -H "Content-Type: application/octet-stream" \
--data-binary "@file_name.ext"
.. code-block:: http
@@ -185,7 +134,7 @@ To send raw binary, the parameter type must be ``bytea`` and the header ``Conten
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_array:
Functions with array parameters
-------------------------------
@@ -198,20 +147,11 @@ You can call a function that takes an array parameter:
SELECT array_agg(n + 1) FROM unnest($1) AS n;
$$ language sql;
.. tabs::
.. code-block:: bash
.. 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]}'
curl "http://localhost:3000/rpc/plus_one" \
-X POST -H "Content-Type: application/json" \
-d '{"arr": [1,2,3,4]}'
.. code-block:: json
@@ -220,37 +160,23 @@ You can call a function that takes an array parameter:
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-block:: bash
.. 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'"
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-block:: bash
.. 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}" }'
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:
.. _functions_variadic:
Variadic functions
------------------
@@ -263,20 +189,11 @@ You can call a variadic function by passing a JSON array in a POST request:
SELECT array_agg(n + 1) FROM unnest($1) AS n;
$$ language sql;
.. tabs::
.. code-block:: bash
.. 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]}'
curl "http://localhost:3000/rpc/plus_one" \
-X POST -H "Content-Type: application/json" \
-d '{"v": [1,2,3,4]}'
.. code-block:: json
@@ -284,63 +201,36 @@ You can call a variadic function by passing a JSON array in a POST request:
In a GET request, you can repeat the same parameter name:
.. tabs::
.. code-block:: bash
.. 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"
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-block:: bash
.. 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'
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>`.
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 <function_embed>`.
.. code-block:: postgres
CREATE FUNCTION best_films_2017() RETURNS SETOF films ..
.. tabs::
.. code-block:: bash
.. code-tab:: http
curl "http://localhost:3000/rpc/best_films_2017?select=title,director:directors(*)"
GET /rpc/best_films_2017?select=title,director:directors(*) HTTP/1.1
.. code-block:: bash
.. 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"
curl "http://localhost:3000/rpc/best_films_2017?rating=gt.8&order=title.desc"
.. _function_inlining:
@@ -361,19 +251,12 @@ For example, for the following function:
Let's get its :ref:`explain_plan` when calling it with filters applied:
.. tabs::
.. code-block:: bash
.. code-tab:: http
curl "http://localhost:3000/rpc/getallprojects?id=eq.1" \
-H "Accept: application/vnd.pgrst.plan"
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
.. code-block:: postgres
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)
@@ -388,29 +271,17 @@ Scalar functions
PostgREST will detect if the function is scalar or table-valued and will shape the response format accordingly:
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/rpc/add_them?a=1&b=2"
.. code-block:: json
3
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /rpc/best_films_2017 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/best_films_2017"
curl "http://localhost:3000/rpc/best_films_2017"
.. code-block:: json
@@ -435,15 +306,9 @@ Functions that return ``record`` or ``SETOF record`` are supported:
select * from projects;
$$ language sql;
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /rpc/projects_setof_record HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/projects_setof_record"
curl "http://localhost:3000/rpc/projects_setof_record"
.. code-block:: json
@@ -466,25 +331,13 @@ You can call overloaded functions with different number of arguments.
CREATE FUNCTION rental_duration(customer_id integer, from_date date) ..
.. tabs::
.. code-block:: bash
.. code-tab:: http
curl "http://localhost:3000/rpc/rental_duration?customer_id=232"
GET /rpc/rental_duration?customer_id=232 HTTP/1.1
.. code-block:: bash
.. 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"
curl "http://localhost:3000/rpc/rental_duration?customer_id=232&from_date=2018-07-01"
.. important::
+26 -20
View File
@@ -11,7 +11,7 @@ Media types are expressed as type aliases using `domains <https://www.postgresql
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`.
Using these domains, :ref:`functions <functions>` 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::
@@ -95,16 +95,18 @@ Now we'll use it on a new aggregate defined for the ``lines`` table.
, sfunc = twkb_handler_transition
);
Make a quick test on SQL to see it working.
.. note::
.. code-block:: psql
You can test see this aggregate working with:
SELECT twkb_agg(l) from lines l;
.. code-block:: psql
twkb_agg
---------------------------------------------------------------
\xa20002c09a0cc09a0c80ea3080ea30a2000280b51880b51880ea3080ea30
(1 row)
SELECT twkb_agg(l) from lines l;
twkb_agg
---------------------------------------------------------------
\xa20002c09a0cc09a0c80ea3080ea30a2000280b51880b51880ea3080ea30
(1 row)
Now you can request the table endpoint with the ``twkb`` media type:
@@ -184,16 +186,20 @@ Now use the transition and final function as part of the new aggregate.
, finalfunc = bom_csv_final
);
.. code-block:: psql
.. note::
select bom_csv_agg(l) from lines l;
bom_csv_agg
-----------------------------------------------------------------------------------------------------
id,name,geom +
1,line-1,0102000020E610000002000000000000000000F03F000000000000F03F00000000000014400000000000001440+
2,line-2,0102000020E6100000020000000000000000000040000000000000004000000000000018400000000000001840+
You can test this with:
(1 row)
.. 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:
@@ -215,11 +221,11 @@ And request it like:
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:
For more flexibility, you can also define a catch-all handler by using a domain named ``*/*`` (any media type). This handler obeys 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.
- It responds to all media types and even to requests that don't include an ``Accept`` header.
- It sets the ``Content-Type`` header to ``application/octet-stream`` by default, but this can be overridden inside the function with :ref:`guc_resp_hdrs`.
- It 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.
+5 -11
View File
@@ -11,7 +11,7 @@ PostgREST automatically serves a full `OpenAPI <https://www.openapis.org/>`_ des
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
.. code-block:: postgres
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';
@@ -26,7 +26,7 @@ These unsavory comments will appear in the generated JSON as the fields, ``info.
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
.. code-block:: postgres
COMMENT ON TABLE entities IS
$$Entities summary
@@ -37,7 +37,7 @@ Also if you wish to generate a ``summary`` field you can do it by having a multi
Similarly, you can override the API title by commenting the schema.
.. code-block:: plpgsql
.. code-block:: postgres
COMMENT ON SCHEMA api IS
$$FooBar API
@@ -81,15 +81,9 @@ You can override the whole default response with a function result. To do this,
end
$_$ language plpgsql;
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET / HTTP/1.1
.. code-tab:: bash Curl
curl http://localhost:3000
curl http://localhost:3000
.. code-block:: http
+2 -8
View File
@@ -7,15 +7,9 @@ You can verify which HTTP methods are allowed on endpoints for tables and views
For a table named ``people``, OPTIONS would show:
.. tabs::
.. code-block:: bash
.. code-tab:: http
OPTIONS /people HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people" -X OPTIONS -i
curl "http://localhost:3000/people" -X OPTIONS -i
.. code-block:: http
+20 -64
View File
@@ -23,15 +23,9 @@ Query Parameters
One way to request limits and offsets is by using query parameters. For example:
.. tabs::
.. code-block:: bash
.. 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"
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.
@@ -41,19 +35,11 @@ Range Header
You can use headers to specify the range of rows desired.
This request gets the first twenty people:
.. tabs::
.. code-block:: bash
.. 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"
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:
@@ -82,21 +68,12 @@ Exact Count
To get the exact count, use ``Prefer: count=exact``.
.. tabs::
.. code-block:: bash
.. 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"
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
@@ -114,17 +91,10 @@ 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-block:: bash
.. 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"
curl "http://localhost:3000/bigtable?limit=25" -I \
-H "Prefer: count=planned"
.. code-block:: http
@@ -151,17 +121,10 @@ 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-block:: bash
.. 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"
curl "http://localhost:3000/smalltable?limit=25" -I \
-H "Prefer: count=estimated"
.. code-block:: http
@@ -170,17 +133,10 @@ Here's an example. Suppose we set ``db-max-rows=1000`` and ``smalltable`` has 32
If we make a similar request on ``bigtable``, which has 3573458 rows, we would get the planned count:
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/bigtable?limit=25" -I \
-H "Prefer: count=estimated"
.. code-block:: http
+112 -83
View File
@@ -13,6 +13,9 @@ The following preferences are supported.
- ``Prefer: count``. See :ref:`prefer_count`.
- ``Prefer: resolution``. See :ref:`prefer_resolution`.
- ``Prefer: missing``. See :ref:`bulk_insert_default`.
- ``Prefer: max-affected``, See :ref:`prefer_max_affected`.
- ``Prefer: tx``. See :ref:`prefer_tx`.
- ``Prefer: params``. See :ref:`prefer_params`.
.. _prefer_handling:
@@ -23,17 +26,10 @@ The server ignores unrecognized or unfulfillable preferences by default. You can
``handling=strict`` will throw an error if you specify invalid preferences. For instance:
.. tabs::
.. code-block:: bash
.. 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"
curl -i "http://localhost:3000/projects" \
-H "Prefer: handling=strict, foo, bar"
.. code-block:: http
@@ -52,17 +48,10 @@ The server ignores unrecognized or unfulfillable preferences by default. You can
``handling=lenient`` ignores invalid preferences.
.. tabs::
.. code-block:: bash
.. 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"
curl -i "http://localhost:3000/projects" \
-H "Prefer: handling=lenient, foo, bar"
.. code-block:: http
@@ -74,20 +63,13 @@ The server ignores unrecognized or unfulfillable preferences by default. You can
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>`_.
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 time zones in `pg_timezone_names <https://www.postgresql.org/docs/current/view-pg-timezone-names.html>`_.
.. tabs::
.. code-block:: bash
.. 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"
curl -i "http://localhost:3000/timestamps" \
-H "Prefer: timezone=America/Los_Angeles"
.. code-block:: http
@@ -103,19 +85,12 @@ The ``timezone`` preference allows you to change the `PostgreSQL timezone <https
{"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>`).
For an invalid time zone, PostgREST returns values with the default time zone (configured on ``postgresql.conf`` or as a setting on the :ref:`authenticator <roles>`).
.. tabs::
.. code-block:: bash
.. 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"
curl -i "http://localhost:3000/timestamps" \
-H "Prefer: timezone=Jupiter/Red_Spot"
.. code-block:: http
@@ -132,19 +107,12 @@ For an invalid timezone, PostgREST returns values with the default timezone (con
Note that there's no ``Preference-Applied`` in the response.
However, with ``handling=strict``, an invalid timezone preference will throw an :ref:`error <pgrst122>`.
However, with ``handling=strict``, an invalid time zone preference will throw an :ref:`error <pgrst122>`.
.. tabs::
.. code-block:: bash
.. 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"
curl -i "http://localhost:3000/timestamps" \
-H "Prefer: handling=strict, timezone=Jupiter/Red_Spot"
.. code-block:: http
@@ -168,21 +136,12 @@ 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-block:: bash
.. 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"}'
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
@@ -195,31 +154,101 @@ 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-block:: bash
.. code-tab:: http
curl -i "http://localhost:3000/projects" -X POST \
-H "Content-Type: application/json" \
-H "Prefer: return=representation" \
-d '{"id":33, "name": "x"}'
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::
.. code-block:: http
HTTP/1.1 201 Created
Preference-Applied: return=representation
.. code-block:: json
[
{
"id": 33,
"name": "x"
}
]
.. _prefer_tx:
Transaction End Preference
==========================
The ``tx`` preference can be set to specify if the :ref:`transaction <transactions>` will end in a COMMIT or ROLLBACK. This preference is not enabled by default but can be activated with :ref:`db-tx-end`.
.. code-block:: bash
curl -i "http://localhost:3000/projects" -X POST \
-H "Content-Type: application/json" \
-H "Prefer: tx=rollback, return=representation" \
-d '{"name": "Project X"}'
.. code-block:: http
HTTP/1.1 200 OK
Preference-Applied: tx=rollback, return=representation
{"id": 35, "name": "Project X"}
.. _prefer_max_affected:
Max Affected
============
You can set a limit to the amount of resources affected in a request by sending ``max-affected`` preference. This feature works in combination with ``handling=strict`` preference. ``max-affected`` would be ignored with lenient handling. The "affected resources" are the number of rows returned by ``DELETE`` and ``PATCH`` requests. This is also supported through ``RPC`` calls.
To illustrate the use of this preference, consider the following scenario where the ``items`` table contains 14 rows.
.. code-block:: bash
curl -i "http://localhost:3000/items?id=lt.15 -X DELETE \
-H "Content-Type: application/json" \
-H "Prefer: handling=strict, max-affected=10"
.. code-block:: http
HTTP/1.1 400 Bad Request
.. code-block:: json
{
"code": "PGRST124",
"message": "Query result exceeds max-affected preference constraint",
"details": "The query affects 14 rows",
"hint": null
}
.. _prefer_params:
Single JSON object as Function Parameter
----------------------------------------
.. warning::
Using this preference is **deprecated** in favor of :ref:`function_single_json`.
:code:`Prefer: params=single-object` allows sending the JSON request body as the single argument of a :ref:`function <functions>`.
.. code-block:: postgres
CREATE FUNCTION mult_them(param json) RETURNS int AS $$
SELECT (param->>'x')::int * (param->>'y')::int
$$ LANGUAGE SQL;
.. code-block:: bash
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
+224 -283
View File
@@ -57,11 +57,11 @@ For example, consider a database of films and their awards:
language text
);
CREATE TABLE technical_specs(
film_id INT REFERENCES films UNIQUE,
runtime TIME,
camera TEXT,
sound TEXT
create table technical_specs(
film_id int references films(id) primary key,
runtime time,
camera text,
sound text
);
create table roles(
@@ -91,15 +91,9 @@ Many-to-one relationships
Since ``films`` has a **foreign key** to ``directors``, this establishes a many-to-one relationship. This enables us to request all the films and the director for each film.
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /films?select=title,directors(id,last_name) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=title,directors(id,last_name)"
curl "http://localhost:3000/films?select=title,directors(id,last_name)"
.. code-block:: json
@@ -128,15 +122,9 @@ Note that the embedded ``directors`` is returned as a JSON object because of the
Since the table name is plural, we can be more accurate by making it singular with an alias.
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /films?select=title,director:directors(id,last_name) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=title,director:directors(id,last_name)"
curl "http://localhost:3000/films?select=title,director:directors(id,last_name)"
.. code-block:: json
@@ -157,15 +145,9 @@ One-to-many relationships
The **foreign key reference** establishes the inverse one-to-many relationship. In this case, ``films`` returns as a JSON array because of the “to-many” end.
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /directors?select=last_name,films(title) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/directors?select=last_name,films(title)"
curl "http://localhost:3000/directors?select=last_name,films(title)"
.. code-block:: json
@@ -196,7 +178,7 @@ The join table determines many-to-many relationships. It must contain foreign ke
The join table is also detected if the composite key has additional columns.
.. code-block:: postgresql
.. code-block:: postgres
create table roles(
id int generated always as identity,
@@ -206,15 +188,9 @@ The join table is also detected if the composite key has additional columns.
, primary key(id, film_id, actor_id)
);
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /actors?select=first_name,last_name,films(title) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/actors?select=first_name,last_name,films(title)"
curl "http://localhost:3000/actors?select=first_name,last_name,films(title)"
.. code-block:: json
@@ -238,24 +214,18 @@ One-to-one relationships are detected in two ways.
- When the foreign key is a primary key as specified in the :ref:`sample film database <erd_film>`.
- When the foreign key has a unique constraint.
.. code-block:: postgresql
.. code-block:: postgres
CREATE TABLE technical_specs(
film_id INT REFERENCES films UNIQUE,
runtime TIME,
camera TEXT,
sound TEXT
create table technical_specs(
film_id int references films(id) unique,
runtime time,
camera text,
sound text
);
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /films?select=title,technical_specs(camera) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=title,technical_specs(camera)"
curl "http://localhost:3000/films?select=title,technical_specs(camera)"
.. code-block:: json
@@ -276,7 +246,7 @@ You can manually define relationships by using functions. This is useful for dat
Assuming there's a foreign table ``premieres`` that we want to relate to ``films``.
.. code-block:: postgresql
.. code-block:: postgres
create foreign table premieres (
id integer,
@@ -292,15 +262,9 @@ Assuming there's a foreign table ``premieres`` that we want to relate to ``films
The above function defines a relationship between ``premieres`` (the parameter) and ``films`` (the return type). Since there's a ``rows 1``, this defines a many-to-one relationship.
The name of the function ``film`` is arbitrary and can be used to do the embedding:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /premieres?select=location,film(name) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/premieres?select=location,film(name)"
curl "http://localhost:3000/premieres?select=location,film(name)"
.. code-block:: json
@@ -323,15 +287,9 @@ Now let's define the opposite one-to-many relationship.
In this case there's an implicit ``ROWS 1000`` defined by PostgreSQL(`search "result_rows" on this PostgreSQL doc <https://www.postgresql.org/docs/current/sql-createfunction.html>`_).
We consider any value greater than 1 as "many" so this defines a one-to-many relationship.
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /films?select=name,premieres(name) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=name,premieres(name)"
curl "http://localhost:3000/films?select=name,premieres(name)"
.. code-block:: json
@@ -417,15 +375,9 @@ For example, suppose you have the following ``orders`` and ``addresses`` tables:
Since the ``orders`` table has two foreign keys to the ``addresses`` table, a foreign key join is ambiguous and PostgREST will respond with an error:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /orders?select=*,addresses(*) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/orders?select=*,addresses(*)" -i
curl "http://localhost:3000/orders?select=*,addresses(*)" -i
.. code-block:: http
@@ -455,15 +407,12 @@ Since the ``orders`` table has two foreign keys to the ``addresses`` table, a fo
To successfully join ``orders`` with ``addresses``, we can follow the error ``hint`` which tells us to add the foreign key name as ``!billing`` or ``!shipping``.
Note that the foreign keys have been named explicitly in the :ref:`SQL definition above <multiple_m2o>`. To make the result clearer we'll also alias the tables:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/orders?select=name,billing_address:addresses!billing(name),shipping_address:addresses!shipping(name)"
GET /orders?select=name,billing_address:addresses!billing(name),shipping_address:addresses!shipping(name) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/orders?select=name,billing_address:addresses!billing(name),shipping_address:addresses!shipping(name)"
curl --get "http://localhost:3000/orders" \
-d "select=name,billing_address:addresses!billing(name),shipping_address:addresses!shipping(name)"
.. code-block:: json
@@ -486,15 +435,13 @@ Multiple One-To-Many
Let's take the tables from :ref:`multiple_m2o`. To get the opposite one-to-many relationship, we can also specify the foreign key name:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/addresses?select=name,billing_orders:orders!billing(name),shipping_orders!shipping(name)&id=eq.1"
GET /addresses?select=name,billing_orders:orders!billing(name),shipping_orders!shipping(name)&id=eq.1 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/addresses?select=name,billing_orders:orders!billing(name),shipping_orders!shipping(name)&id=eq.1"
curl --get "http://localhost:3000/addresses" \
-d "select=name,billing_orders:orders!billing(name),shipping_orders!shipping(name)" \
-d "id=eq.1"
.. code-block:: json
@@ -538,7 +485,7 @@ Recursive One-To-One
To get either side of the Recursive One-To-One relationship, create the functions:
.. code-block:: postgresql
.. code-block:: postgres
create or replace function predecessor(presidents) returns setof presidents rows 1 as $$
select * from presidents where id = $1.predecessor_id
@@ -550,15 +497,13 @@ To get either side of the Recursive One-To-One relationship, create the function
Now, to query a president with their predecessor and successor:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/presidents?select=last_name,predecessor(last_name),successor(last_name)&id=eq.2"
GET /presidents?select=last_name,predecessor(last_name),successor(last_name)&id=eq.2 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/presidents?select=last_name,predecessor(last_name),successor(last_name)&id=eq.2"
curl --get "http://localhost:3000/presidents" \
-d "select=last_name,predecessor(last_name),successor(last_name)" \
-d "id=eq.2"
.. code-block:: json
@@ -596,7 +541,7 @@ Recursive One-To-Many
To get the One-To-Many embedding, that is, the supervisors with their supervisees, create a function like this one:
.. code-block:: postgresql
.. code-block:: postgres
create or replace function supervisees(employees) returns setof employees as $$
select * from employees where supervisor_id = $1.id
@@ -604,15 +549,13 @@ To get the One-To-Many embedding, that is, the supervisors with their supervisee
Now, the query would be:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/employees?select=last_name,supervisees(last_name)&id=eq.1"
GET /employees?select=last_name,supervisees(last_name)&id=eq.1 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/employees?select=last_name,supervisees(last_name)&id=eq.1"
curl --get "http://localhost:3000/employees" \
-d "select=last_name,supervisees(last_name)" \
-d "id=eq.1"
.. code-block:: json
@@ -634,7 +577,7 @@ Recursive Many-To-One
Let's take the same ``employees`` table from :ref:`recursive_o2m_embed`.
To get the Many-To-One relationship, that is, the employees with their respective supervisor, you need to create a function like this one:
.. code-block:: postgresql
.. code-block:: postgres
create or replace function supervisor(employees) returns setof employees rows 1 as $$
select * from employees where id = $1.supervisor_id
@@ -642,15 +585,13 @@ To get the Many-To-One relationship, that is, the employees with their respectiv
Then, the query would be:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/employees?select=last_name,supervisor(last_name)&id=eq.3"
GET /employees?select=last_name,supervisor(last_name)&id=eq.3 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/employees?select=last_name,supervisor(last_name)&id=eq.3"
curl --get "http://localhost:3000/employees" \
-d "select=last_name,supervisor(last_name)" \
-d "id=eq.3"
.. code-block:: json
@@ -692,7 +633,7 @@ Recursive Many-To-Many
To get all the subscribers of a user as well as the ones they're following, define these functions:
.. code-block:: postgresql
.. code-block:: postgres
create or replace function subscribers(users) returns setof users as $$
select u.*
@@ -712,15 +653,13 @@ To get all the subscribers of a user as well as the ones they're following, defi
Then, the request would be:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/users?select=username,subscribers(username),following(username)&id=eq.4"
GET /users?select=username,subscribers(username),following(username)&id=eq.4 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/users?select=username,subscribers(username),following(username)&id=eq.4"
curl --get "http://localhost:3000/users" \
-d "select=username,subscribers(username),following(username)" \
-d "id=eq.4"
.. code-block:: json
@@ -773,15 +712,13 @@ For example, let's create the ``box_office`` partitioned table that has the gros
Since it contains the ``films_id`` foreign key, it is possible to join ``box_office`` and ``films``:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/box_office?select=bo_date,gross_revenue,films(title)&gross_revenue=gte.1000000"
GET /box_office?select=bo_date,gross_revenue,films(title)&gross_revenue=gte.1000000 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/box_office?select=bo_date,gross_revenue,films(title)&gross_revenue=gte.1000000"
curl --get "http://localhost:3000/box_office" \
-d "select=bo_date,gross_revenue,films(title)" \
-d "gross_revenue=gte.1000000"
.. note::
@@ -814,15 +751,13 @@ For instance, the following view has ``nominations``, ``films`` and ``competitio
Since this view contains ``nominations.film_id``, which has a **foreign key** relationship to ``films``, then we can join the ``films`` table. Similarly, because the view contains ``films.id``, then we can also join the ``roles`` and the ``actors`` tables (the last one in a many-to-many relationship):
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/nominations_view?select=film_title,films(language),roles(character),actors(last_name,first_name)&rank=eq.5"
GET /nominations_view?select=film_title,films(language),roles(character),actors(last_name,first_name)&rank=eq.5 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/nominations_view?select=film_title,films(language),roles(character),actors(last_name,first_name)&rank=eq.5"
curl --get "http://localhost:3000/nominations_view" \
-d "select=film_title,films(language),roles(character),actors(last_name,first_name)" \
-d "rank=eq.5"
It's also possible to foreign key join `Materialized Views <https://www.postgresql.org/docs/current/rules-materializedviews.html>`_.
@@ -843,16 +778,16 @@ Foreign Key Joins on Chains of Views
Views can also depend on other views, which in turn depend on the actual base table. For PostgREST to pick up those chains recursively to any depth, all the views must be in the search path, so either in the exposed schema (:ref:`db-schemas`) or in one of the schemas set in :ref:`db-extra-search-path`. This does not apply to the base table, which could be in a private schema as well. See :ref:`schema_isolation` for more details.
.. _s_proc_embed:
.. _function_embed:
Foreign Key Joins on Table-Valued Functions
===========================================
If you have a :ref:`Stored Procedure <s_procs>` that returns a table type, you can do a Foreign Key join on the result.
If you have a :ref:`Function <functions>` that returns a table type, you can do a Foreign Key join on the result.
Here's a sample function (notice the ``RETURNS SETOF films``).
.. code-block:: plpgsql
.. code-block:: postgres
CREATE FUNCTION getallfilms() RETURNS SETOF films AS $$
SELECT * FROM films;
@@ -860,15 +795,13 @@ Here's a sample function (notice the ``RETURNS SETOF films``).
A request with ``directors`` embedded:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/rpc/getallfilms?select=title,directors(id,last_name)&title=like.*Workers*"
GET /rpc/getallfilms?select=title,directors(id,last_name)&title=like.*Workers* HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/getallfilms?select=title,directors(id,last_name)&title=like.*Workers*"
curl --get "http://localhost:3000/rpc/getallfilms" \
-d "select=title,directors(id,last_name)" \
-d "title=like.*Workers*"
.. code-block:: json
@@ -890,36 +823,20 @@ You can join related database objects after doing :ref:`insert`, :ref:`update` o
Say you want to insert a **film** and then get some of its attributes plus join its **director**.
.. tabs::
.. code-block:: bash
.. code-tab:: http
POST /films?select=title,year,director:directors(first_name,last_name) HTTP/1.1
Prefer: return=representation
{
curl "http://localhost:3000/films?select=title,year,director:directors(first_name,last_name)" \
-H "Prefer: return=representation" \
-d @- << EOF
{
"id": 100,
"director_id": 40,
"title": "127 hours",
"year": 2010,
"rating": 7.6,
"language": "english"
}
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=title,year,director:directors(first_name,last_name)" \
-H "Prefer: return=representation" \
-d @- << EOF
{
"id": 100,
"director_id": 40,
"title": "127 hours",
"year": 2010,
"rating": 7.6,
"language": "english"
}
EOF
}
EOF
Response:
@@ -941,15 +858,9 @@ Nested Embedding
If you want to embed through join tables but need more control on the intermediate resources, you can do nested embedding. For instance, you can request the Actors, their Roles and the Films for those Roles:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /actors?select=roles(character,films(title,year)) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/actors?select=roles(character,films(title,year))"
curl "http://localhost:3000/actors?select=roles(character,films(title,year))"
.. _embed_filters:
@@ -958,77 +869,65 @@ Embedded Filters
Embedded resources can be shaped similarly to their top-level counterparts. To do so, prefix the query parameters with the name of the embedded resource. For instance, to order the actors in each film:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=*,actors(*)&actors.order=last_name,first_name"
GET /films?select=*,actors(*)&actors.order=last_name,first_name HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=*,actors(*)&actors.order=last_name,first_name"
curl --get "http://localhost:3000/films" \
-d "select=*,actors(*)" \
-d "actors.order=last_name,first_name"
This sorts the list of actors in each film but does *not* change the order of the films themselves. To filter the roles returned with each film:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=*,roles(*)&roles.character=in.(Chico,Harpo,Groucho)"
GET /films?select=*,roles(*)&roles.character=in.(Chico,Harpo,Groucho) HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=*,roles(*)&roles.character=in.(Chico,Harpo,Groucho)"
curl --get "http://localhost:3000/films" \
-d "select=*,roles(*)" \
-d "roles.character=in.(Chico,Harpo,Groucho)"
Once again, this restricts the roles included to certain characters but does not filter the films in any way. Films without any of those characters would be included along with empty character lists.
An ``or`` filter can be used for a similar operation:
An ``or`` filter can be used for a similar operation:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=*,roles(*)&roles.or=(character.eq.Gummo,character.eq.Zeppo)"
GET /films?select=*,roles(*)&roles.or=(character.eq.Gummo,character.eq.Zeppo) HTTP/1.1
curl --get "http://localhost:3000/films" \
-d "select=*,roles(*)" \
-d "roles.or=(character.eq.Gummo,character.eq.Zeppo)"
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=*,roles(*)&roles.or=(character.eq.Gummo,character.eq.Zeppo)"
However, this only works for columns inside ``roles``. See :ref:`how to use "or" across multiple resources <or_embed_rels>`.
Limit and offset operations are possible:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=*,actors(*)&actors.limit=10&actors.offset=2"
GET /films?select=*,actors(*)&actors.limit=10&actors.offset=2 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=*,actors(*)&actors.limit=10&actors.offset=2"
curl --get "http://localhost:3000/films" \
-d "select=*,actors(*)" \
-d "actors.limit=10" \
-d "actors.offset=2"
Embedded resources can be aliased and filters can be applied on these aliases:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=*,actors(*)&actors.limit=10&actors.offset=2"
GET /films?select=*,90_comps:competitions(name),91_comps:competitions(name)&90_comps.year=eq.1990&91_comps.year=eq.1991 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=*,90_comps:competitions(name),91_comps:competitions(name)&90_comps.year=eq.1990&91_comps.year=eq.1991"
curl --get "http://localhost:3000/films" \
-d "select=*,90_comps:competitions(name),91_comps:competitions(name)" \
-d "90_comps.year=eq.1990" \
-d "91_comps.year=eq.1991"
Filters can also be applied on nested embedded resources:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /films?select=*,roles(*,actors(*))&roles.actors.order=last_name&roles.actors.first_name=like.*Tom* HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=*,roles(*,actors(*))&roles.actors.order=last_name&roles.actors.first_name=like.*Tom*"
curl "http://localhost:3000/films?select=*,roles(*,actors(*))&roles.actors.order=last_name&roles.actors.first_name=like.*Tom*"
The result will show the nested actors named Tom and order them by last name. Aliases can also be used instead of the resource names to filter the nested tables.
@@ -1039,15 +938,13 @@ Top-level Filtering
By default, :ref:`embed_filters` don't change the top-level resource(``films``) rows at all:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=title,actors(first_name,last_name)&actors.first_name=eq.Jehanne
GET /films?select=title,actors(first_name,last_name)&actors.first_name=eq.Jehanne HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=title,actors(first_name,last_name)&actors.first_name=eq.Jehanne
curl --get "http://localhost:3000/films" \
-d "select=title,actors(first_name,last_name)" \
-d "actors.first_name=eq.Jehanne"
.. code-block:: json
@@ -1073,15 +970,13 @@ By default, :ref:`embed_filters` don't change the top-level resource(``films``)
In order to filter the top level rows you need to add ``!inner`` to the embedded resource. For instance, to get **only** the films that have an actor named ``Jehanne``:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=title,actors!inner(first_name,last_name)&actors.first_name=eq.Jehanne"
GET /films?select=title,actors!inner(first_name,last_name)&actors.first_name=eq.Jehanne HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=title,actors!inner(first_name,last_name)&actors.first_name=eq.Jehanne"
curl --get "http://localhost:3000/films" \
-d "select=title,actors!inner(first_name,last_name)" \
-d "actors.first_name=eq.Jehanne"
.. code-block:: json
@@ -1106,40 +1001,94 @@ Null filtering on the embedded resources can behave the same as ``!inner``. Whil
For example, doing ``actors=not.is.null`` returns the same result as ``actors!inner(*)``:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=title,actors(*)&actors=not.is.null"
GET /films?select=title,actors(*)&actors=not.is.null HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=title,actors(*)&actors=not.is.null"
curl --get "http://localhost:3000/films" \
-d "select=title,actors(*)" \
-d "actors=not.is.null"
The ``is.null`` filter can be used in embedded resources to perform an anti-join. To get all the films that do not have any nominations:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=title,nominations()&nominations=is.null"
GET /films?select=title,nominations()&nominations=is.null HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=title,nominations()&nominations=is.null"
curl --get "http://localhost:3000/films" \
-d "select=title,nominations()" \
-d "nominations=is.null"
Both ``is.null`` and ``not.is.null`` can be included inside the `or` operator. For instance, to get the films that have no actors **or** directors registered yet:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=title,nominations()&nominations=is.null"
GET /films?select=title,actors(*),directors(*)&or=(actors.is.null,directors.is.null) HTTP/1.1
curl --get "http://localhost:3000/films" \
-d select=title,actors(*),directors(*)" \
-d "or=(actors.is.null,directors.is.null)"
.. code-tab:: bash Curl
.. _or_embed_rels:
curl "http://localhost:3000/films?select=title,actors(*),directors(*)&or=(actors.is.null,directors.is.null)"
OR filtering across Embedded Resources
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can also use ``not.is.null`` to make an ``or`` filter across multiple resources.
For instance, to show the films with actors **or** directors named John:
.. code-block:: bash
# curl "http://localhost:3000/films?select=title,actors(),directors()&directors.first_name=eq.John&actors.first_name=eq.John&or=(directors.not.is.null,actors.not.is.null)"
curl --get "http://localhost:3000/films" \
-d "select=title,actors(),directors()" \
-d "directors.first_name=eq.John" \
-d "actors.first_name=eq.John" \
-d "or=(directors.not.is.null,actors.not.is.null)"
.. code-block:: json
[
{ "title": "Pulp Fiction" },
{ "title": "The Thing" },
".."
]
Here, we use :ref:`empty embeds <empty_embed>` because retrieving their info would be restricted by the filters.
For example, the ``directors`` embedding would return ``null`` if its ``first_name`` is not John.
To solve this, you need to add extra embedded resources and use the empty ones for filtering.
From the above example:
.. code-block:: bash
# curl "http://localhost:3000/films?select=title,act:actors(),dir:directors(),actors(first_name),directors(first_name)&dir.first_name=eq.John&act.first_name=eq.John&or=(dir.not.is.null,act.not.is.null)"
curl --get "http://localhost:3000/films" \
# We need to use aliases like "act" and "dir" to filter the empty embeds
-d "select=title,act:actors(),dir:directors(),actors(first_name),directors(first_name)" \
-d "dir.first_name=eq.John" \
-d "act.first_name=eq.John" \
-d "or=(dir.not.is.null,act.not.is.null)"
.. code-block:: json
[
{
"title": "Pulp Fiction",
"actors": [
{ "first_name": "John" },
{ "first_name": "Samuel" },
{ "first_name": "Uma" },
".."
]
"directors": {
"first_name": "Quentin"
}
},
".."
]
.. _empty_embed:
@@ -1150,15 +1099,14 @@ You can leave an embedded resource empty, this helps with filtering in some case
To filter the films by actors but not include them:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=title,actors()&actors.first_name=eq.Jehanne&actors=not.is.null"
GET /films?select=title,actors()&actors.first_name=eq.Jehanne&actors=not.is.null HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=title,actors()&actors.first_name=eq.Jehanne&actors=not.is.null"
curl --get "http://localhost:3000/films" \
-d "select=title,actors()" \
-d "actors.first_name=eq.Jehanne" \
-d "actors=not.is.null"
.. code-block:: json
@@ -1177,15 +1125,13 @@ On :ref:`Many-to-One <many-to-one>` and :ref:`One-to-One <one-to-one>` relations
For example, to arrange the films in descending order using the director's last name.
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=title,directors(last_name)&order=directors(last_name).desc"
GET /films?select=title,directors(last_name)&order=directors(last_name).desc HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=title,directors(last_name)&order=directors(last_name).desc"
curl --get "http://localhost:3000/films" \
-d "select=title,directors(last_name)" \
-d "order=directors(last_name).desc"
.. _spread_embed:
@@ -1194,15 +1140,13 @@ Spread embedded resource
On many-to-one and one-to-one relationships, you can "spread" the embedded resource. That is, remove the surrounding JSON object for the embedded resource columns.
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=title,...directors(director_last_name:last_name)&title=like.*Workers*"
GET /films?select=title,...directors(director_last_name:last_name)&title=like.*Workers* HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=title,...directors(director_last_name:last_name)&title=like.*Workers*"
curl --get "http://localhost:3000/films" \
-d "select=title,...directors(director_last_name:last_name)" \
-d "title=like.*Workers*"
.. code-block:: json
@@ -1217,15 +1161,13 @@ Note that there is no ``"directors"`` object. Also the embed columns can be alia
You can use this to get the columns of a join table in a many-to-many relationship. For instance, to get films and its actors, but including the ``character`` column from the roles table:
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/films?select=title,actors:roles(character,...actors(first_name,last_name))&title=like.*Lighthouse*"
GET /films?select=title,actors:roles(character,...actors(first_name,last_name))&title=like.*Lighthouse* HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/films?select=title,actors:roles(character,...actors(first_name,last_name))&title=like.*Lighthouse*"
curl --get "http://localhost:3000/films" \
-d "select=title,actors:roles(character,...actors(first_name,last_name))" \
-d "title=like.*Lighthouse*"
.. code-block:: json
@@ -1245,4 +1187,3 @@ You can use this to get the columns of a join table in a many-to-many relationsh
.. note::
The spread operator ``...`` is borrowed from the Javascript `spread syntax <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax>`_.
+17 -45
View File
@@ -11,17 +11,10 @@ Response Format
Use the Accept request header to specify the acceptable format (or formats) for the response:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /people HTTP/1.1
Accept: application/json
.. code-tab:: bash Curl
curl "http://localhost:3000/people" \
-H "Accept: application/json"
curl "http://localhost:3000/people" \
-H "Accept: application/json"
.. _builtin_media:
@@ -30,7 +23,7 @@ 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`.
* ``text/csv`` and ``application/json``, for all API endpoints. See :ref:`tables_views` and :ref:`functions`.
* ``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.
@@ -42,17 +35,10 @@ The following vendor media types handlers are also supported.
Any unrecognized media type will throw an error.
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /people HTTP/1.1
Accept: unknown/unknown
.. code-tab:: bash Curl
curl "http://localhost:3000/people" \
-H "Accept: unknown/unknown"
curl "http://localhost:3000/people" \
-H "Accept: unknown/unknown"
.. code-block:: http
@@ -77,17 +63,10 @@ By default PostgREST returns all JSON results in an array, even when there is on
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-block:: bash
.. 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"
curl "http://localhost:3000/items?id=eq.1" \
-H "Accept: application/vnd.pgrst.object+json"
This returns
@@ -129,19 +108,12 @@ By default PostgREST returns all JSON null values. For example, requesting ``/pr
{ "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``:
On large result sets, the unused keys with ``null`` values can waste bandwidth unnecessarily. To remove them, specify ``nulls=stripped`` as a parameter of ``application/vnd.pgrst.array``:
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/projects?id=gt.10" \
-H "Accept: application/vnd.pgrst.array+json;nulls=stripped"
This returns
@@ -164,12 +136,12 @@ The server handles the following request body media types:
* ``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 :ref:`tables_views` this works on ``POST``, ``PATCH`` and ``PUT`` methods. For :ref:`functions`, it works on ``POST`` methods.
For stored procedures there are three additional types:
For functions there are three additional types:
* ``application/octet-stream``
* ``text/plain``
* ``text/xml``
See :ref:`s_proc_single_unnamed`.
See :ref:`function_single_unnamed`.
+16 -41
View File
@@ -42,59 +42,34 @@ GET/HEAD
For GET or HEAD, select the schema with ``Accept-Profile``.
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /items HTTP/1.1
Accept-Profile: tenant2
.. code-tab:: bash Curl
curl "http://localhost:3000/items" \
-H "Accept-Profile: tenant2"
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-block:: bash
.. code-tab:: http
curl "http://localhost:3000/items" \
-X POST -H "Content-Type: application/json" \
-H "Content-Profile: tenant2" \
-d '{...}'
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`.
You can also select the schema for :ref:`functions` 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-block:: bash
.. code-tab:: http
GET /items HTTP/1.1
Accept-Profile: tenant3
{...}
.. code-tab:: bash Curl
curl "http://localhost:3000/items" \
-H "Accept-Profile: tenant3"
curl "http://localhost:3000/items" \
-H "Accept-Profile: tenant3"
.. code-block::
@@ -113,7 +88,7 @@ To add schemas dynamically, you can use :ref:`in_db_config` plus :ref:`config re
- If the schemas' names have a pattern, like a ``tenant_`` prefix, do:
.. code-block:: postgresql
.. code-block:: postgres
create or replace function postgrest.pre_config()
returns void as $$
@@ -125,7 +100,7 @@ To add schemas dynamically, you can use :ref:`in_db_config` plus :ref:`config re
- If there's no name pattern but they're created with a particular role (``CREATE SCHEMA mine AUTHORIZATION joe``), do:
.. code-block:: postgresql
.. code-block:: postgres
create or replace function postgrest.pre_config()
returns void as $$
@@ -137,7 +112,7 @@ To add schemas dynamically, you can use :ref:`in_db_config` plus :ref:`config re
- Otherwise, you might need to create a table that stores the allowed schemas.
.. code-block:: postgresql
.. code-block:: postgres
create table postgrest.config (schemas text);
@@ -150,7 +125,7 @@ To add schemas dynamically, you can use :ref:`in_db_config` plus :ref:`config re
Then each time you add an schema, do:
.. code-block:: postgresql
.. code-block:: postgres
NOTIFY pgrst, 'reload config';
NOTIFY pgrst, 'reload schema';
+221 -462
View File
@@ -3,31 +3,34 @@
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.
All tables and views 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.
For instance the full contents of a table `people` is returned at
.. code-block:: bash
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 resources. This is covered on :ref:`resource_embedding`.
.. _read:
Read
====
For instance the full contents of a table `people` is returned at
.. _head_req:
.. tabs::
GET and HEAD
------------
.. 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`.
Using the GET method, you can retrieve tables and views rows. The default :ref:`res_format` is JSON.
A HEAD method will behave identically to GET except that no response 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).
.. _h_filter:
@@ -36,27 +39,15 @@ Horizontal Filtering
You can filter result rows by adding conditions on columns. For instance, to return people aged under 13 years old:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /people?age=lt.13 HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?age=lt.13"
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-block:: bash
.. 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"
curl "http://localhost:3000/people?age=gte.18&student=is.true"
.. _operators:
@@ -100,13 +91,13 @@ adj :code:`-|-` is adjacent to, e.g. :code:`?range=adj.(
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`
all :code:`ALL` comparison matches all the values in the list, see :ref:`modifiers`
any :code:`ANY` comparison matches any value in the list, see :ref:`modifiers`
============ ======================== ==================================================================================
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:
For more complicated filters you will have to create a new view in the database, or use a function. For instance, here's a view to show "today's stories" including possibly older pinned stories:
.. code-block:: postgresql
.. code-block:: postgres
CREATE VIEW fresh_stories AS
SELECT *
@@ -117,15 +108,9 @@ For more complicated filters you will have to create a new view in the database,
The view will provide a new endpoint:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /fresh_stories HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/fresh_stories"
curl "http://localhost:3000/fresh_stories"
.. _logical_operators:
@@ -134,29 +119,28 @@ 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-block:: bash
.. 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)"
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-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/people?grade=gte.90&student=is.true&or=(age.eq.14,not.and(age.gte.11,age.lte.17))"
GET /people?grade=gte.90&student=is.true&or=(age.eq.14,not.and(age.gte.11,age.lte.17)) HTTP/1.1
curl --get "http://localhost:3000/people" \
-d "grade=gte.90" \
-d "student=is.true" \
-d "or=(age.eq.14,not.and(age.gte.11,age.lte.17))"
.. code-tab:: bash Curl
If the filter value has a :ref:`reserved character <reserved-chars>`, then you need to wrap it in double quotes:
curl "http://localhost:3000/people?grade=gte.90&student=is.true&or=(age.eq.14,not.and(age.gte.11,age.lte.17))"
.. code-block:: bash
curl -g 'http://localhost:3000/survey?or=(age_range.adj."[18,21)",age_range.cs."[30,35]")'
.. _modifiers:
@@ -167,27 +151,15 @@ You may further simplify the logic using the ``any/all`` modifiers of ``eq,like,
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-block:: bash
.. 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*}"
curl -g "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-block:: bash
.. 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}"
curl -g "http://localhost:3000/people?last_name=like(all).{O*,*n}"
.. _pattern_matching:
@@ -205,45 +177,21 @@ 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-block:: bash
.. code-tab:: http
curl "http://localhost:3000/tsearch?my_tsv=fts(french).amusant"
GET /tsearch?my_tsv=fts(french).amusant HTTP/1.1
.. code-block:: bash
.. code-tab:: bash Curl
curl "http://localhost:3000/tsearch?my_tsv=plfts.The%20Fat%20Cats"
curl "http://localhost:3000/tsearch?my_tsv=fts(french).amusant"
.. code-block:: bash
.. tabs::
curl "http://localhost:3000/tsearch?my_tsv=not.phfts(english).The%20Fat%20Cats"
.. code-tab:: http
.. code-block:: bash
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"
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.
@@ -254,15 +202,9 @@ 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-block:: bash
.. 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"
curl "http://localhost:3000/people?select=first_name,age"
.. code-block:: json
@@ -280,15 +222,9 @@ Renaming Columns
You can rename the columns by prefixing them with an alias followed by the colon ``:`` operator.
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/people?select=fullName:full_name,birthDate:birth_date"
.. code-block:: json
@@ -297,36 +233,12 @@ You can rename the columns by prefixing them with an alias followed by the colon
{"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>`__.
To further reduce the data transferred, 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
@@ -335,15 +247,9 @@ You can specify a path for a ``json`` or ``jsonb`` column using the arrow operat
json_data json
);
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/people?select=id,json_data->>blood_type,json_data->phones"
.. code-block:: json
@@ -352,15 +258,9 @@ You can specify a path for a ``json`` or ``jsonb`` column using the arrow operat
{ "id": 2, "blood_type": "O+", "phones": [{"country_code": "43", "number": "512-446-4988"}, {"country_code": "43", "number": "213-891-5979"}] }
]
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/people?select=id,json_data->phones->0->>number"
.. code-block:: json
@@ -371,15 +271,9 @@ You can specify a path for a ``json`` or ``jsonb`` column using the arrow operat
This also works with filters:
.. tabs::
.. code-block:: bash
.. 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-"
curl "http://localhost:3000/people?select=id,json_data->blood_type&json_data->>blood_type=eq.A-"
.. code-block:: json
@@ -391,15 +285,9 @@ This also works with filters:
Note that ``->>`` is used to compare ``blood_type`` as ``text``. To compare with an integer value use ``->``:
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/people?select=id,json_data->age&json_data->age=gt.20"
.. code-block:: json
@@ -408,10 +296,25 @@ Note that ``->>`` is used to compare ``blood_type`` as ``text``. To compare with
{ "id": 12, "age": 30 },
{ "id": 15, "age": 35 }
]
Ordering is also supported:
.. code-block:: bash
curl "http://localhost:3000/people?select=id,json_data->age&order=json_data->>age.desc"
.. code-block:: json
[
{ "id": 15, "age": 35 },
{ "id": 12, "age": 30 },
{ "id": 11, "age": 25 }
]
.. _composite_array_columns:
Composite / Array Columns
-------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~
The arrow operators(``->``, ``->>``) can also be used for accessing composite fields and array elements.
@@ -428,15 +331,13 @@ The arrow operators(``->``, ``->>``) can also be used for accessing composite fi
languages text[]
);
.. tabs::
.. code-block:: bash
.. code-tab:: http
# curl "http://localhost:3000/countries?select=id,location->>lat,location->>long,primary_language:languages->0&location->lat=gte.19"
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"
curl --get "http://localhost:3000/countries" \
-d "select=id,location->>lat,location->>long,primary_language:languages->0" \
-d "location->lat=gte.19"
.. code-block:: json
@@ -457,6 +358,28 @@ The arrow operators(``->``, ``->>``) can also be used for accessing composite fi
CREATE INDEX ON mytable ((to_jsonb(data) -> 'identification' ->> 'registration_number'));
.. _casting_columns:
Casting Columns
~~~~~~~~~~~~~~~
Casting the columns is possible by suffixing them with the double colon ``::`` plus the desired type.
.. code-block:: bash
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"}
]
.. note::
To prevent invalidating :ref:`index_usage`, casting on horizontal filtering is not allowed. To do this, you can use :ref:`computed_cols`.
.. _ordering:
Ordering
@@ -464,69 +387,56 @@ Ordering
The reserved word ``order`` reorders the response rows. It uses a comma-separated list of columns and directions:
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/people?order=age.desc,height.asc"
If no direction is specified it defaults to ascending order:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /people?order=age HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/people?order=age"
curl "http://localhost:3000/people?order=age"
If you care where nulls are sorted, add ``nullsfirst`` or ``nullslast``:
.. tabs::
.. code-block:: bash
.. code-tab:: http
curl "http://localhost:3000/people?order=age.nullsfirst"
GET /people?order=age.nullsfirst HTTP/1.1
.. code-block:: bash
.. 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"
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-block:: bash
.. code-tab:: http
curl "http://localhost:3000/countries?order=location->>lat"
GET /countries?order=location->>lat HTTP/1.1
.. _index_usage:
.. code-tab:: bash Curl
Index Usage
-----------
curl "http://localhost:3000/countries?order=location->>lat"
Indexes work transparently when using horizontal filtering, vertical filtering and ordering. For example, when having:
.. _head_req:
.. code-block:: postgresql
HEAD
----
create index salary_idx on employees (salary);
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).
We can confirm that a filter on employees uses the index by getting the :ref:`explain_plan`.
.. code-block:: bash
curl 'localhost:3000/employees?salary=eq.36000' -H "Accept: application/vnd.pgrst.plan"
Aggregate (cost=9.52..9.54 rows=1 width=144)
-> Bitmap Heap Scan on employees (cost=4.16..9.50 rows=2 width=136)
Recheck Cond: (salary = '$36,000.00'::money)
-> Bitmap Index Scan on salary_idx (cost=0.00..4.16 rows=2 width=0)
Index Cond: (salary = '$36,000.00'::money)
There we can see `"Index Cond" <https://www.pgmustard.com/docs/explain/index-cond>`_, which confirms the index is being used by the query planner.
.. _insert:
@@ -537,45 +447,28 @@ All tables and `auto-updatable views <https://www.postgresql.org/docs/current/sq
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-block:: bash
.. 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" }'
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.
No response body will be returned by default but you can use :ref:`prefer_return` to get the affected resource and :ref:`resource_embedding` to add related resources.
x-www-form-urlencoded
---------------------
URL encoded payloads can be posted with ``Content-Type: application/x-www-form-urlencoded``.
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/people" \
-X POST -H "Content-Type: application/x-www-form-urlencoded" \
-d "name=John+Doe&age=50&weight=80"
.. note::
@@ -595,7 +488,7 @@ URL encoded payloads can be posted with ``Content-Type: application/x-www-form-u
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.
If you want to keep using rules, a workaround is to wrap the view insertion in a function and call it through the :ref:`functions` interface.
For more details, see this `github issue <https://github.com/PostgREST/postgrest/issues/1283>`_.
.. _bulk_insert:
@@ -607,53 +500,30 @@ Bulk insert works exactly like single row insert except that you provide either
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-block:: bash
.. 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
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
.. code-block:: bash
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 }
]
.. 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
EOF
.. _bulk_insert_default:
@@ -674,50 +544,25 @@ Having:
A request:
.. tabs::
.. code-block:: bash
.. 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
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
}
{ "id": 1, "bar": "val1", "baz": 100 },
{ "id": 2, "bar": "val2", "baz": 15 }
]
.. _specify_columns:
@@ -727,38 +572,21 @@ 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-block:: bash
.. 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
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.
@@ -772,21 +600,13 @@ 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-block:: bash
.. code-tab:: http
curl "http://localhost:3000/people?age=lt.13" \
-X PATCH -H "Content-Type: application/json" \
-d '{ "category": "child" }'
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`.
Updates also support :ref:`prefer_return`, :ref:`resource_embedding` and :ref:`v_filter`.
.. warning::
@@ -801,31 +621,18 @@ 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
.. code-block:: bash
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 }
]
.. 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
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>`_.
@@ -839,31 +646,18 @@ 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
.. code-block:: bash
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 }
]
.. 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
EOF
.. _upsert_put:
@@ -872,19 +666,11 @@ PUT
A single row upsert can be done by using :code:`PUT` and filtering the primary key columns with :code:`eq`:
.. tabs::
.. code-block:: bash
.. 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 }'
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.
@@ -895,29 +681,16 @@ Delete
To delete rows in a table, use the DELETE verb plus :ref:`h_filter`. For instance deleting inactive users:
.. tabs::
.. code-block:: bash
.. code-tab:: http
curl "http://localhost:3000/user?active=is.false" -X DELETE
DELETE /user?active=is.false HTTP/1.1
Deletions also support :ref:`prefer_return`, :ref:`resource_embedding` and :ref:`v_filter`.
.. code-tab:: bash Curl
.. code-block:: bash
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"
curl "http://localhost:3000/user?id=eq.1" -X DELETE \
-H "Prefer: return=representation"
.. code-block:: json
@@ -934,29 +707,15 @@ 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-block:: bash
.. code-tab:: http
curl -X PATCH "/users?limit=10&order=id&last_login=lt.2020-01-01" \
-H "Content-Type: application/json" \
-d '{ "status": "inactive" }'
PATCH /users?limit=10&order=id&last_login=lt.2017-01-01 HTTP/1.1
.. code-block:: bash
{ "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"
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.
+11 -42
View File
@@ -12,7 +12,7 @@ URL Grammar
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
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 functions to provide richer endpoints. The most common causes for custom endpoints are
* Table unions
* More complicated joins than those provided by :ref:`resource_embedding`.
@@ -31,15 +31,9 @@ To request this:
Do this:
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/%D9%85%D9%88%D8%A7%D8%B1%D8%AF"
.. _tabs-cols-w-spaces:
@@ -48,15 +42,9 @@ Table / Columns with spaces
You can request table/columns with spaces in them by percent encoding the spaces with ``%20``:
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/Order%20Items?Unit%20Price=lt.200"
.. _reserved-chars:
@@ -67,44 +55,25 @@ If filters include PostgREST reserved characters(``,``, ``.``, ``:``, ``()``) yo
Here ``Hebdon,John`` and ``Williams,Mary`` are values.
.. tabs::
.. code-block:: bash
.. 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)"
curl "http://localhost:3000/employees?name=in.(%22Hebdon,John%22,%22Williams,Mary%22)"
Here ``information.cpe`` is a column name.
.. tabs::
.. code-block:: bash
.. 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*"
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-block:: bash
.. 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)"
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`.
+5 -14
View File
@@ -86,17 +86,10 @@ 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-block:: bash
.. 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"
curl "http://localhost:3000/foo" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiamRvZSIsImV4cCI6MTQ3NTUxNjI1MH0.GYDZV3yM0gqvuEtJmfpplLBXSGYnke_Pvnl0tbKAjB4"
The ``Bearer`` header value can be used with or without capitalization(``bearer``).
@@ -175,9 +168,7 @@ There are at least three types of common critiques against using JWT: 1) against
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.
Another 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.
@@ -186,7 +177,7 @@ PostgREST uses JWT mainly for authentication and authorization purposes and enco
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.
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 function to call immediately after :ref:`user_impersonation` and before the main query itself runs.
.. code:: ini
+51
View File
@@ -0,0 +1,51 @@
.. _cli:
CLI
===
PostgREST provides a CLI with the commands listed below:
Help
----
.. code:: bash
$ postgrest [-h|--help]
Shows all the commands available.
Version
-------
.. code:: bash
$ postgrest [-v|--version]
Prints the PostgREST version.
Example
-------
.. code:: bash
$ postgrest [-e|--example]
Shows example configuration options.
Dump Config
-----------
.. code:: bash
$ postgrest [--dump-config]
Dumps the loaded :ref:`configuration` values, considering the configuration file, environment variables and :ref:`in_db_config`.
Dump Schema
-----------
.. code:: bash
$ postgrest [--dump-schema]
Dumps the schema cache in JSON format.
+33 -12
View File
@@ -80,7 +80,7 @@ You can also configure the server with database settings by using a :ref:`pre-co
PGRST_DB_PRE_CONFIG = "postgrest.pre_config"
.. code-block:: postgresql
.. code-block:: postgres
-- create a dedicated schema, hidden from the API
create schema postgrest;
@@ -121,8 +121,8 @@ It's possible to reload PostgREST's configuration without restarting the server.
.. _config_reloading_signal:
Reload with signal
------------------
Configuration Reload with signal
--------------------------------
To reload the configuration via signal, send a SIGUSR2 signal to the server process.
@@ -132,8 +132,8 @@ To reload the configuration via signal, send a SIGUSR2 signal to the server proc
.. _config_reloading_notify:
Reload with NOTIFY
------------------
Configuration Reload with NOTIFY
--------------------------------
To reload the configuration from within the database, you can use a NOTIFY command.
@@ -161,7 +161,7 @@ admin-server-port
**In-Database** `n/a`
=============== =======================
Specifies the port for the :ref:`health_check` endpoints.
Specifies the port for the :ref:`admin_server`.
.. _app.settings.*:
@@ -176,7 +176,7 @@ 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')`.
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:
@@ -230,7 +230,7 @@ db-channel
**In-Database** `n/a`
=============== =======================
The name of the notification channel that PostgREST uses for :ref:`schema_reloading` and configuration reloading.
The name of the notification channel that PostgREST uses for :ref:`schema_reloading_notify` and :ref:`config_reloading_notify`.
.. _db-channel-enabled:
@@ -292,12 +292,27 @@ 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`.
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 functions **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-hoisted-tx-settings:
db-hoisted-tx-settings
----------------------
=============== ==================================================================================
**Type** String
**Default** statement_timeout, plan_filter.statement_cost_limit, default_transaction_isolation
**Reloadable** Y
**Environment** PGRST_DB_HOISTED_TX_SETTINGS
**In-Database** pgrst.db_hoisted_tx_settings
=============== ==================================================================================
Hoisted settings are allowed to be applied as transaction-scoped function settings. Multiple settings can be added in a comma-separated string, e.g. ``work_mem, statement_timeout``.
.. _db-max-rows:
db-max-rows
@@ -313,7 +328,7 @@ 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.
A hard limit to the number of rows PostgREST will fetch from a view, table, or function. Limits payload size for accidental or malicious requests.
.. _db-plan-enabled:
@@ -424,7 +439,7 @@ 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`.
A schema-qualified function name to call right after the :ref:`tx_settings` are set. See :ref:`pre-request`.
.. _db-prepared-statements:
@@ -487,7 +502,7 @@ db-tx-end
**Default** commit
**Reloadable** N
**Environment** PGRST_DB_TX_END
**In-Database** `n/a`
**In-Database** pgrst.db_tx_end
=============== =================================
Specifies how to terminate the database transactions.
@@ -569,6 +584,10 @@ 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.
.. warning::
Using this setting will only reject tokens with a different audience claim. Tokens **without** audience claim will still be accepted.
.. _jwt-role-claim-key:
jwt-role-claim-key
@@ -676,6 +695,8 @@ log-level
# All the "warn" level events plus all requests (every status code) are logged
log-level = "info"
# All the above plus events for development purposes are logged
log-level = "debug"
Because currently there's no buffering for logging, the levels with minimal logging(``crit/error``) will increase throughput.
+21 -6
View File
@@ -7,11 +7,6 @@ A connection pool is a cache of reusable database connections. It allows serving
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:
@@ -22,6 +17,26 @@ To conserve system resources, PostgREST uses a dynamic connection pool. This ena
- 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.
- For connecting to the database, the :ref:`authenticator <roles>` role is used. You can configure this using :ref:`db-uri`.
Connection Application Name
~~~~~~~~~~~~~~~~~~~~~~~~~~~
PostgREST sets the connection `application_name <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-FALLBACK-APPLICATION-NAME>`_ for all of its used connections.
This is useful for PostgreSQL statistics and logs.
For example, you can query `pg_stat_activity <https://www.postgresql.org/docs/current/monitoring-stats.html#MONITORING-PG-STAT-ACTIVITY-VIEW>`_ to get the PostgREST version:
.. code-block:: postgres
select distinct usename, application_name
from pg_stat_activity
where usename = 'authenticator';
usename | application_name
---------------+--------------------------
authenticator | PostgREST 12.1
Connection lifetime
-------------------
@@ -60,7 +75,7 @@ If the request reaches the timeout, it will be aborted with the following respon
- 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.
- Use :ref:`functions` for combining read and write logic into a single request.
- Increase the :ref:`db-pool` size.
+12 -12
View File
@@ -67,9 +67,11 @@ PostgREST translates `PostgreSQL error codes <https://www.postgresql.org/docs/cu
+--------------------------+-------------------------+---------------------------------+
| 40* | 500 | transaction rollback |
+--------------------------+-------------------------+---------------------------------+
| 53400 | 500 | config limit exceeded |
+--------------------------+-------------------------+---------------------------------+
| 53* | 503 | insufficient resources |
+--------------------------+-------------------------+---------------------------------+
| 54* | 413 | too complex |
| 54* | 500 | too complex |
+--------------------------+-------------------------+---------------------------------+
| 55* | 500 | obj not in prerequisite state |
+--------------------------+-------------------------+---------------------------------+
@@ -175,7 +177,7 @@ Related to the HTTP request elements.
| | | See :ref:`h_filter`, :ref:`operators` and :ref:`ordering`. |
| PGRST100 | | |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst101: | 405 | For :ref:`functions <s_procs>`, only ``GET`` and ``POST`` |
| .. _pgrst101: | 405 | For :ref:`functions <functions>`, only ``GET`` and ``POST`` |
| | | verbs are allowed. Any other verb will throw this error. |
| PGRST101 | | |
+---------------+-------------+-------------------------------------------------------------+
@@ -247,7 +249,7 @@ Related to the HTTP request elements.
| | | ``is.null`` or ``not.is.null`` :ref:`operators <operators>`.|
| PGRST120 | | |
+---------------+-------------+-------------------------------------------------------------+
| .. _pgrst121: | 400 | PostgREST can't parse the JSON objects in RAISE |
| .. _pgrst121: | 500 | PostgREST can't parse the JSON objects in RAISE |
| | | ``PGRST`` error. See :ref:`raise headers <raise_headers>`. |
| PGRST121 | | |
+---------------+-------------+-------------------------------------------------------------+
@@ -261,7 +263,7 @@ Related to the HTTP request elements.
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>`.
Related to a :ref:`schema_cache`. Most of the time, these errors are solved by :ref:`schema_reloading`.
+---------------+-------------+-------------------------------------------------------------+
| Code | HTTP status | Description |
@@ -339,9 +341,9 @@ You can customize the errors by using the `RAISE statement <https://www.postgres
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:
Custom status codes can be done by raising SQL exceptions inside :ref:`functions <functions>`. For instance, here's a saucy function that always responds with an error:
.. code-block:: postgresql
.. code-block:: postgres
CREATE OR REPLACE FUNCTION just_fail() RETURNS void
LANGUAGE plpgsql
@@ -368,7 +370,7 @@ One way to customize the HTTP status code is by raising particular exceptions ac
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
.. code-block:: postgres
RAISE sqlstate 'PT402' using
message = 'Payment Required',
@@ -394,9 +396,9 @@ Returns:
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:
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
.. code-block:: postgres
RAISE sqlstate 'PGRST' USING
message = '{"code":"123","message":"Payment Required","details":"Quota exceeded","hint":"Upgrade your plan"}',
@@ -420,10 +422,8 @@ Returns:
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
.. code-block:: postgres
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**>`.
@@ -1,12 +1,19 @@
.. _admin:
.. _observability:
Admin
#####
Observability
#############
Observability allows measuring a system's current state based on the data it generates, such as logs, metrics, and traces.
.. contents::
:depth: 1
:local:
:backlinks: none
.. _pgrst_logging:
Logging
-------
Logs
====
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.
@@ -15,32 +22,19 @@ PostgREST logs basic request information to ``stdout``, including the authentica
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``.
For diagnostic information about the server itself, PostgREST logs to ``stderr``. It includes the server version and also the version of the connected PostgreSQL.
.. 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.
06/May/2024:08:16:11 -0500: Starting PostgREST 12.1...
06/May/2024:08:16:11 -0500: Attempting to connect to the database...
06/May/2024:08:16:11 -0500: Successfully connected to PostgreSQL 14.10 (Ubuntu 14.10-0ubuntu0.22.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, 64-bit
06/May/2024:08:16:11 -0500: Listening on port 3000
Database Logs
~~~~~~~~~~~~~
-------------
Currently PostgREST doesn't log the SQL commands executed against the underlying database.
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.
@@ -81,12 +75,97 @@ Restart the database and watch the log file in real-time to understand how HTTP
docker run -v "$(pwd)/init.sh":"/docker-entrypoint-initdb.d/init.sh" -d postgres
docker logs -f <container-id>
Server Version
--------------
.. _metrics:
When debugging a problem it's important to verify the running PostgREST version. There are three ways to do this:
Metrics
=======
- Look for the :code:`Server` HTTP response header that is returned on every request.
The ``metrics`` endpoint on the :ref:`admin_server` endpoint provides metrics in `Prometheus text format <https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format>`_.
.. code-block:: bash
curl "http://localhost:3001/metrics"
# HELP pgrst_schema_cache_query_time_seconds The query time in seconds of the last schema cache load
# TYPE pgrst_schema_cache_query_time_seconds gauge
pgrst_schema_cache_query_time_seconds 1.5937927e-2
# HELP pgrst_schema_cache_loads_total The total number of times the schema cache was loaded
# TYPE pgrst_schema_cache_loads_total counter
pgrst_schema_cache_loads_total 1.0
...
Schema Cache Metrics
--------------------
Metrics related to the :ref:`schema_cache`.
pgrst_schema_cache_query_time_seconds
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
======== =======
**Type** Gauge
======== =======
The query time in seconds of the last schema cache load.
pgrst_schema_cache_loads_total
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
========== ==========================
**Type** Counter
**Labels** ``status``: SUCCESS | FAIL
========== ==========================
The total number of times the schema cache was loaded.
Connection Pool Metrics
-----------------------
Metrics related to the :ref:`connection_pool`.
pgrst_db_pool_timeouts_total
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
======== =======
**Type** Counter
======== =======
The total number of pool connection timeouts.
pgrst_db_pool_available
~~~~~~~~~~~~~~~~~~~~~~~
======== =======
**Type** Gauge
======== =======
Available connections in the pool.
pgrst_db_pool_waiting
~~~~~~~~~~~~~~~~~~~~~
======== =======
**Type** Gauge
======== =======
Requests waiting to acquire a pool connection
pgrst_db_pool_max
~~~~~~~~~~~~~~~~~
======== =======
**Type** Gauge
======== =======
Max pool connections.
Traces
======
Server Version Header
---------------------
When debugging a problem it's important to verify the running PostgREST version. For this you can look at the :code:`Server` HTTP response header that is returned on every request.
.. code::
@@ -94,26 +173,6 @@ When debugging a problem it's important to verify the running PostgREST version.
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
@@ -125,18 +184,10 @@ You can enable tracing HTTP requests by setting :ref:`server-trace-header`. Spec
server-trace-header = "X-Request-Id"
.. tabs::
.. code-block:: bash
.. 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"
curl "http://localhost:3000/users" \
-H "X-Request-Id: 123"
.. code::
@@ -151,15 +202,9 @@ 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-block:: bash
.. code-tab:: http
GET /users HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/users" -i
curl "http://localhost:3000/users" -i
.. code::
@@ -186,36 +231,22 @@ 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-block:: bash
.. code-tab:: http
curl "http://localhost:3000/users?select=name&order=id" \
-H "Accept: application/vnd.pgrst.plan"
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
.. code-block:: postgres
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-block:: bash
.. 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"
curl "http://localhost:3000/users?select=name&order=id" \
-H "Accept: application/vnd.pgrst.plan+json"
.. code-block:: json
@@ -265,7 +296,7 @@ However, if you choose to use it in production you can add a :ref:`db-pre-reques
For example, to only allow requests from an IP address to get the execution plans:
.. code-block:: postgresql
.. code-block:: postgres
-- 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()
@@ -284,34 +315,19 @@ For example, to only allow requests from an IP address to get the execution plan
-- set this function on your postgrest.conf
-- db-pre-request = filter_plan_requests
.. raw:: html
.. _health_check:
<script type="text/javascript">
let hash = window.location.hash;
Health Check
------------
const redirects = {
'#health_check': 'health_check.html',
'#server-version': '#server-version-header',
};
You can enable a health check to verify if PostgREST is available for client requests. Also to check the status of its internal state.
let willRedirectTo = redirects[hash];
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.
if (willRedirectTo) {
window.location.href = willRedirectTo;
}
</script>
+23 -39
View File
@@ -3,43 +3,29 @@
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.
PostgREST requires metadata from the database schema to provide a REST API that abstracts SQL details. One example of this is the interface for :ref:`resource_embedding`.
+--------------------------------------------+-------------------------------------------------------------------------------+
| Feature | Required Metadata |
+============================================+===============================================================================+
| :ref:`resource_embedding` | Foreign key constraints |
+--------------------------------------------+-------------------------------------------------------------------------------+
| :ref:`Stored Functions <s_procs>` | Function signature (parameters, return type, volatility and |
| | `overloading <https://www.postgresql.org/docs/current/xfunc-overload.html>`_) |
+--------------------------------------------+-------------------------------------------------------------------------------+
| :ref:`Upserts <upsert>` | Primary keys |
+--------------------------------------------+-------------------------------------------------------------------------------+
| :ref:`Insertions <insert>` | Primary keys (optional: only if the Location header is requested) |
+--------------------------------------------+-------------------------------------------------------------------------------+
| :ref:`OPTIONS requests <options_requests>` | View INSTEAD OF TRIGGERS and primary keys |
+--------------------------------------------+-------------------------------------------------------------------------------+
| :ref:`open-api` | Table columns, primary keys and foreign keys |
+ +-------------------------------------------------------------------------------+
| | View columns and INSTEAD OF TRIGGERS |
+ +-------------------------------------------------------------------------------+
| | Function signature |
+--------------------------------------------+-------------------------------------------------------------------------------+
.. _stale_schema:
Stale Schema Cache
------------------
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.
You can solve this by reloading the cache manually or automatically.
Getting this metadata requires expensive queries. To avoid repeating this work, PostgREST uses a schema cache.
.. _schema_reloading:
Schema Cache Reloading
----------------------
To not let the schema cache go stale (happens when you make changes to the database), you need to reload it.
You can do this with UNIX signals or with PostgreSQL notifications. It's also possible to do this automatically using `event triggers <https://www.postgresql.org/docs/current/event-trigger-definition.html>`_.
.. note::
- Requests will wait until the schema cache reload is done. This to prevent client errors due to an stale schema cache.
- If you are using the :ref:`in_db_config`, a schema cache reload will :ref:`reload the configuration<config_reloading>` as well.
.. _schema_reloading_signals:
Schema Cache Reloading with Unix Signals
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To manually reload the cache without restarting the PostgREST server, send a SIGUSR1 signal to the server process.
.. code:: bash
@@ -56,16 +42,14 @@ For docker you can do:
# or in docker-compose
docker-compose kill -s SIGUSR1 <service>
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
~~~~~~~~~~~~~~~~~~~~~
Schema Cache Reloading with NOTIFY
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PostgREST also allows you to reload its schema cache through PostgreSQL `NOTIFY <https://www.postgresql.org/docs/current/sql-notify.html>`_.
.. code-block:: postgresql
.. code-block:: postgres
NOTIFY pgrst, 'reload schema'
@@ -78,9 +62,9 @@ The ``pgrst`` notification channel is enabled by default. For configuring the ch
Automatic Schema Cache Reloading
--------------------------------
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``.
You can do automatic reloading and forget there is a schema cache. For this use an `event trigger <https://www.postgresql.org/docs/current/event-trigger-definition.html>`_ and ``NOTIFY``.
.. code-block:: postgresql
.. code-block:: postgres
-- Create an event trigger function
CREATE OR REPLACE FUNCTION pgrst_watch() RETURNS event_trigger
@@ -100,7 +84,7 @@ Now, whenever the ``pgrst_watch`` trigger fires, PostgREST will auto-reload the
To disable auto reloading, drop the trigger.
.. code-block:: postgresql
.. code-block:: postgres
DROP EVENT TRIGGER pgrst_watch
@@ -110,7 +94,7 @@ Finer-Grained Event Trigger
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
.. code-block:: postgres
-- watch CREATE and ALTER
CREATE OR REPLACE FUNCTION pgrst_ddl_watch() RETURNS event_trigger AS $$
+29 -46
View File
@@ -5,7 +5,7 @@ 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
.. code-block:: postgres
START TRANSACTION; -- <Access Mode> <Isolation Level>
-- <Transaction-scoped settings>
@@ -21,7 +21,7 @@ The access mode determines whether the transaction can modify the database or no
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
.. code-block:: postgres
CREATE SEQUENCE callcounter_count START 1;
@@ -30,15 +30,9 @@ Modifying the database inside READ ONLY transactions is not possible. PostgREST
Since the ``callcounter`` view modifies the sequence, calling it with GET or HEAD will result in an error:
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /callcounter HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/callcounter"
curl "http://localhost:3000/callcounter"
.. code-block:: http
@@ -64,7 +58,7 @@ The access mode on :ref:`tables_views` is determined by the HTTP method.
Access Mode on Functions
~~~~~~~~~~~~~~~~~~~~~~~~
:ref:`s_procs` additionally depend on the function `volatility <https://www.postgresql.org/docs/current/xfunc-volatility.html>`_.
:ref:`functions` additionally depend on the function `volatility <https://www.postgresql.org/docs/current/xfunc-volatility.html>`_.
.. list-table::
:header-rows: 2
@@ -98,7 +92,7 @@ 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
.. code-block:: postgres
ALTER ROLE webuser SET default_transaction_isolation TO 'repeatable read';
@@ -106,7 +100,7 @@ Every ``webuser`` gets its queries executed with ``default_transaction_isolation
Or to change the isolation level per function call.
.. code-block:: postgresql
.. code-block:: postgres
CREATE OR REPLACE FUNCTION myfunc()
RETURNS text as $$
@@ -124,7 +118,7 @@ PostgREST uses settings tied to the transaction lifetime. These can be used to g
You can get these with ``current_setting``
.. code-block:: postgresql
.. code-block:: postgres
-- request settings use the ``request.`` prefix.
SELECT
@@ -132,7 +126,7 @@ You can get these with ``current_setting``
And you can set them with ``set_config``
.. code-block:: postgresql
.. code-block:: postgres
-- response settings use the ``response.`` prefix.
SELECT
@@ -145,7 +139,7 @@ Request Headers, Cookies and JWT claims
PostgREST stores the headers, cookies and headers as JSON. To get them:
.. code-block:: postgresql
.. code-block:: postgres
-- To get all the headers sent in the request
SELECT current_setting('request.headers', true)::json;
@@ -168,7 +162,7 @@ PostgREST stores the headers, cookies and headers as JSON. To get them:
+ 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
.. code-block:: postgres
CREATE FUNCTION my_current_setting(text) RETURNS text
LANGUAGE SQL AS $$
@@ -182,7 +176,7 @@ Request Path and Method
The path and method are stored as ``text``.
.. code-block:: postgresql
.. code-block:: postgres
SELECT current_setting('request.path', true);
@@ -193,7 +187,7 @@ 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
.. code-block:: postgres
SELECT current_role;
@@ -210,7 +204,7 @@ 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
.. code-block:: postgres
-- tell client to cache response for two days
@@ -246,15 +240,9 @@ You can set the ``response.status`` to override the default status code PostgRES
end;
$$ language plpgsql;
.. tabs::
.. code-block:: bash
.. code-tab:: http
GET /rpc/teapot HTTP/1.1
.. code-tab:: bash Curl
curl "http://localhost:3000/rpc/teapot" -i
curl "http://localhost:3000/rpc/teapot" -i
.. code-block:: http
@@ -277,7 +265,7 @@ 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
.. code-block:: postgres
ALTER ROLE authenticator SET statement_timeout TO '10s';
ALTER ROLE anonymous SET statement_timeout TO '1s';
@@ -292,7 +280,7 @@ For more details see `Understanding Postgres Parameter Context <https://www.ente
However, starting from PostgreSQL 15, you can grant privileges for these settings with:
.. code-block:: postgresql
.. code-block:: postgres
GRANT SET ON PARAMETER <setting> TO <authenticator>;
@@ -302,7 +290,7 @@ 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
.. code-block:: postgres
CREATE OR REPLACE FUNCTION myfunc()
RETURNS void as $$
@@ -311,23 +299,25 @@ the impersonated and connection role settings.
LANGUAGE SQL
SET statement_timeout TO '4s';
When calling the above function (see :ref:`s_procs`), the statement timeout will be 4 seconds.
When calling the above function (see :ref:`functions`), the statement timeout will be 4 seconds.
.. note::
Currently, only ``statement_timeout`` is applied for functions.
Only the transactions that are hoisted by config :ref:`db-hoisted-tx-settings` will be applied.
.. _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`).
The main query is generated by requesting :ref:`tables_views` or :ref:`functions`. All generated queries use prepared statements (:ref:`db-prepared-statements`).
.. _tx_end:
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.
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 the :ref:`prefer_tx`. This is useful for testing purposes.
Aborting transactions
---------------------
@@ -350,7 +340,7 @@ 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
.. code-block:: postgres
create or replace function custom_headers()
returns void as $$
@@ -368,14 +358,7 @@ As an example, let's add some cache headers for all requests that come from an I
Now when you make a GET request to a table or view, you'll get the cache headers.
.. tabs::
.. code-block:: bash
.. 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)"
curl "http://localhost:3000/people" -i \
-H "User-Agent: Mozilla/4.01 (compatible; MSIE 6.0; Windows NT 5.1)"
+4 -2
View File
@@ -1,7 +1,9 @@
docutils==0.20.1
sphinx>=5.0.2
sphinx-copybutton
sphinx-intl
sphinx-rtd-theme>=0.5.1
sphinx-rtd-dark-mode>=1.3.0
sphinx-tabs>=3.2.0
urllib3==2.2.1
sphinx>=5.0.2
sphinxext-opengraph==0.9.1
urllib3==2.2.1
-22
View File
@@ -1,22 +0,0 @@
let
docs =
import ./default.nix;
inherit (docs) pkgs;
in
pkgs.mkShell {
name = "postgrest-docs";
buildInputs = [
docs.build
docs.serve
docs.spellcheck
docs.dictcheck
docs.linkcheck
docs.check
];
shellHook = ''
export HISTFILE=.history
'';
}
+33 -12
View File
@@ -9,9 +9,11 @@ Welcome to PostgREST! In this pre-tutorial we're going to get things running so
PostgREST is a standalone web server which turns a PostgreSQL database into a RESTful API. It serves an API that is customized based on the structure of the underlying database.
.. image:: ../_static/tuts/tut0-request-flow.png
.. container:: img-translucent
To make an API we'll simply be building a database. All the endpoints and permissions come from database objects like tables, views, roles, and stored procedures. These tutorials will cover a number of common scenarios and how to model them in the database.
.. image:: ../_static/tuts/tut0-request-flow.png
To make an API we'll simply be building a database. All the endpoints and permissions come from database objects like tables, views, roles, and functions. These tutorials will cover a number of common scenarios and how to model them in the database.
By the end of this tutorial you'll have a working database, PostgREST server, and a simple single-user todo list API.
@@ -29,11 +31,22 @@ If Docker is not installed, you can get it `here <https://www.docker.com/get-sta
.. code-block:: bash
sudo docker run --name tutorial -p 5433:5432 \
-e POSTGRES_PASSWORD=mysecretpassword \
sudo docker run --name tutorial -p 5432:5432 \
-e POSTGRES_PASSWORD=notused \
-d postgres
This will run the Docker instance as a daemon and expose port 5433 to the host system so that it looks like an ordinary PostgreSQL server to the rest of the system.
This will run the Docker instance as a daemon and expose port 5432 to the host system so that it looks like an ordinary PostgreSQL server to the rest of the system.
.. note::
This only works if there is no other PostgreSQL instance running on the default port on your computer. If this port is already in use, you will receive a message similar to this:
.. code-block:: text
docker: Error response from daemon: [...]: Bind for 0.0.0.0:5432 failed: port is already allocated.
In this case, you will need to change the **first** of the two 5432 to something else, for example to :code:`5433:5432`. Remember to also adjust the port in your config file in Step 5!
Step 3. Install PostgREST
-------------------------
@@ -122,7 +135,7 @@ You should see the psql command prompt:
::
psql (9.6.3)
psql (16.2)
Type "help" for help.
postgres=#
@@ -138,7 +151,7 @@ Our API will have one endpoint, :code:`/todos`, which will come from a table.
.. code-block:: postgres
create table api.todos (
id serial primary key,
id int primary key generated by default as identity,
done boolean not null default false,
task text not null,
due timestamptz
@@ -179,13 +192,17 @@ PostgREST can use a configuration file to tell it how to connect to the database
.. code-block:: ini
db-uri = "postgres://authenticator:mysecretpassword@localhost:5433/postgres"
db-uri = "postgres://authenticator:mysecretpassword@localhost:5432/postgres"
db-schemas = "api"
db-anon-role = "web_anon"
The configuration file has other :ref:`options <configuration>`, but this is all we need.
If you are not using Docker, make sure that your port number is correct and replace `postgres` with the name of the database where you added the todos table.
.. note::
In case you had to adjust the port in Step 2, remember to adjust the port here, too!
Now run the server:
.. code-block:: bash
@@ -196,13 +213,17 @@ Now run the server:
# Running postgrest binary
./postgrest tutorial.conf
You should see
You should see something similar to:
.. code-block:: text
Listening on port 3000
Starting PostgREST 12.0.2...
Attempting to connect to the database...
Connection successful
Listening on port 3000
Config reloaded
Listening for notifications on the pgrst channel
Schema cache loaded
It's now ready to serve web requests. There are many nice graphical API exploration tools you can use, but for this tutorial we'll use :code:`curl` because it's likely to be installed on your system already. Open a new terminal (leaving the one open that PostgREST is running inside). Try doing an HTTP request for the todos.
@@ -242,9 +263,9 @@ Response is 401 Unauthorized:
.. code-block:: json
{
"hint": null,
"details": null,
"code": "42501",
"details": null,
"hint": null,
"message": "permission denied for table todos"
}
+19 -20
View File
@@ -22,33 +22,32 @@ The previous tutorial created a :code:`web_anon` role in the database with which
grant usage on schema api to todo_user;
grant all on api.todos to todo_user;
grant usage, select on sequence api.todos_id_seq to todo_user;
Step 2. Make a Secret
---------------------
Clients authenticate with the API using JSON Web Tokens. These are JSON objects which are cryptographically signed using a password known to only us and the server. Because clients do not know the password, they cannot tamper with the contents of their tokens. PostgREST will detect counterfeit tokens and will reject them.
Clients authenticate with the API using JSON Web Tokens. These are JSON objects which are cryptographically signed using a secret only known to the server. Because clients do not know this secret, they cannot tamper with the contents of their tokens. PostgREST will detect counterfeit tokens and will reject them.
Let's create a password and provide it to PostgREST. Think of a nice long one, or use a tool to generate it. **Your password must be at least 32 characters long.**
Let's create a secret and provide it to PostgREST. Think of a nice long one, or use a tool to generate it. **Your secret must be at least 32 characters long.**
.. note::
Unix tools can generate a nice password for you:
Unix tools can generate a nice secret for you:
.. code-block:: bash
# Allow "tr" to process non-utf8 byte sequences
export LC_CTYPE=C
# read random bytes and keep only alphanumerics
echo "jwt-secret = \"$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c32)\"" >> tutorial.conf
# Read random bytes keeping only alphanumerics and add the secret to the configuration file
echo "jwt-secret = \"$(< /dev/urandom tr -dc A-Za-z0-9 | head -c32)\"" >> tutorial.conf
check the :code:`tutorial.conf` (created in the previous tutorial) the line with the password:
Check that the :code:`tutorial.conf` (created in the previous tutorial) has the secret set in :code:`jwt-secret`:
.. code-block:: ini
.. code-block:: bash
# PASSWORD MUST BE AT LEAST 32 CHARS LONG
# THE SECRET MUST BE AT LEAST 32 CHARS LONG
cat tutorial.conf
If the PostgREST server is still running from the previous tutorial, restart it to load the updated configuration file.
@@ -63,11 +62,11 @@ Ordinarily your own code in the database or in another server will create and si
How to create a token at https://jwt.io
**Remember to fill in the password you generated rather than the word "secret".** After you have filled in the password and payload, the encoded data on the left will update. Copy the encoded token.
**Remember to fill in the secret you generated rather than the word "secret".** After you have filled in the secret and payload, the encoded data on the left will update. Copy the encoded token.
.. note::
While the token may look well obscured, it's easy to reverse engineer the payload. The token is merely signed, not encrypted, so don't put things inside that you don't want a determined client to see.
While the token may look well obscured, it's easy to reverse engineer the payload. The token is merely signed, not encrypted, so don't put things inside that you don't want a determined client to see. While it is possible to read the payload of the token, it is not possible to read the secret with which it was signed.
Step 4. Make a Request
----------------------
@@ -124,7 +123,7 @@ A request for the todos shows three of them, and all completed.
Step 5. Add Expiration
----------------------
Currently our authentication token is valid for all eternity. The server, as long as it continues using the same JWT password, will honor the token.
Currently our authentication token is valid for all eternity. The server, as long as it continues using the same JWT secret, will honor the token.
It's better policy to include an expiration timestamp for tokens using the :code:`exp` claim. This is one of two JWT claims that PostgREST treats specially.
@@ -140,7 +139,7 @@ It's better policy to include an expiration timestamp for tokens using the :code
Epoch time is defined as the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC), January 1st 1970, minus the number of leap seconds that have taken place since then.
To observe expiration in action, we'll add an :code:`exp` claim of five minutes in the future to our previous token. First find the epoch value of five minutes from now. In psql run this:
To observe expiration in action, we'll add an :code:`exp` claim of five minutes in the future to our previous token. First find the epoch value of five minutes from now. In :code:`psql` run this:
.. code-block:: postgres
@@ -155,7 +154,7 @@ Go back to jwt.io and change the payload to
"exp": 123456789
}
**NOTE**: Don't forget to change the dummy epoch value :code:`123456789` in the snippet above to the epoch value returned by the psql command.
**NOTE**: Don't forget to change the dummy epoch value :code:`123456789` in the snippet above to the epoch value returned by the :code:`psql` command.
Copy the updated token as before, and save it as a new environment variable.
@@ -175,9 +174,9 @@ After expiration, the API returns HTTP 401 Unauthorized:
.. code-block:: json
{
"hint": null,
"details": null,
"code": "PGRST301",
"details": null,
"hint": null,
"message": "JWT expired"
}
@@ -203,11 +202,11 @@ Save it to an environment variable:
export WAYWARD_TOKEN="<paste new token>"
PostgREST allows us to specify a stored procedure to run during attempted authentication. The function can do whatever it likes, including raising an exception to terminate the request.
PostgREST allows us to specify a function to run during attempted authentication. The function can do whatever it likes, including raising an exception to terminate the request.
First make a new schema and add the function:
.. code-block:: plpgsql
.. code-block:: postgres
create schema auth;
grant usage on schema auth to web_anon, todo_user;
@@ -255,8 +254,8 @@ The server responds with 403 Forbidden:
.. code-block:: json
{
"hint": "Nope, we are on to you",
"details": null,
"code": "42501",
"details": null,
"hint": "Nope, we are on to you",
"message": "insufficient_privilege"
}
+70
View File
@@ -248,6 +248,27 @@ $ nix-shell --run postgrest-style
There is also `postgrest-style-check` that exits with a non-zero exit code if
the check resulted in any uncommitted changes. It's mostly useful for CI.
## Documentation
The following commands can help you when working on the PostgREST docs:
```bash
# Build the docs
[nix-shell]$ postgrest-docs-build
# Build the docs and start a livereload server on `http://localhost:5500`
[nix-shell]$ postgrest-docs-serve
# Run aspell, to verify spelling mistakes
[nix-shell]$ postgrest-docs-spellcheck
# Detect obsolete entries in postgrest.dict
[nix-shell]$ postgrest-docs-dictcheck
# Build and run all the validation scripts
[nix-shell]$ postgrest-docs-check
```
## General development tools
Tools like `postgrest-build`, `postgrest-run`, `postgrest-repl` etc. are simple wrappers around
@@ -281,6 +302,55 @@ ghci> decodeMediaType "application/json"
MTApplicationJSON
```
## Working with locally modified Haskell packages
Sometimes, we need to modify Haskell libraries in order to debug them or enhance them.
For example, if you want to debug the [`hasql-pool`](https://hackage.haskell.org/package/hasql-pool)
library:
First, copy the package to the repo root. We'll use GitHub in this example.
```bash
$ git clone --depth=1 --branch=0.10.1 https://github.com/nikita-volkov/hasql-pool.git
$ rm -rf ./hasql-pool/.git
```
Then, pin the local package to the [`haskell-packages.nix`](./overlays/haskell-packages.nix) file.
```nix
overrides =
# ...
rec {
# Different subpath may be needed if the cabal file is not in the library's base directory
hasql-pool = lib.dontCheck
(prev.callCabal2nixWithOptions "hasql-pool" ../../hasql-pool "--subpath=." {} );
};
```
Next, both [`cabal.project`](/cabal.project) and [`stack.yaml`](/stack.yaml) need to be updated
with the local library:
```cabal
-- cabal.project
packages:
./hasql-pool/hasql-pool.cabal
```
```yaml
# stack.yaml
extra-deps:
- ./hasql-pool/hasql-pool.cabal
```
Lastly, run `nix-shell` to build the local package. You don't need to exit and
enter the Nix shell every time you modify the library's code, re-executing
`postgrest-run` should be enough.
This is done for development purposes only. Local libraries must not be left
in production ready code.
## Tour
The following is not required for working on PostgREST with Nix, but it will

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