Database Operations
Backflow supports Supabase, Firebase, SQLite, and custom REST-based databases (MongoDB, BigQuery, etc.) with a unified query interface.
Configuration
{
"databaseProvider": "supabase",
"supabase": {
"url": "{{env.SUPABASE_URL}}",
"anonKey": "{{env.SUPABASE_ANON_KEY}}"
}
}{
"databaseProvider": "firebase",
"firebase": {
"projectId": "{{env.FIREBASE_PROJECT_ID}}",
"clientEmail": "{{env.FIREBASE_CLIENT_EMAIL}}",
"privateKey": "{{env.FIREBASE_PRIVATE_KEY}}"
}
}{
"databaseProvider": "sqlite",
"sqlite": {
"path": "./data/app.db"
}
}Query Structure
{
"supabaseQueries": [{
"table": "users",
"operation": "select",
"select": "id, name, email",
"filters": [],
"order": { "column": "created_at", "ascending": false },
"limit": 10
}]
}{
"firebaseQueries": [{
"collection": "users",
"operation": "query",
"filters": [],
"orderBy": { "field": "created_at", "direction": "desc" },
"limit": 10
}]
}{
"supabaseQueries": [{
"table": "users",
"operation": "select",
"select": "id, name, email",
"filters": [],
"order": { "column": "created_at", "ascending": false },
"limit": 10
}]
}SQLite uses the same query schema as Supabase.
Operations
Select / Query
{
"table": "posts",
"operation": "select",
"select": "*",
"single": false
}{
"collection": "posts",
"operation": "query"
}{
"table": "posts",
"operation": "select",
"select": "*",
"single": false
}Get by ID
{
"table": "posts",
"operation": "select",
"filters": [{ "column": "id", "operator": "eq", "value": "{{params.id}}" }],
"single": true
}{
"collection": "posts",
"operation": "get",
"docId": "{{params.id}}"
}{
"table": "posts",
"operation": "select",
"filters": [{ "column": "id", "operator": "eq", "value": "{{params.id}}" }],
"single": true
}Insert / Add
{
"table": "posts",
"operation": "insert",
"data": {
"title": "{{body.title}}",
"content": "{{body.content}}",
"user_id": "{{auth.sub}}"
},
"select": "*",
"single": true
}{
"collection": "posts",
"operation": "add",
"data": {
"title": "{{body.title}}",
"content": "{{body.content}}",
"user_id": "{{auth.sub}}"
}
}{
"table": "posts",
"operation": "insert",
"data": {
"title": "{{body.title}}",
"content": "{{body.content}}",
"user_id": "{{auth.sub}}"
},
"select": "*",
"single": true
}Update
{
"table": "posts",
"operation": "update",
"data": { "title": "{{body.title}}" },
"filters": [{ "column": "id", "operator": "eq", "value": "{{params.id}}" }]
}{
"collection": "posts",
"operation": "update",
"docId": "{{params.id}}",
"data": { "title": "{{body.title}}" }
}{
"table": "posts",
"operation": "update",
"data": { "title": "{{body.title}}" },
"filters": [{ "column": "id", "operator": "eq", "value": "{{params.id}}" }]
}Upsert / Set
{
"table": "settings",
"operation": "upsert",
"data": {
"user_id": "{{auth.sub}}",
"theme": "{{body.theme}}"
},
"upsertOptions": { "onConflict": "user_id" }
}{
"collection": "settings",
"operation": "set",
"docId": "{{auth.sub}}",
"data": { "theme": "{{body.theme}}" }
}{
"table": "settings",
"operation": "upsert",
"data": {
"user_id": "{{auth.sub}}",
"theme": "{{body.theme}}"
},
"upsertOptions": { "onConflict": "user_id" }
}Delete
{
"table": "posts",
"operation": "delete",
"filters": [{ "column": "id", "operator": "eq", "value": "{{params.id}}" }]
}{
"collection": "posts",
"operation": "delete",
"docId": "{{params.id}}"
}{
"table": "posts",
"operation": "delete",
"filters": [{ "column": "id", "operator": "eq", "value": "{{params.id}}" }]
}Filters
| Operator | Description | Example |
|----------|-------------|---------|
| `eq` | Equal | `{ "column": "status", "operator": "eq", "value": "active" }` |
| `neq` | Not equal | `{ "column": "status", "operator": "neq", "value": "deleted" }` |
| `gt` | Greater than | `{ "column": "age", "operator": "gt", "value": 18 }` |
| `gte` | Greater or equal | `{ "column": "price", "operator": "gte", "value": 100 }` |
| `lt` | Less than | `{ "column": "stock", "operator": "lt", "value": 10 }` |
| `lte` | Less or equal | `{ "column": "rating", "operator": "lte", "value": 5 }` |
| `like` | Pattern match | `{ "column": "name", "operator": "like", "value": "%john%" }` |
| `ilike` | Case-insensitive | `{ "column": "email", "operator": "ilike", "value": "%@gmail.com" }` |
| `in` | In array | `{ "column": "status", "operator": "in", "value": ["active", "pending"] }` |
| `is` | IS NULL | `{ "column": "deleted_at", "operator": "is", "value": null }` |
| `contains` | Array contains | `{ "column": "tags", "operator": "contains", "value": ["tech"] }` || Operator | Description | Example |
|----------|-------------|---------|
| `==` | Equal | `{ "field": "status", "operator": "==", "value": "active" }` |
| `!=` | Not equal | `{ "field": "status", "operator": "!=", "value": "deleted" }` |
| `>` | Greater than | `{ "field": "age", "operator": ">", "value": 18 }` |
| `>=` | Greater or equal | `{ "field": "price", "operator": ">=", "value": 100 }` |
| `<` | Less than | `{ "field": "stock", "operator": "<", "value": 10 }` |
| `<=` | Less or equal | `{ "field": "rating", "operator": "<=", "value": 5 }` |
| `in` | In array | `{ "field": "status", "operator": "in", "value": ["active", "pending"] }` |
| `array-contains` | Array contains | `{ "field": "tags", "operator": "array-contains", "value": "tech" }` |Multiple Filters
{
"filters": [
{ "column": "status", "operator": "eq", "value": "active" },
{ "column": "created_at", "operator": "gte", "value": "2024-01-01" }
]
}{
"filters": [
{ "field": "status", "operator": "==", "value": "active" },
{ "field": "created_at", "operator": ">=", "value": "2024-01-01" }
]
}Ordering
{
"order": {
"column": "created_at",
"ascending": false
}
}{
"orderBy": {
"field": "created_at",
"direction": "desc"
}
}Pagination
{
"limit": 10,
"offset": 0
}With query parameters:
{
"limit": "{{query.limit}}",
"offset": "{{query.offset}}"
}Relations
Supabase/SQLite support joins via select syntax:
{
"table": "posts",
"operation": "select",
"select": "*, users(id, name, email)"
}Nested relations:
{
"select": "*, comments(*, users(name))"
}Firebase: Use subcollections or multiple queries for relations.
Multiple Queries
{
"supabaseQueries": [
{
"table": "orders",
"operation": "insert",
"data": { "user_id": "{{auth.sub}}" }
},
{
"table": "inventory",
"operation": "update",
"data": { "stock": "{{body.newStock}}" }
}
]
}{
"firebaseQueries": [
{
"collection": "orders",
"operation": "add",
"data": { "user_id": "{{auth.sub}}" }
},
{
"collection": "inventory",
"operation": "update",
"docId": "{{body.itemId}}",
"data": { "stock": "{{body.newStock}}" }
}
]
}Custom Databases (REST Adapter)
Add any REST-based database (MongoDB, BigQuery, etc.) via JSON config without code changes.
Configuration
Create an integration file in integrations/ folder:
{
"name": "MongoDB Atlas Data API",
"baseUrl": "{{secrets.MONGODB_DATA_API_URL}}",
"auth": {
"type": "api_key",
"location": "header",
"paramName": "api-key",
"key": "{{secrets.MONGODB_API_KEY}}"
},
"defaultDatabase": "{{secrets.MONGODB_DATABASE}}",
"operations": {
"find": {
"method": "POST",
"path": "/action/find",
"bodyTemplate": {
"collection": "{{table}}",
"database": "{{database}}",
"filter": "{{filters}}",
"limit": "{{limit}}",
"skip": "{{offset}}",
"sort": "{{sort}}",
"projection": "{{projection}}"
}
},
"insertOne": {
"method": "POST",
"path": "/action/insertOne",
"bodyTemplate": {
"collection": "{{table}}",
"database": "{{database}}",
"document": "{{data}}"
}
},
"updateOne": {
"method": "POST",
"path": "/action/updateOne",
"bodyTemplate": {
"collection": "{{table}}",
"database": "{{database}}",
"filter": "{{filters}}",
"update": { "$set": "{{data}}" }
}
},
"deleteOne": {
"method": "POST",
"path": "/action/deleteOne",
"bodyTemplate": {
"collection": "{{table}}",
"database": "{{database}}",
"filter": "{{filters}}"
}
}
},
"operatorMap": {
"eq": "$eq",
"neq": "$ne",
"gt": "$gt",
"gte": "$gte",
"lt": "$lt",
"lte": "$lte",
"in": "$in",
"like": "$regex"
},
"responseMap": {
"find": "documents",
"findOne": "document",
"insertOne": "insertedId"
}
}Auth Types
| Type | Fields |
|---|---|
bearer | token or key |
api_key | key, location (header/query), paramName |
basic | username, password |
custom | headers object |
Operator Mapping
Map generic operators to database-specific syntax:
{
"operatorMap": {
"eq": "$eq",
"neq": "$ne",
"gt": "$gt",
"gte": "$gte",
"lt": "$lt",
"lte": "$lte",
"in": "$in",
"like": "$regex"
}
}OR Queries
Execute multiple queries and merge results (deduplicated by ID):
{
"table": "users",
"or": [
{ "filters": [{ "column": "status", "operator": "eq", "value": "active" }] },
{ "filters": [{ "column": "role", "operator": "eq", "value": "admin" }] }
]
}Chain Queries (JOINs)
Execute sequential queries with result references:
{
"table": "orders",
"chain": [
{
"table": "users",
"filters": [{ "column": "email", "operator": "eq", "value": "{{body.email}}" }],
"single": true,
"as": "user"
},
{
"table": "orders",
"filters": [{ "column": "user_id", "operator": "eq", "value": "{{chain.user.id}}" }],
"as": "orders"
}
]
}Use {{chain.alias.field}} to reference results from previous queries.
Workflow Usage
Use custom databases in workflows with or and chain params:
{
"steps": [
{
"id": "get-user-orders",
"tool": "database",
"params": {
"table": "orders",
"operation": "select",
"chain": [
{
"table": "users",
"filters": [{ "column": "id", "operator": "eq", "value": "{{input.userId}}" }],
"single": true,
"as": "user"
},
{
"table": "orders",
"filters": [{ "column": "user_id", "operator": "eq", "value": "{{chain.user.id}}" }],
"as": "orders"
}
]
}
}
]
}