
PostgREST 是一个为 PostgreSQL 数据库提供 REST API 的后端服务, 是用 Haskell 开发的开源软件,源代码非常值得学习。有了 PostgREST,搭配 PostgreSQL,只要建好表,加上一个简单的配置,就可以拥有一个 REST API 后端,简直不要太爽。
- 官方文档:https://docs.postgrest.org/en/v12/index.html
- GitHub:https://github.com/PostgREST/postgrest
安装
PostgREST 的安装十分简单,只需要在 GitHub 的 release 页面下载对应的可执行文件就行,下载过来是压缩包,解压之后会得到一个可执行文件。
虽然 PostgREST 是以单个二进制文件形式发布的,但是它需要依赖 PostgreSQL 的库,所以最好还是先安装一下 PostgreSQL,然后将 bin
目录(PostgreSQL\13\bin
)添加到 path
环境变量。
最后我们在命令行中运行 postgrest -v
看到输出版本号,就说明 OK 了。
>postgrest -v
PostgREST 12.2.8 (4a3936f)
如果缺少 PostgreSQL 库,运行 postgrest
命令不会有任何输出,也没有报错,我这个版本是这样。
准备数据
现在让我们在数据库中准备一些数据用于测试。打开 psql
,登录到你的数据库,这里要用 root 账号去登录。
第一步是创建 scheme 和表。如果把表比作文件的话,那么 scheme 就相当于文件夹。虽然创建 schema 并不是一定必须的,但是它有利于我们的公私数据隔离和权限管控。如果不创建 schema,那么所有表都会在默认的 public
schema 下。
create schema api;
create table api.todos (
id int primary key generated by default as identity,
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');
我们创建了一个交 api
的 schema,并在它下面创建了一个 todos
表,并插入了两条数据。
然后我们要创建两个角色。第一个角色我们称为“陌生人”角色,是给未登录的用户来查询数据的。注意这里的“登录”不是指登录数据库,而是登录你的网站。PostgREST 是使用 JWT 来验证权限的,所有不带 jwt token 的请求都会切换到这个“陌生人”角色去查数据库,那么它能看到什么数据,就完全取决于这个“陌生人”角色所具有的权限,一般是只给一些公开表的只读权限。
create role web_anon nologin;
grant usage on schema api to web_anon;
grant select on api.todos to web_anon;
这里我们创建了一个叫 web_anon
的角色,然后授予它使用 api
schema 和查询 todos
表的权限。
接下来还要创建一个用来连接数据库的角色,它才是给 PostgREST 用来登录数据的。
create role authenticator noinherit login password 'mysecretpassword';
grant web_anon to authenticator;
这里我们创建了一个叫 authenticator
的角色用来连接数据库,注意它一定要给 login
的权限。然后我们授予它 web_anon
的权限,这样当未登录的用户发来请求时,PostgREST 才能从 authenticator
角色切换到 web_anon
来执行 SQL。PostgREST 的权限管理正是通过不同的 PostgreSQL 角色来实现的。当然如果你选择直接用超级用户连接数据库的话,这一步就可以省了,不过不建议生产环境这么玩。
配置
程序、数据都准备好了,要启动服务我们还需要一个配置文件。我们创建一个叫 toturial.conf
的文件,然后输入以下内容。
db-uri = "postgres://authenticator:mysecretpassword@localhost/postgres"
db-schema = "api"
db-anon-role = "web_anon"
这就是最简单的配置了,只需要指定数据库连接地址,schema 和“陌生人”角色就可以了。如果是比较老的 PostgreSQL,可能不支持这种 url 形式,而是键值对。
见证奇迹
最后一步,在命令行运行:
> postgrest toturial.conf
14/Mar/2025:10:29:26 +0800: Starting PostgREST 12.2.8 (4a3936f)...
14/Mar/2025:10:29:26 +0800: Listening on port 3000
14/Mar/2025:10:29:26 +0800: Successfully connected to PostgreSQL 13.20, compiled by Visual C++ build 1942, 64-bit
14/Mar/2025:10:29:26 +0800: Config reloaded
14/Mar/2025:10:29:26 +0800: Listening for notifications on the "pgrst" channel
14/Mar/2025:10:29:26 +0800: Schema cache queried in 0.0 milliseconds
14/Mar/2025:10:29:26 +0800: Schema cache loaded 1 Relations, 0 Relationships, 0 Functions, 0 Domain Representations, 4 Media Type Handlers, 597 Timezones
14/Mar/2025:10:29:26 +0800: Schema cache loaded in 0.0 milliseconds
如果一切正常,你会看到服务在 3000
端口启动,访问 http://localhost:3000/todos
就能看到我们之前准备的数据了,是一个 json 列表。
在命令行输入 postgrest -e
可以会打印出一份完整的配置, -e
是 --example
的简短形式。
>postgrest -e
## Admin server used for checks. It's disabled by default unless a port is specified.
# admin-server-port = 3001
## The database role to use when no client authentication is provided
# db-anon-role = "anon"
## Notification channel for reloading the schema cache
db-channel = "pgrst"
## Enable or disable the notification channel
db-channel-enabled = true
## Enable in-database configuration
db-config = true
## Function for in-database configuration
## db-pre-config = "postgrest.pre_config"
## Extra schemas to add to the search_path of every request
db-extra-search-path = "public"
## Limit rows in response
# db-max-rows = 1000
## Allow getting the EXPLAIN plan through the `Accept: application/vnd.pgrst.plan` header
# db-plan-enabled = false
## Number of open connections in the pool
db-pool = 10
## Time in seconds to wait to acquire a slot from the connection pool
# db-pool-acquisition-timeout = 10
## Time in seconds after which to recycle pool connections
# db-pool-max-lifetime = 1800
## Time in seconds after which to recycle unused pool connections
# db-pool-max-idletime = 30
## Allow automatic database connection retrying
# db-pool-automatic-recovery = true
## Stored proc to exec immediately after auth
# db-pre-request = "stored_proc_name"
## Enable or disable prepared statements. disabling is only necessary when behind a connection pooler.
## When disabled, statements will be parametrized but won't be prepared.
db-prepared-statements = true
## The name of which database schema to expose to REST clients
db-schemas = "public"
## How to terminate database transactions
## Possible values are:
## commit (default)
## Transaction is always committed, this can not be overriden
## commit-allow-override
## Transaction is committed, but can be overriden with Prefer tx=rollback header
## rollback
## Transaction is always rolled back, this can not be overriden
## rollback-allow-override
## Transaction is rolled back, but can be overriden with Prefer tx=commit header
db-tx-end = "commit"
## The standard connection URI format, documented at
## https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
db-uri = "postgresql://"
# jwt-aud = "your_audience_claim"
## Jspath to the role claim key
jwt-role-claim-key = ".role"
## Choose a secret, JSON Web Key (or set) to enable JWT auth
## (use "@filename" to load from separate file)
# jwt-secret = "secret_with_at_least_32_characters"
jwt-secret-is-base64 = false
## Enables and set JWT Cache max lifetime, disables caching with 0
# jwt-cache-max-lifetime = 0
## Logging level, the admitted values are: crit, error, warn, info and debug.
log-level = "error"
## Determine if the OpenAPI output should follow or ignore role privileges or be disabled entirely.
## Admitted values: follow-privileges, ignore-privileges, disabled
openapi-mode = "follow-privileges"
## Base url for the OpenAPI output
openapi-server-proxy-uri = ""
## Configurable CORS origins
# server-cors-allowed-origins = ""
server-host = "!4"
server-port = 3000
## Allow getting the request-response timing information through the `Server-Timing` header
server-timing-enabled = false
## Unix socket location
## if specified it takes precedence over server-port
# server-unix-socket = "/tmp/pgrst.sock"
## Unix socket file mode
## When none is provided, 660 is applied by default
# server-unix-socket-mode = "660"
有些配置我们会在后面遇到的时候再详细解释,但是大部分应该能一眼猜出大概意思。比如 server-port = 3000
可以用来设置服务的端口,默认是 3000
端口。