REST API
Every table you create gets a full REST API automatically. No code generation, no config — just create a table and start querying.
Endpoints are auto-generated for all tables except users and sessions, which are managed by the auth system.
Row-level scoping
If a table has a user_id (or userId) column, Based automatically scopes all CRUD operations to the authenticated user. No policy language, no configuration — add the column, get isolation.
| Operation | Behavior with user_id column |
|---|---|
| GET list (auth) | Only returns the caller's own rows |
| GET list (anon) | Rejected (403) |
| GET :id | 404 if the row belongs to another user |
| POST | user_id is forced to the caller, ignoring any client value |
| PUT :id | Scoped to caller; user_id is immutable |
| DELETE :id | 404 for rows belonging to another user |
For shared/public tables, simply omit the user_id column — all authenticated users see everything, and anon reads work.
GET /api/:table
List records with optional pagination and filtering.
# Paginated list
curl "https://my-app.based.yourdomain.com/api/posts?limit=10&offset=0" \
-H "apikey: <anonKey>"
# With filtering
curl "https://my-app.based.yourdomain.com/api/posts?status=draft&limit=5" \
-H "apikey: <anonKey>"{
"data": [
{ "id": 1, "title": "Hello", "status": "draft", ... },
{ "id": 2, "title": "World", "status": "draft", ... }
],
"total": 2
}Use limit and offset for pagination. Any other query parameter is treated as a filter on the corresponding column.
GET /api/:table/:id
Fetch a single record by ID.
curl "https://my-app.based.yourdomain.com/api/posts/1" \
-H "apikey: <anonKey>"{
"data": { "id": 1, "title": "Hello", "status": "draft", ... }
}POST /api/:table
Create a new record. Requires authentication — anon keys cannot create.
curl -X POST "https://my-app.based.yourdomain.com/api/posts" \
-H "Authorization: Bearer <accessToken>" \
-H "Content-Type: application/json" \
-d '{ "title": "New Post", "status": "draft" }'{
"data": { "id": 3, "title": "New Post", "status": "draft", ... }
}PUT /api/:table/:id
Upsert: creates the record if it doesn't exist, updates it if it does. Requires authentication. Returns 201 on create, 200 on update.
# Update existing
curl -X PUT "https://my-app.based.yourdomain.com/api/posts/3" \
-H "Authorization: Bearer <accessToken>" \
-H "Content-Type: application/json" \
-d '{ "status": "published" }'
# Upsert with custom id (creates if missing)
curl -X PUT "https://my-app.based.yourdomain.com/api/settings/user-preferences" \
-H "Authorization: Bearer <accessToken>" \
-H "Content-Type: application/json" \
-d '{ "theme": "dark" }'{
"data": { "id": 3, "title": "New Post", "status": "published", ... }
}DELETE /api/:table/:id
Delete a record. Requires authentication.
curl -X DELETE "https://my-app.based.yourdomain.com/api/posts/3" \
-H "Authorization: Bearer <accessToken>"Authentication
| Method | Header | Access |
|---|---|---|
| Bearer token | Authorization: Bearer <accessToken> | Full read/write |
| Anon key | apikey: <anonKey> | Read-only |
Error responses
Errors follow a consistent format across all endpoints.
{
"error": {
"code": "NOT_FOUND",
"message": "Record not found"
}
}