概述
写后端API最烦的是什么?建表、写模型、写路由、写鉴权、写CORS、写分页……一堆重复劳动。
PostgREST是个神器。它直接读你的PostgreSQL数据库schema,自动生成完整的REST API。不用写一行后端代码,表就是接口,字段就是参数,关系就是嵌套查询。
这篇文章教你用Docker在VPS上10分钟搭好PostgREST,配合已有的PostgreSQL教程,数据库+API一站式搞定。
准备工作
你需要:
- 一台运行Ubuntu 22.04或24.04的VPS(1核2G以上够用)
- 已安装的PostgreSQL数据库(如果没有,参考PostgreSQL Docker部署教程)
- Docker和Docker Compose
- 一个域名(可选,用于生产环境)
- SSH登录权限
如果你还没有PostgreSQL,可以先跟着PostgreSQL Docker部署教程装一个,或者用本文最后的独立部署方案。
步骤
第一步:准备数据库和表
先登录PostgreSQL,创建一个测试数据库和表:
sudo -u postgres psql
进入psql后执行:
CREATE DATABASE myapp;
\c myapp
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
title VARCHAR(200) NOT NULL,
content TEXT,
published BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW()
);
INSERT INTO users (username, email) VALUES
('alice', 'alice@example.com'),
('bob', 'bob@example.com');
INSERT INTO posts (user_id, title, content, published) VALUES
(1, 'Hello World', '这是我的第一篇文章', true),
(1, 'PostgREST教程', '如何用PostgreSQL快速搭建API', true),
(2, '草稿', '还没写完', false);
退出psql:\q
第二步:创建PostgREST配置文件
在项目目录下创建docker-compose.yml:
mkdir -p ~/postgrest-demo && cd ~/postgrest-demo
version: '3.8'
services:
postgres:
image: postgres:16-alpine
container_name: postgrest-db
restart: unless-stopped
environment:
POSTGRES_DB: myapp
POSTGRES_USER: postgres
POSTGRES_PASSWORD: your-strong-password-here
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- postgrest-net
postgrest:
image: postgrest/postgrest:v12.2.0
container_name: postgrest-api
restart: unless-stopped
depends_on:
- postgres
environment:
PGRST_DB_URI: postgres://postgres:your-strong-password-here@postgres:5432/myapp
PGRST_DB_SCHEMAS: public
PGRST_DB_ANON_ROLE: anon
PGRST_JWT_SECRET: your-jwt-secret-key-at-least-32-chars
PGRST_APP_SETTINGS_JWT_SECRET: your-jwt-secret-key-at-least-32-chars
ports:
- "3000:3000"
networks:
- postgrest-net
volumes:
pgdata:
networks:
postgrest-net:
把your-strong-password-here和your-jwt-secret-key-at-least-32-chars换成你自己的密码和密钥。
第三步:启动服务
docker compose up -d
等几秒让数据库初始化完成:
docker compose logs -f postgrest
看到Starting PostgREST...就说明启动成功了。按Ctrl+C退出日志。
第四步:测试API
PostgREST启动后,所有表自动变成REST接口。试试这些请求:
查询所有用户:
curl http://localhost:3000/users
返回JSON数组,包含alice和bob的记录。
查询单条记录:
curl http://localhost:3000/users?id=eq.1
带条件过滤:
curl "http://localhost:3000/posts?published=eq.true&order=title"
分页查询:
curl "http://localhost:3000/posts?limit=10&offset=0"
嵌套查询(关联表):
curl "http://localhost:3000/posts?select=*,users(username,email)"
创建数据:
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"username":"charlie","email":"charlie@example.com"}'
更新数据:
curl -X PATCH http://localhost:3000/users?id=eq.1 \
-H "Content-Type: application/json" \
-d '{"email":"alice.new@example.com"}'
删除数据:
curl -X DELETE "http://localhost:3000/users?id=eq.2"
第五步:配置JWT鉴权(生产环境必须)
匿名访问太危险了,加上JWT鉴权:
# 生成一个测试JWT token(过期时间1小时)
TOKEN=$(node -e "const jwt=require('jsonwebtoken');console.log(jwt.sign({\"user_name\":\"alice\",\"roles\":[\" anon\"]}, 'your-jwt-secret-key-at-least-32-chars', {expiresIn:'1h'}))")
如果没有node,可以用这个在线方法生成(生产环境请用正规JWT库):
# 或者用python生成测试token
python3 -c "
import hmac, hashlib, base64, time, json
header = base64.urlsafe_b64encode(json.dumps({'alg':'HS256','typ':'JWT'}).encode()).rstrip(b'=').decode()
payload = base64.urlsafe_b64encode(json.dumps({'user_name':'alice','roles':[' anon'],'iat':int(time.time())}).encode()).rstrip(b'=').decode()
signature = base64.urlsafe_b64encode(hmac.new(b'your-jwt-secret-key-at-least-32-chars', f'{header}.{payload}'.encode(), hashlib.sha256).digest()).rstrip(b'=').decode()
print(f'{header}.{payload}.{signature}')
"
带token请求:
curl http://localhost:3000/users \
-H "Authorization: Bearer YOUR_JWT_TOKEN_HERE" \
-H "Prefer: return=representation"
第六步:配置CORS(前端调用必备)
如果前端和API不在同一个域名,需要配置CORS。创建postgrest.conf:
cat > postgrest.conf << 'EOF'
db-uri = "postgres://postgres:your-strong-password-here@postgres:5432/myapp"
db-schema = "public"
db-anon-role = "anon"
jwt-secret = "your-jwt-secret-key-at-least-32-chars"
server-host = "0.0.0.0"
server-port = 3000
cors-origin = "*"
max-rows = "1000"
EOF
然后修改docker-compose.yml,把配置文件挂载进去:
postgrest:
image: postgrest/postgrest:v12.2.0
volumes:
- ./postgrest.conf:/postgrest.conf
command: ["postgrest", "/postgrest.conf"]
重启服务:
docker compose down && docker compose up -d
第七步:配置数据库行级安全(RLS)
PostgREST推荐使用PostgreSQL的RLS(Row Level Security)来做细粒度权限控制:
sudo -u postgres psql -d myapp
-- 创建anon角色
CREATE ROLE anon NOLOGIN;
-- 启用RLS
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- 允许anon角色查询所有记录
CREATE POLICY "anon_select_all" ON users FOR SELECT TO anon USING (true);
CREATE POLICY "anon_select_all_posts" ON posts FOR SELECT TO anon USING (true);
-- 只有authenticated用户可以写
CREATE POLICY "auth_insert_user" ON users FOR INSERT TO authenticated WITH CHECK (true);
CREATE POLICY "auth_update_user" ON users FOR UPDATE TO authenticated USING (true);
CREATE POLICY "auth_delete_user" ON users FOR DELETE TO authenticated USING (true);
-- posts同理
CREATE POLICY "auth_insert_post" ON posts FOR INSERT TO authenticated WITH CHECK (true);
CREATE POLICY "auth_update_post" ON posts FOR UPDATE TO authenticated USING (true);
CREATE POLICY "auth_delete_post" ON posts FOR DELETE TO authenticated USING (true);
验证
用这几个命令确认一切正常:
# 1. 服务状态
docker compose ps
# 2. 健康检查
curl -s http://localhost:3000/ | head -c 100
# 3. 查询测试
curl -s http://localhost:3000/users | python3 -m json.tool
# 4. 关联查询测试
curl -s "http://localhost:3000/posts?select=*,users(username)" | python3 -m json.tool
# 5. 计数验证
curl -s "http://localhost:3000/users?count=exact" -H "Prefer: count=exact"
全部正常返回JSON数据就说明API搭建成功了。
常见问题
Q: 启动后报"role anon does not exist"?
PostgREST需要一个特殊的anon角色来代表匿名用户。在PostgreSQL里执行:
CREATE ROLE anon NOLOGIN;
GRANT USAGE ON SCHEMA public TO anon;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO anon;
Q: 查询返回空数组但表里有数据?
检查PGRST_DB_SCHEMAS环境变量是否包含了你的表所在的schema。默认是public。如果你的表在别的schema里,需要加上:
PGRST_DB_SCHEMAS: public,my_schema
Q: 怎么让API只暴露部分表?
PostgREST默认暴露schema下所有表。不想暴露某些表?在PostgreSQL里把表移到专门的schema:
CREATE SCHEMA api_private;
ALTER TABLE sensitive_data SET SCHEMA api_private;
然后配置只暴露public schema:PGRST_DB_SCHEMAS=public
Q: 支持实时订阅(Realtime)吗?
支持。PostgREST自带实时功能,配合PostgreSQL的LISTEN/NOTIFY机制。前端可以用WebSocket订阅数据变更:
const ws = new WebSocket('ws://localhost:3000/realtime');
ws.onmessage = (event) => console.log(event.data);
Q: 性能怎么样?能扛住高并发吗?
PostgREST基于Haskell,单进程性能很强。配合PgBouncer连接池,轻松扛住每秒几百到几千请求。如果流量很大,建议加一层Nginx反向代理做缓存。
Q: 怎么加自定义API端点?
PostgREST支持数据库函数映射为API端点。创建一个存储过程:
CREATE OR REPLACE FUNCTION get_user_count()
RETURNS TABLE(count bigint) AS $$
SELECT count(*) FROM users;
$$ LANGUAGE sql STABLE;
然后就能用GET /rpc/get_user_count调用了。
Q: 支持GraphQL吗?
原生不支持。如果需要GraphQL,可以在PostgREST外面包一层Hasura或者Prisma,或者直接上PostgREST的GraphQL层。
总结
PostgREST最厉害的地方在于"零代码"——你建好表,API就自动有了。增删改查、分页、过滤、排序、关联查询全部原生支持。对于内部工具、后台管理系统、移动端API,简直是福音。
配合现有的PostgreSQL教程,你现在有了从数据库到API的完整自托管方案。不需要Node.js、不需要Python、不需要任何后端框架。
下一步可以试试加上JWT鉴权,接入前端框架,或者配合Nginx做反向代理加SSL。
你的PostgreSQL数据库准备好变身API了没?