diff --git a/admin.rst b/admin.rst index 6b290105e..e3cd25168 100644 --- a/admin.rst +++ b/admin.rst @@ -278,11 +278,22 @@ As discussed in `Singular or Plural`_, there are no special URL forms for singul GET /people?id=eq.1 Accept: application/vnd.pgrst.object+json -This allows compound primary keys and makes the intent for singular response independent of a URL convention. However for any table which uses a simple primary key you can use Nginx to simulate the familiar URL convention. +This allows compound primary keys and makes the intent for singular response independent of a URL convention. + +Nginx rewrite rules allow you to simulate the familiar URL convention. The following example adds a rewrite rule for all table endpoints, but you'll want to restrict it to those tables that have a numeric simple primary key named "id." .. code:: nginx - nginx code here + # support /endpoint/:id url style + location ~ ^/([a-z_]+)/([0-9]+) { + + # make the response singular + proxy_set_header Accept 'application/vnd.pgrst.object+json'; + + # assuming an upstream named "postgrest" + proxy_pass http://postgrest/$1?id=eq.$2; + + } .. TODO .. Administration diff --git a/api.rst b/api.rst index 46b9411ee..7595c33fa 100644 --- a/api.rst +++ b/api.rst @@ -57,7 +57,26 @@ not negates another operator, see below To negate any operator, prefix it with :code:`not` like :code:`?a=not.eq.2`. -For more complicated filters (such as those involving condition 1 OR condition 2) you will have to create a new view in the database. +For more complicated filters (such as those involving disjunctions) you will have to create a new view in the database, or use a stored procedure. For instance, here's a view to show "today's stories" including possibly older pinned stories: + +.. code-block:: postgresql + + CREATE VIEW fresh_stories AS + SELECT * + FROM stories + WHERE pinned = true + OR published > now() - interval '1 day' + ORDER BY pinned DESC, published DESC; + +The view will provide a new endpoint: + +.. code-block:: http + + GET /fresh_stories HTTP/1.1 + +.. note:: + + We're working to extend the PostgREST query grammar to allow more complicated boolean logic, while continuing to prevent performance problems from arbitrary client queries. .. _v_filter: @@ -238,6 +257,8 @@ This returns { "id": 1 } +When a singular response is requested but no entries are found, the server responds with an empty body and 404 status code rather than the usual empty array and 200 status. + .. note:: Many APIs distinguish plural and singular resources using a special nested URL convention e.g. `/stories` vs `/stories/1`. Why do we use `/stories?id=eq.1`? The answer is because a singlular resource is (for us) a row determined by a primary key, and primary keys can be compound (meaning defined across more than one column). The more familiar nested urls consider only a degenerate case of simple and overwhelmingly numeric primary keys. These so-called artificial keys are often introduced automatically by Object Relational Mapping libraries. @@ -374,7 +395,7 @@ The client can call it by posting an object like { "a": 1, "b": 2 } -The keys of the object match the parameter names. Note that PostgreSQL converts parameter names to lowercase unless you quote them like :sql:`CREATE FUNCTION foo("mixedCase" text) ...`. +The keys of the object match the parameter names. Note that PostgreSQL converts parameter names to lowercase unless you quote them like :sql:`CREATE FUNCTION foo("mixedCase" text) ...`. 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. .. note:: @@ -501,3 +522,66 @@ You can use a tool like `Swagger UI `_ to create .. note:: The OpenAPI information can go out of date as the schema changes under a running server. To learn how to refresh the cache see :ref:`schema_reloading`. + +HTTP Status Codes +================= + +PostgREST translates `PostgreSQL error codes `_ into HTTP status as follows: + ++--------------------------+-------------------------+---------------------------------+ +| PostgreSQL error code(s) | HTTP status | Error description | ++==========================+=========================+=================================+ +| 08* | 503 | pg connection err | ++--------------------------+-------------------------+---------------------------------+ +| 09* | 500 | triggered action exception | ++--------------------------+-------------------------+---------------------------------+ +| 0L* | 403 | invalid grantor | ++--------------------------+-------------------------+---------------------------------+ +| 0P* | 403 | invalid role specification | ++--------------------------+-------------------------+---------------------------------+ +| 23503 | 409 | foreign key violation | ++--------------------------+-------------------------+---------------------------------+ +| 23505 | 409 | uniqueness violation | ++--------------------------+-------------------------+---------------------------------+ +| 25* | 500 | invalid transaction state | ++--------------------------+-------------------------+---------------------------------+ +| 28* | 403 | invalid auth specification | ++--------------------------+-------------------------+---------------------------------+ +| 2D* | 500 | invalid transaction termination | ++--------------------------+-------------------------+---------------------------------+ +| 38* | 500 | external routine exception | ++--------------------------+-------------------------+---------------------------------+ +| 39* | 500 | external routine invocation | ++--------------------------+-------------------------+---------------------------------+ +| 3B* | 500 | savepoint exception | ++--------------------------+-------------------------+---------------------------------+ +| 40* | 500 | transaction rollback | ++--------------------------+-------------------------+---------------------------------+ +| 53* | 503 | insufficient resources | ++--------------------------+-------------------------+---------------------------------+ +| 54* | 413 | too complex | ++--------------------------+-------------------------+---------------------------------+ +| 55* | 500 | obj not in prereq state | ++--------------------------+-------------------------+---------------------------------+ +| 57* | 500 | operator intervention | ++--------------------------+-------------------------+---------------------------------+ +| 58* | 500 | system error | ++--------------------------+-------------------------+---------------------------------+ +| F0* | 500 | conf file error | ++--------------------------+-------------------------+---------------------------------+ +| HV* | 500 | foreign data wrapper error | ++--------------------------+-------------------------+---------------------------------+ +| P0001 | 400 | default code for "raise" | ++--------------------------+-------------------------+---------------------------------+ +| P0* | 500 | PL/pgSQL error | ++--------------------------+-------------------------+---------------------------------+ +| XX* | 500 | internal error | ++--------------------------+-------------------------+---------------------------------+ +| 42883 | 404 | undefined function | ++--------------------------+-------------------------+---------------------------------+ +| 42P01 | 404 | undefined table | ++--------------------------+-------------------------+---------------------------------+ +| 42501 | if authed 403, else 401 | insufficient privileges | ++--------------------------+-------------------------+---------------------------------+ +| other | 500 | | ++--------------------------+-------------------------+---------------------------------+