PostgREST一键搭建:把PostgreSQL秒变REST API,零代码写后端

概述

写后端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了没?

这篇文章对你有帮助吗?