Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8200f54ae4 | ||
|
|
5eca8668ef | ||
|
|
30e9733719 | ||
|
|
0830772384 |
@@ -2,55 +2,140 @@
|
|||||||
|
|
||||||
[](https://circleci.com/gh/begriffs/postgrest/tree/master)
|
[](https://circleci.com/gh/begriffs/postgrest/tree/master)
|
||||||
|
|
||||||
### Installation
|
PostgREST serves a fully RESTful API from any existing PostgreSQL
|
||||||
|
database. It provides a cleaner, more standards-compliant, faster
|
||||||
|
API than you are likely to write from scratch.
|
||||||
|
|
||||||
```sh
|
### Demo
|
||||||
brew install postgres
|
|
||||||
cabal install -j --enable-tests
|
Try making requests to the live [demo server] with an HTTP client
|
||||||
|
such as [postman](http://www.getpostman.com/).
|
||||||
|
|
||||||
|
[video placeholder]
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
Download [binaries for your platform] and invoke the program like so:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
postgrest --db-host localhost --db-port 5432 \
|
||||||
|
--db-name my_db --db-user postgres \
|
||||||
|
--db-pass foobar --db-pool 200 \
|
||||||
|
--anonymous postgres --secure \
|
||||||
|
--port 3000
|
||||||
```
|
```
|
||||||
|
|
||||||
Example usage:
|
### Security
|
||||||
|
|
||||||
```sh
|
PostgREST handles authentication (HTTP Basic over SSL) and delegates
|
||||||
cabal run -d [database] -U [auth-role] -a [anonymous-role]
|
authorization to the role information defined in the database. This
|
||||||
```
|
ensures there is a single declarative source of truth for security.
|
||||||
|
When dealing with the database the server assumes the identity of
|
||||||
|
the currently authenticated user, and for the duration of the
|
||||||
|
connection cannot do anything the user themselves couldn't.
|
||||||
|
|
||||||
This will connect to a postgres DB at the url
|
Postgres 9.5 will soon support true [row-level
|
||||||
`postgres://[auth-role]:@localhost:5432/[database]`.
|
security](http://michael.otacoo.com/postgresql-2/postgres-9-5-feature-highlight-row-level-security/).
|
||||||
|
In the meantime what isn't yet implemented can be simulated with
|
||||||
|
triggers and security-barrier views. Because the possible queries
|
||||||
|
to the database are limited to certain templates using
|
||||||
|
[leakproof](http://blog.2ndquadrant.com/how-do-postgresql-security_barrier-views-work/)
|
||||||
|
functions, the trigger workaround does not compromise row-level
|
||||||
|
security.
|
||||||
|
|
||||||
You will need to provide two database roles (which are allowed to
|
For example security patterns see the [security
|
||||||
be the same). One is called the authenticator role (`auth-role`
|
guide](https://github.com/begriffs/postgrest/wiki/Security-and-Permissions).
|
||||||
above) which should have enough privileges to read the `auth` table
|
|
||||||
in the `postgrest` schema if you intend to support multi-user
|
|
||||||
applications.
|
|
||||||
|
|
||||||
The other role is for anonymous access (`anonymous-role` above).
|
### Performance
|
||||||
Immediately upon acceping any unauthenticated HTTP connection postgrest
|
|
||||||
assumes this role in its queries to postgres. Give this role as
|
|
||||||
much or little permissions as you would like.
|
|
||||||
|
|
||||||
### Running tests
|
If you're used to servers written in interpreted languages (or named
|
||||||
|
after precious gems), prepare to be pleasantly surprised by PostgREST
|
||||||
|
performance.
|
||||||
|
|
||||||
```sh
|
Three factors contribute to the speed. First the server is written
|
||||||
createuser --superuser --no-password postgrest_test
|
in [Haskell](https://new-www.haskell.org/) using the
|
||||||
createdb -O postgrest_test -U postgres postgrest_test
|
[Warp](http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks)
|
||||||
|
HTTP server (aka a compiled language with lightweight threads).
|
||||||
|
Next it delegates as much calculation as possible to the database
|
||||||
|
including
|
||||||
|
|
||||||
cabal test --show-details=always --test-options="--color"
|
* Serializing JSON responses directly in SQL
|
||||||
```
|
* Data validation
|
||||||
|
* Authorization
|
||||||
|
* Combined row counting and retrieval
|
||||||
|
* Data post in single command (`returning *`)
|
||||||
|
|
||||||
### Distributing Heroku build
|
Finally it uses the database efficiently with the
|
||||||
|
[Hasql](https://nikita-volkov.github.io/hasql-benchmarks/) library
|
||||||
|
by
|
||||||
|
|
||||||
```sh
|
* Reusing prepared statements
|
||||||
heroku create --stack=cedar --buildpack https://github.com/begriffs/heroku-buildpack-ghc.git
|
* Keeping a pool of db connections
|
||||||
git push heroku master
|
* Using the Postgres binary protocol
|
||||||
|
* Being stateless to allow horizontal scaling
|
||||||
|
|
||||||
heroku config:set S3_ACCESS_KEY=abc
|
Ultimately the server (when load balanced) is constrained by database
|
||||||
heroku config:set S3_SECRET_KEY=123
|
performance. This may make it inappropriate for very large traffic
|
||||||
heroku config:set S3_BUCKET=s3://foo/bar
|
load. To learn more about scaling with Heroku and Amazon RDS see
|
||||||
|
the [performance guide](https://github.com/begriffs/postgrest/wiki/Performance-and-Scaling).
|
||||||
|
|
||||||
heroku run scripts/release_s3.sh
|
Other optimizations are possible, and some are outlined in the
|
||||||
```
|
[Future Features](#future-features).
|
||||||
|
|
||||||
### Acknowledgements
|
### Versioning
|
||||||
|
|
||||||
Thanks to [Adam Baker](https://github.com/adambaker) for code contributions and many fundamental design discussions. Also thanks to [Loop/Recur](https://looprecur.com) for open-source Fridays to advance the code, and for their courage to use this thing in real projects.
|
A robust long-lived API needs the freedom to exist in multiple
|
||||||
|
versions. PostgREST supports versioning through HTTP content
|
||||||
|
negotiation. Requests for a certain version translate into switching
|
||||||
|
which database schema to search for tables. PostgreSQL schema search
|
||||||
|
paths allow tables from earlier versions to be reused verbatim in
|
||||||
|
later versions.
|
||||||
|
|
||||||
|
To learn more, see the [guide to versioning](https://github.com/begriffs/postgrest/wiki/API-Versioning).
|
||||||
|
|
||||||
|
### Self-documention
|
||||||
|
|
||||||
|
Rather than writing and maintaining separate docs yourself let the
|
||||||
|
API explain its own affordances using HTTP. All PostgREST endpoints
|
||||||
|
respond to the OPTIONS verb and explain what they support as well
|
||||||
|
as the data format of their JSON payload.
|
||||||
|
|
||||||
|
The number of rows returned by an endpoint is reported by - and
|
||||||
|
limited with - range headers. More about
|
||||||
|
[that](http://begriffs.com/posts/2014-03-06-beyond-http-header-links.html).
|
||||||
|
|
||||||
|
There are more opportunities for self-documentation listed in [Future
|
||||||
|
Features](#future-features).
|
||||||
|
|
||||||
|
### Data Integrity
|
||||||
|
|
||||||
|
Rather than relying on an Object Relational Mapper and custom
|
||||||
|
imperative coding, this system requires you put declarative constraints
|
||||||
|
directly into your database. Hence no application can corrupt your
|
||||||
|
data (including your API server).
|
||||||
|
|
||||||
|
The PostgREST exposes HTTP interface with safeguards to prevent
|
||||||
|
surprises, such as enforcing idempotent PUT requests, and
|
||||||
|
|
||||||
|
See examples of [Postgres
|
||||||
|
constraints](http://www.tutorialspoint.com/postgresql/postgresql_constraints.htm)
|
||||||
|
and the [guide to routing](https://github.com/begriffs/postgrest/wiki/Routing).
|
||||||
|
|
||||||
|
### Future Features
|
||||||
|
|
||||||
|
* Watching endpoint changes with sockets and Postgres pubsub
|
||||||
|
* Specifying per-view HTTP caching
|
||||||
|
* Inferring good default caching policies from the Postgres stats
|
||||||
|
* Generating mock data to test clients
|
||||||
|
* Maintaining separate connection pools per role to avoid "set/reset
|
||||||
|
role" performance penalty
|
||||||
|
* Describe more relationships with Link headers
|
||||||
|
* Depending on accept headers, render OPTIONS as [RAML] or a
|
||||||
|
relational diagram
|
||||||
|
* Add two-legged auth with OAuth 1.0a(?)
|
||||||
|
* ... the other [issues](https://github.com/begriffs/postgrest/issues)
|
||||||
|
|
||||||
|
### Thanks
|
||||||
|
|
||||||
|
Thanks to [Adam Baker](https://github.com/adambaker) for code
|
||||||
|
contributions and many fundamental design discussions.
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
name: postgrest
|
name: postgrest
|
||||||
version: 0.2.4.7
|
version: 0.2.4.8
|
||||||
synopsis: The database is your api
|
synopsis: The database is your api
|
||||||
license: MIT
|
license: MIT
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
|
|||||||
+9
-7
@@ -173,26 +173,28 @@ isSqlError = Just
|
|||||||
sqlError :: H.Error -> Response
|
sqlError :: H.Error -> Response
|
||||||
sqlError err =
|
sqlError err =
|
||||||
let inside = case err of
|
let inside = case err of
|
||||||
H.CantConnect t -> t
|
H.CantConnect _ ->
|
||||||
|
"Message: \"Cannot connect to postgres server\""
|
||||||
H.ConnectionLost t -> t
|
H.ConnectionLost t -> t
|
||||||
H.ErroneousResult t -> t
|
H.ErroneousResult t -> t
|
||||||
H.UnexpectedResult t -> t
|
H.UnexpectedResult t -> t
|
||||||
H.UnparsableTemplate t -> t
|
H.UnparsableTemplate t -> t
|
||||||
H.UnparsableRow t -> t
|
H.UnparsableRow t -> t
|
||||||
H.NotInTransaction -> "An operation which requires a"
|
H.NotInTransaction -> "An operation which requires a"
|
||||||
<> "database transaction was executed without one"
|
<> "database transaction was executed without one" in
|
||||||
p = parse message
|
|
||||||
"{\"message\": \"failed to parse exception\" }" inside in
|
|
||||||
either
|
either
|
||||||
(\nope ->
|
(\hint ->
|
||||||
responseLBS status500
|
responseLBS status500
|
||||||
[(hContentType, "application/json")]
|
[(hContentType, "application/json")]
|
||||||
(cs . show $ nope))
|
(cs . encode . object $ [
|
||||||
|
("message", String $
|
||||||
|
"Failed to parse exception:" <> inside)
|
||||||
|
, ("hint", String . cs . show $ hint)]))
|
||||||
(\msg ->
|
(\msg ->
|
||||||
responseLBS (httpStatus msg)
|
responseLBS (httpStatus msg)
|
||||||
[(hContentType, "application/json")]
|
[(hContentType, "application/json")]
|
||||||
(encode msg))
|
(encode msg))
|
||||||
p
|
(parse message "" inside)
|
||||||
|
|
||||||
|
|
||||||
rangeStatus :: Int -> Int -> Int -> Status
|
rangeStatus :: Int -> Int -> Int -> Status
|
||||||
|
|||||||
Reference in New Issue
Block a user