softr-vibe-coding 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,41 @@
1
+ # Data Source Overview
2
+
3
+ ## Comparison Matrix
4
+
5
+ | Data Source | Max Records | Concurrent Users | Security | Skill Level | Vibe Coding Approach |
6
+ |---|---|---|---|---|---|
7
+ | Softr Databases | 1M+ | Unlimited | Medium | Low | `useRecords` + `q.select()` |
8
+ | Airtable | 500K | 200-300 | Medium | Low | `useRecords` + `q.select()` |
9
+ | Google Sheets | 1M+ | 50-100 | Low | Low | `useRecords` + `q.select()` |
10
+ | SmartSuite | 500K | 200-300 | Medium | Low | `useRecords` + `q.select()` |
11
+ | HubSpot | 1M+ | 500-1000 | Medium | Medium | `useRecords` + `q.select()` |
12
+ | monday.com | 500K | 50-100 | Low | Low | `useRecords` + `q.select()` |
13
+ | Xano | Unlimited | Unlimited | High | High | `useRecords` + `q.select()` |
14
+ | SQL Database | Unlimited | Unlimited | High | High | `useRecords` + `q.select()` |
15
+ | Supabase | Unlimited | Unlimited | High | High | `useRecords` + `q.select()` |
16
+ | Notion | 1M+ | 50-100 | Medium | Low | `useRecords` + `q.select()` |
17
+ | ClickUp | 500K | 50-100 | Medium | Low | `useRecords` + `q.select()` |
18
+ | Coda | 1M+ | 200-300 | Medium | Low | `useRecords` + `q.select()` |
19
+ | BigQuery | Unlimited | N/A | Low | Medium | `useRecords` + `q.select()` |
20
+ | REST API | Unlimited | N/A | Varies | High | `useProxyFetch` + `useQuery` |
21
+
22
+ ## Selection Guide
23
+
24
+ - **New projects with no existing data** -> Softr Databases (no rate limits, native performance, included in subscription)
25
+ - **Existing spreadsheet data** -> Airtable or Google Sheets (easy migration, one-click import from Airtable)
26
+ - **Scalable backend with full API control** -> Xano, Supabase, or SQL
27
+ - **Any data source not natively supported** -> REST API
28
+
29
+ ## Key Concepts
30
+
31
+ - Softr does **not store or sync** external data -- it proxies requests in real-time (with 24-hour caching).
32
+ - Data is connected to **dynamic blocks** (List, Grid, Table, Kanban, Chart, Form, etc.) in the Softr Studio.
33
+ - **Multiple data sources** can coexist in a single Softr app, even on the same page.
34
+ - Softr IP addresses to whitelist for secured databases: `3.120.79.212`, `3.123.159.186`, `52.58.246.121`
35
+
36
+ ## User Sync Availability
37
+
38
+ | Source | 2-Way User Sync |
39
+ |---|---|
40
+ | Softr Databases, Airtable, Google Sheets, HubSpot, Notion, Coda, monday.com, SmartSuite, ClickUp, Xano, Supabase, SQL Database | Supported |
41
+ | BigQuery, REST API | Not supported |
@@ -0,0 +1,222 @@
1
+ # Reading Data
2
+
3
+ Fetching, filtering, sorting, pagination, metrics, charts, and current user.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Query Builder](#query-builder)
8
+ - [useRecords -- Fetch a Paginated List](#userecords----fetch-a-paginated-list)
9
+ - [useRecord -- Fetch a Single Record](#userecord----fetch-a-single-record)
10
+ - [useLinkedRecords -- Fetch Linked/Related Options](#uselinkedrecords----fetch-linkedrelated-options)
11
+ - [useFieldOptions -- Fetch Single/Multi-Select Choices](#usefieldoptions----fetch-singlemulti-select-choices)
12
+ - [Filtering](#filtering)
13
+ - [Sorting](#sorting)
14
+ - [Current User](#current-user)
15
+ - [Metrics](#metrics)
16
+ - [Chart Data](#chart-data)
17
+
18
+ ## Query Builder
19
+
20
+ Field mappings must be static (no dynamic keys or computed values -- hard constraint for Softr's static analysis):
21
+
22
+ ```jsx
23
+ import { q } from "@/lib/datasource";
24
+
25
+ var select = q.select({
26
+ title: "FIELD_ID1",
27
+ description: "FIELD_ID2",
28
+ createdAt: "FIELD_ID3",
29
+ });
30
+ ```
31
+
32
+ ## useRecords -- Fetch a Paginated List
33
+
34
+ ```jsx
35
+ import { useRecords, q } from "@/lib/datasource";
36
+
37
+ var result = useRecords({
38
+ select: q.select({ name: "FIELD_ID1", email: "FIELD_ID2" }),
39
+ count: 6, // records per page (default 6, max 100)
40
+ where: q.text("name").contains("Alice"), // optional filter
41
+ orderBy: q.desc("createdAt"), // optional sort
42
+ enabled: true, // optional, defer loading
43
+ });
44
+
45
+ var data = result.data;
46
+ var status = result.status; // "pending" | "success" | "error"
47
+ var error = result.error;
48
+ var fetchNextPage = result.fetchNextPage;
49
+ var hasNextPage = result.hasNextPage;
50
+ var isFetching = result.isFetching;
51
+ var isFetchingNextPage = result.isFetchingNextPage;
52
+ var refetch = result.refetch;
53
+ var isRefetching = result.isRefetching;
54
+
55
+ // Flatten pages into a single array:
56
+ var items = (data && data.pages) ? data.pages.flatMap(function(p) { return p.items; }) : [];
57
+ ```
58
+
59
+ **CRITICAL:** Only ONE `useRecords` call per block. Fetch all data in one call and filter client-side. Multiple `useMetric` calls ARE allowed.
60
+
61
+ ### Loading All Records (Auto-Pagination)
62
+
63
+ ```jsx
64
+ import { useState, useEffect } from "react";
65
+
66
+ var result = useRecords({ select: select, count: 100 });
67
+
68
+ useEffect(function() {
69
+ if (result.hasNextPage && !result.isFetchingNextPage && result.status === "success") {
70
+ result.fetchNextPage();
71
+ }
72
+ }, [result.hasNextPage, result.isFetchingNextPage, result.status, result.fetchNextPage]);
73
+ ```
74
+
75
+ ## useRecord -- Fetch a Single Record
76
+
77
+ ```jsx
78
+ import { useRecord, useCurrentRecordId, q } from "@/lib/datasource";
79
+
80
+ var recordId = useCurrentRecordId(); // resolves from URL context, can be null
81
+ var result = useRecord({
82
+ select: q.select({ title: "FIELD_ID1", description: "FIELD_ID2" }),
83
+ recordId: recordId,
84
+ });
85
+ ```
86
+
87
+ ## useLinkedRecords -- Fetch Linked/Related Options
88
+
89
+ ```jsx
90
+ import { q, useLinkedRecords } from "@/lib/datasource";
91
+
92
+ var result = useLinkedRecords({
93
+ select: q.select({ category: "$CATEGORY_FIELD_ID" }),
94
+ field: "category", // the ALIAS from q.select(), NOT the raw field ID
95
+ sortOrder: "ASC", // "ASC" | "DESC"
96
+ search: "", // optional search string
97
+ enabled: true, // defer loading until needed
98
+ count: 50,
99
+ });
100
+
101
+ var options = (result.data && result.data.pages) ? result.data.pages.flatMap(function(p) { return p.items; }) : [];
102
+ ```
103
+
104
+ **CRITICAL:** The `field` prop takes the ALIAS from `q.select()`, NOT the raw field ID. Items are shaped as `{ id, title }` -- use `opt.title` (NOT `opt.label`).
105
+
106
+ ## useFieldOptions -- Fetch Single/Multi-Select Choices
107
+
108
+ Returns the current option list for any `singleSelect` / `multipleSelects` field — without hardcoding option IDs in your block. Useful when the schema's option list changes (renames, additions, reorders) and you don't want to redeploy the block every time.
109
+
110
+ ```jsx
111
+ import { useFieldOptions, q } from "@/lib/datasource";
112
+
113
+ var statusOptions = useFieldOptions({
114
+ select: q.select({ status: "Status" }),
115
+ field: "status", // the ALIAS from q.select(), NOT the raw field ID
116
+ });
117
+
118
+ // statusOptions.options → [{ id: "sel...", label: "Active" }, { id: "sel...", label: "Inactive" }]
119
+ // Each item has `id` (the option's UUID, used in mutate payloads) and `label` (display string).
120
+ ```
121
+
122
+ **When to use this vs. hardcoding:**
123
+
124
+ - **Use `useFieldOptions`** when option IDs / labels could change post-deploy — selects with rapidly-evolving lists, user-editable choices, or any case where re-pasting blocks for an option rename is annoying.
125
+ - **Hardcode** when the option set is stable and frequently referenced (e.g. a status enum that drives a state machine), so the IDs live in source and rename-safety is enforced by greppable constants.
126
+
127
+ `useFieldOptions` is the read-side equivalent of using `useLinkedRecords` for foreign records — it abstracts away the field's option store. Items are shaped `{ id, label }` (note: `label`, not `title` like `useLinkedRecords`).
128
+
129
+ ## Filtering
130
+
131
+ Build filters with typed builders. Filters support up to 2 levels of nesting.
132
+
133
+ **Text fields** -- `q.text(field)`:
134
+ `is`, `isNot`, `contains`, `startsWith`, `endsWith`, `isOneOf`, `isNoneOf`, `hasAllOf`, `isEmpty`, `isNotEmpty`
135
+
136
+ **Number fields** -- `q.number(field)`:
137
+ `is`, `isNot`, `gt`, `gte`, `lt`, `lte`, `between`, `isEmpty`, `isNotEmpty`
138
+
139
+ **Boolean fields** -- `q.boolean(field)`:
140
+ `is`, `isNot`, `isEmpty`, `isNotEmpty`
141
+
142
+ **Date fields** -- `q.date(field)`:
143
+ `is`, `isNot`, `gt`, `gte`, `lt`, `lte`, `between`, `isNotBetween`, `isEmpty`, `isNotEmpty`
144
+
145
+ **Array fields** -- `q.array(field)`:
146
+ `is`, `isOneOf`, `isNoneOf`, `hasAllOf`, `isEmpty`, `isNotEmpty`
147
+
148
+ **Logical combinators**: `q.and(...)`, `q.or(...)`
149
+
150
+ ```jsx
151
+ where: q.and(
152
+ q.text("name").contains("Alice"),
153
+ q.number("age").gte(18),
154
+ q.or(
155
+ q.boolean("isActive").is(true),
156
+ q.text("notes").isNotEmpty()
157
+ )
158
+ )
159
+ ```
160
+
161
+ ## Sorting
162
+
163
+ ```jsx
164
+ orderBy: q.desc("createdAt")
165
+ orderBy: q.asc("lastName")
166
+ orderBy: [q.asc("lastName"), q.asc("firstName")] // multiple fields
167
+ ```
168
+
169
+ ## Current User
170
+
171
+ ```jsx
172
+ import { useCurrentUser } from "@/lib/user";
173
+
174
+ var user = useCurrentUser();
175
+ // Returns null if not logged in
176
+ // Fields: { id, fullName, firstName, lastName, email, avatar } (all string or null)
177
+ ```
178
+
179
+ **For user groups, role, or custom fields** -- use `window.__softr_current_user` (NOT `useCurrentUser()`):
180
+
181
+ ```jsx
182
+ var softrUser = window.__softr_current_user || {};
183
+ var userGroups = softrUser.userGroups || [];
184
+ var isPremium = userGroups.some(function(g) { return g.name === "Premium Member"; });
185
+ ```
186
+
187
+ ## Metrics
188
+
189
+ ```jsx
190
+ import { useMetric, q, metric } from "@/lib/datasource";
191
+
192
+ var result = useMetric({
193
+ select: q.select({ revenue: "$REVENUE_FIELD_ID" }),
194
+ metric: metric.sum("revenue"),
195
+ where: q.date("createdAt").gte("2025-01-01"),
196
+ });
197
+ // result.data is the aggregated value (number)
198
+ ```
199
+
200
+ Aggregations: `metric.sum(field)`, `metric.avg(field)`, `metric.max(field)`, `metric.min(field)`, `metric.distinct(field)`, `metric.count()`
201
+
202
+ ## Chart Data
203
+
204
+ ```jsx
205
+ import { useChartData, q, metric } from "@/lib/datasource";
206
+
207
+ var result = useChartData({
208
+ select: q.select({ date: "$DATE_FIELD_ID", revenue: "$REVENUE_FIELD_ID" }),
209
+ orderBy: q.asc("date"),
210
+ metric: { revenue: metric.sum("revenue") },
211
+ groupBy: metric.groupBy("date", metric.bucket.month.long),
212
+ });
213
+ ```
214
+
215
+ Grouping buckets: `metric.bucket.year`, `metric.bucket.month.iso`, `metric.bucket.month.long`, `metric.bucket.day.iso`, `metric.bucket.day.long`
216
+
217
+ Use **recharts** with shadcn's chart wrapper:
218
+
219
+ ```jsx
220
+ import { LineChart, Line, XAxis, CartesianGrid } from "recharts";
221
+ import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart";
222
+ ```
@@ -0,0 +1,206 @@
1
+ # REST API Data Source
2
+
3
+ ## Overview
4
+
5
+ A generic connector that allows Softr to connect to virtually any external platform or service that exposes an HTTP REST API. This is the escape hatch for any source not natively supported.
6
+
7
+ **Plan requirement:** Business and Enterprise Softr plans only.
8
+
9
+ **Authentication:** Any header-based auth (API keys, Bearer tokens, Basic Auth, etc.) configured manually.
10
+
11
+ **2-way user sync:** Not supported.
12
+
13
+ **Best for:** Connecting any external service with an API; extending Softr's native integrations; building RevOps or automation workflows where Softr serves as a frontend for tools like Salesforce, Pipedrive, Stripe, Luma, or custom internal APIs.
14
+
15
+ ## Connection Setup
16
+
17
+ ### Step 1: Add REST API as a data source
18
+ Data Sources > Connect Data Source > Choose **REST API**. Select a pre-built template (Stripe, Salesforce, Pipedrive, Asana, Shopify, etc.) or **Add Manually**.
19
+
20
+ ### Step 2: Set up default headers
21
+ These headers apply to all endpoints in the connection:
22
+ - **Name**: A label to identify the REST API connection.
23
+ - **Headers**: Key-value pairs for universal headers (e.g., `Authorization: Bearer YOUR_TOKEN`, custom header for API key, etc.).
24
+
25
+ ### Step 3: Add API Resources (Endpoints)
26
+ Each resource is a specific API endpoint. Supports **GET** and **POST** methods.
27
+
28
+ For each resource:
29
+ - **Name**: A label for the endpoint.
30
+ - **HTTP Method**: GET (fetch data) or POST (create records).
31
+ - **URL**: The endpoint URL from the API's documentation.
32
+ - **Headers tab**: Endpoint-specific headers (in addition to default headers).
33
+ - **URL Params tab**: Query string parameters. Can use placeholders (see below).
34
+ - **Body tab**: JSON body for POST requests.
35
+ - **Pagination tab**: Configure cursor or offset-based pagination parameters.
36
+ - **Transformer tab**: Vanilla JavaScript to reshape the API's JSON response.
37
+
38
+ Click **Execute** to test the endpoint before saving.
39
+
40
+ ### Step 4: Schema setup
41
+ After a successful test call:
42
+ - **Key**: The field name from the JSON response.
43
+ - **Type**: TEXT, NUMBER, BOOLEAN, DATE, DATETIME, TIMESTAMP, OBJECT, ARRAY, EMAIL, URL.
44
+ - **Enabled**: Toggle to show/hide the field in Softr Studio.
45
+ - **ID Field**: Mark the unique record identifier.
46
+
47
+ ### Placeholders (dynamic values)
48
+ - `{LOGGED_IN_USER: <FIELD>}` -- inject logged-in user data (e.g., `{LOGGED_IN_USER: StripeID}`)
49
+ - `{recordId}` -- inject the currently selected record ID
50
+ - `{filter.<FIELD>}` -- pass filter/search values from Softr block filters into the API request
51
+
52
+ Placeholders can be used anywhere: headers, URL, URL params, body.
53
+
54
+ ### Transformer Example
55
+ For APIs with nested responses (e.g., Luma's `entries[].event` structure):
56
+
57
+ ```javascript
58
+ return data.entries.map(function(entry) { return entry.event; });
59
+ ```
60
+
61
+ ## Vibe Coding Integration
62
+
63
+ **REST API data sources use a completely different pattern from Airtable/Softr Database.** They use `useProxyFetch` + `useQuery` instead of `useRecords` + `q.select()`.
64
+
65
+ ### useProxyFetch + useQuery
66
+
67
+ ```jsx
68
+ import { useProxyFetch } from "@/lib/datasource";
69
+ import { useQuery } from "@tanstack/react-query";
70
+
71
+ export default function Block() {
72
+ var proxyFetch = useProxyFetch();
73
+
74
+ var result = useQuery({
75
+ queryKey: ["my-data"],
76
+ queryFn: function() {
77
+ return proxyFetch("https://api.example.com/v1/data")
78
+ .then(function(res) {
79
+ if (!res.ok) throw new Error("Failed to fetch data");
80
+ return res.json();
81
+ });
82
+ },
83
+ });
84
+
85
+ var data = result.data;
86
+ var status = result.status; // "pending" | "success" | "error"
87
+ var error = result.error;
88
+
89
+ if (status === "pending") { /* loading UI */ }
90
+ if (status === "error") { /* error UI */ }
91
+
92
+ // Access the raw API response directly -- no record.fields nesting
93
+ var items = (data && data.entries) || [];
94
+ }
95
+ ```
96
+
97
+ ### How It Works
98
+
99
+ - `useProxyFetch` returns a fetch function that routes requests through Softr's proxy
100
+ - Softr injects the authentication headers configured in the data source automatically
101
+ - API keys are **never exposed** in client-side code
102
+ - The response is the raw API JSON -- access fields directly (e.g., `item.name`, not `record.fields.name`)
103
+
104
+ ### Dynamic Query Parameters
105
+
106
+ Build URLs with dynamic values. Include them in the `queryKey` so React Query refetches when they change:
107
+
108
+ ```jsx
109
+ var proxyFetch = useProxyFetch();
110
+ var afterDate = new Date().toISOString();
111
+
112
+ var result = useQuery({
113
+ queryKey: ["events", afterDate],
114
+ queryFn: function() {
115
+ var url = "https://api.example.com/v1/events?after=" + encodeURIComponent(afterDate);
116
+ return proxyFetch(url)
117
+ .then(function(res) {
118
+ if (!res.ok) throw new Error("Failed to fetch");
119
+ return res.json();
120
+ });
121
+ },
122
+ });
123
+ ```
124
+
125
+ ### POST Requests
126
+
127
+ `proxyFetch` accepts the same options as standard `fetch`:
128
+
129
+ ```jsx
130
+ var result = useQuery({
131
+ queryKey: ["search", searchTerm],
132
+ queryFn: function() {
133
+ return proxyFetch("https://api.example.com/v1/search", {
134
+ method: "POST",
135
+ headers: { "Content-Type": "application/json" },
136
+ body: JSON.stringify({ query: searchTerm }),
137
+ })
138
+ .then(function(res) {
139
+ if (!res.ok) throw new Error("Search failed");
140
+ return res.json();
141
+ });
142
+ },
143
+ enabled: searchTerm.length > 0,
144
+ });
145
+ ```
146
+
147
+ ### Sending Webhooks (Non-Proxy)
148
+
149
+ For outbound requests to services that don't need proxy auth (e.g., Softr workflow webhooks), use regular `fetch()`:
150
+
151
+ ```jsx
152
+ fetch("https://workflows-api.softr.io/v1/workflows/WORKFLOW_ID/executions/EXECUTION_ID", {
153
+ method: "POST",
154
+ headers: { "Content-Type": "application/json" },
155
+ body: JSON.stringify({ data: { key: "value" } }),
156
+ });
157
+ ```
158
+
159
+ ### Key Differences from useRecords
160
+
161
+ | | useRecords (Airtable/Softr DB) | useProxyFetch (REST API) |
162
+ |---|---|---|
163
+ | Field mapping | `q.select()` with aliases | None -- raw API response |
164
+ | Data access | `record.fields.alias` | Direct (e.g., `item.name`) |
165
+ | Response shape | `data.pages[].items[]` | Whatever the API returns |
166
+ | Pagination | Built-in `fetchNextPage` | Manual via URL params + cursor |
167
+ | Filtering | `q.text()`, `q.number()`, etc. | API query params or client-side |
168
+ | Auth | Handled by Softr | Proxied through Softr (key hidden) |
169
+ | Mutations | `useRecordCreate/Update/Delete` | Direct `fetch()` or `proxyFetch()` |
170
+
171
+ ## Limitations
172
+
173
+ - Supports GET and POST methods only in the data source connector (no PUT, PATCH, DELETE -- use Softr Action buttons with custom API call actions for those)
174
+ - Response data must be parseable JSON
175
+ - No 2-way user sync
176
+ - Requires Business or Enterprise plan
177
+
178
+ ## Debug Utility
179
+
180
+ Use this block to inspect the raw API response:
181
+
182
+ ```jsx
183
+ import { useProxyFetch } from "@/lib/datasource";
184
+ import { useQuery } from "@tanstack/react-query";
185
+ export default function Block() {
186
+ var proxyFetch = useProxyFetch();
187
+ var result = useQuery({
188
+ queryKey: ["api-inspect"],
189
+ queryFn: function() {
190
+ return proxyFetch("YOUR_FULL_API_URL_HERE")
191
+ .then(function(res) { return res.json(); });
192
+ },
193
+ });
194
+ if (result.status === "pending") return <div className="container py-6"><div className="content"><p>Loading...</p></div></div>;
195
+ if (result.status === "error") return <div className="container py-6"><div className="content"><p className="text-red-500">Error: {result.error && result.error.message}</p></div></div>;
196
+ return (
197
+ <div className="container py-6">
198
+ <div className="content">
199
+ <pre style={{ fontSize: 12, whiteSpace: "pre-wrap", wordBreak: "break-all", background: "#f9fafb", padding: 16, borderRadius: 8 }}>
200
+ {JSON.stringify(result.data, null, 2)}
201
+ </pre>
202
+ </div>
203
+ </div>
204
+ );
205
+ }
206
+ ```
@@ -0,0 +1,9 @@
1
+ # Shared Vibe Coding Patterns
2
+
3
+ These patterns apply to all data sources that use `useRecords` + `q.select()` (everything except REST API). For REST API, see [rest-api.md](rest-api.md).
4
+
5
+ | Topic | Guide |
6
+ |---|---|
7
+ | Query builder, useRecords, useRecord, useLinkedRecords, filtering, sorting, pagination, current user, metrics, chart data | [reading.md](reading.md) |
8
+ | Record mutations (create/update/delete), file uploads, linked record format, cross-table REST API writes | [writing.md](writing.md) |
9
+ | `getFieldValue()` helper, field type shapes, record structure, debug utilities (Field Inspector, User Inspector) | [fields.md](fields.md) |
@@ -0,0 +1,39 @@
1
+ # SmartSuite
2
+
3
+ ## Overview
4
+ SmartSuite is a work management and relational database platform. Available on Professional plans and above.
5
+
6
+ ## Connection Setup
7
+ 1. In the Softr admin, go to your data source settings and select SmartSuite.
8
+ 2. Authenticate via OAuth -- you will be redirected to SmartSuite to authorize access.
9
+ 3. Choose your Workspace, then select a Solution, then select a Table to connect.
10
+
11
+ ## Vibe Coding Field IDs
12
+ Use the Field Inspector block to determine exact field IDs for this data source.
13
+
14
+ ## Supported Fields
15
+
16
+ | Field Type | Writable | Notes |
17
+ |----------------|----------|-------|
18
+ | Text | Yes | |
19
+ | Number | Yes | |
20
+ | Date | Yes | |
21
+ | Dropdown | Yes | |
22
+ | Checkbox | Yes | |
23
+ | Linked Record | Yes | Linked records between SmartSuite tables are supported |
24
+ | Formula | Read-only | |
25
+ | Rollup | Read-only | |
26
+ | Lookup | Read-only | |
27
+
28
+ ## Rate Limits
29
+ Subject to SmartSuite API rate limits. Implement pagination and caching where possible to reduce request volume.
30
+
31
+ ## Gotchas
32
+ - **Linked records** work across SmartSuite tables but require that both tables are within the same Solution.
33
+ - **Formula, Rollup, and Lookup fields** are read-only. Attempting to write to them will fail silently or error.
34
+ - **OAuth tokens** may expire. If data stops loading, re-authenticate via the Softr admin.
35
+
36
+ ## Best For
37
+ - Teams already using SmartSuite as their primary work OS
38
+ - Apps that need relational data across multiple SmartSuite tables
39
+ - Organizations consolidating project management and app building on SmartSuite
@@ -0,0 +1,47 @@
1
+ # Softr Database
2
+
3
+ ## Overview
4
+ Softr's native built-in database. No external account or integration required. Available on all plans with support for 1M+ records.
5
+
6
+ ## Connection Setup
7
+ No setup needed. Softr Database is available by default in every Softr app. Create tables directly from the Softr admin dashboard under the "Data" section. Import data via CSV, one-click migration from Airtable, or AI-assisted table generation.
8
+
9
+ ## Vibe Coding Field IDs
10
+ Field IDs are short alphanumeric codes (e.g., `"xgETy"`, `"TLhWF"`). These codes are NOT human-readable names.
11
+
12
+ ```jsx
13
+ // CORRECT - use the alphanumeric field ID
14
+ q.select({ name: "xgETy" })
15
+
16
+ // WRONG - human-readable names do not work
17
+ q.select({ name: "First Name" })
18
+ ```
19
+
20
+ Find field IDs by clicking a field's name in the Data tab (the ID is shown in the field-edit drawer), or via the network inspector technique (DevTools -> Network -> filter `tablespace-with-tables` for the full schema including dropdown option UUIDs). The network inspector method is recommended when working with an AI assistant -- pasting that JSON into the chat eliminates transcription errors. See [fields.md](fields.md#field-inspector-block) for both approaches plus a runtime REST API method. The generic Field Inspector pattern with empty `q.select({})` does NOT work for Softr Database.
21
+
22
+ ## Supported Fields
23
+
24
+ | Field Type | Writable | Notes |
25
+ |----------------|----------|-------|
26
+ | Text | Yes | |
27
+ | Number | Yes | |
28
+ | Date | Yes | |
29
+ | File / Image | Yes | |
30
+ | Checkbox | Yes | |
31
+ | Dropdown | Yes | |
32
+ | Relationship | Yes | Linked records to other Softr Database tables |
33
+ | Formula | Read-only | Booleans return as strings: use `=== "1"` for true, `=== "0"` for false |
34
+
35
+ ## Rate Limits
36
+ No API rate limits. Softr Database queries run internally without external API calls, making it the best choice for high-traffic applications.
37
+
38
+ ## Gotchas
39
+ - **Formula boolean values are strings.** A formula that evaluates to true returns `"1"`, not `true`. Always compare with `=== "1"` or `=== "0"`.
40
+ - **Field IDs are opaque codes.** You cannot guess them from column names. Use the Field Inspector block to find them.
41
+ - **Relationships** work similarly to linked records in Airtable but use Softr's internal record IDs.
42
+
43
+ ## Best For
44
+ - New projects starting from scratch
45
+ - High-traffic applications (no rate limit concerns)
46
+ - Teams that do not already have data in an external platform
47
+ - Apps requiring the simplest possible setup
@@ -0,0 +1,48 @@
1
+ # SQL Database
2
+
3
+ ## Overview
4
+ Direct connection to an external SQL database (PostgreSQL, MySQL, SQL Server, or MariaDB). Available on Business and Enterprise plans only.
5
+
6
+ ## Connection Setup
7
+ 1. Ensure your database server is accessible from the internet (or via a static IP).
8
+ 2. **Whitelist Softr IP addresses** in your database firewall:
9
+ - `3.120.79.212`
10
+ - `3.123.159.186`
11
+ - `52.58.246.121`
12
+ 3. In the Softr admin, go to your data source settings and select SQL Database.
13
+ 4. Enter the connection credentials:
14
+ - Host
15
+ - Port (defaults: PostgreSQL `5432`, MySQL/MariaDB `3306`, SQL Server `1433`)
16
+ - Database Name
17
+ - User
18
+ - Password
19
+ 5. Select the database type (PostgreSQL, MySQL, SQL Server, or MariaDB).
20
+
21
+ ## Vibe Coding Field IDs
22
+ Use the Field Inspector block to determine exact field IDs for this data source.
23
+
24
+ ## Supported Fields
25
+ Field support depends on the underlying database engine. Standard column types for each engine are writable. Generated columns, computed columns, and views are read-only.
26
+
27
+ | Engine | Common Writable Types |
28
+ |---------------|----------------------|
29
+ | PostgreSQL | text, integer, boolean, timestamp, jsonb, uuid, numeric, varchar |
30
+ | MySQL/MariaDB | varchar, int, tinyint, datetime, text, decimal, json, enum |
31
+ | SQL Server | nvarchar, int, bit, datetime2, decimal, uniqueidentifier |
32
+
33
+ ## Rate Limits
34
+ No Softr-imposed rate limits. Performance depends on your database server's capacity and connection limits. Unlimited records and unlimited concurrent users from Softr's side.
35
+
36
+ ## Gotchas
37
+ - **IP whitelisting is required.** Connections will be refused if the three Softr IPs are not allowed through your firewall.
38
+ - **Custom SQL SELECT queries are supported.** Use the built-in query editor in the Softr admin for advanced retrieval, joins across tables, and filtered views.
39
+ - **Database-level access control applies.** The database user you provide determines what tables and operations are available. Use a scoped user with minimal required permissions.
40
+ - **Default ports matter.** If your database runs on a non-standard port, specify it explicitly in the connection settings.
41
+ - **Connection pooling is managed by Softr.** You do not need to configure a connection pool, but ensure your database allows enough concurrent connections for your expected traffic.
42
+ - **No ORM or migration support.** Softr connects to your existing schema. Table creation and schema changes must be done directly in your database.
43
+
44
+ ## Best For
45
+ - Companies with existing SQL databases that want a no-code frontend
46
+ - Enterprise apps requiring high security with database-level access control
47
+ - Projects needing complex queries with joins across multiple tables
48
+ - Teams that manage their own infrastructure and want full control over the data layer
@@ -0,0 +1,38 @@
1
+ # Supabase
2
+
3
+ ## Overview
4
+ Supabase is an open-source Firebase alternative built on PostgreSQL. Available on Professional plans and above.
5
+
6
+ ## Connection Setup
7
+ 1. In your Supabase project, go to **Project Settings > Database**.
8
+ 2. Copy the **Session Pooler** connection credentials:
9
+ - Host
10
+ - Database name
11
+ - Port: `5432`
12
+ - User
13
+ - Password
14
+ 3. **Set the Pool Size to 45 or higher** in Supabase Project Settings > Database. This is required for stable Softr connections.
15
+ 4. In the Softr admin, go to your data source settings and select Supabase.
16
+ 5. Enter the Session Pooler credentials.
17
+
18
+ ## Vibe Coding Field IDs
19
+ Use the Field Inspector block to determine exact field IDs for this data source.
20
+
21
+ ## Supported Fields
22
+ Field support follows PostgreSQL column types. Standard column types (text, integer, boolean, timestamp, jsonb, uuid, etc.) are writable. Generated/computed columns are read-only.
23
+
24
+ ## Rate Limits
25
+ No Softr-imposed rate limits. Supabase handles concurrency at the database and connection-pool level. Unlimited records and unlimited concurrent users.
26
+
27
+ ## Gotchas
28
+ - **Pool Size must be 45+.** If the Supabase connection pool is too small, Softr connections will be dropped under load. Set this in Supabase Project Settings > Database before connecting.
29
+ - **Use Session Pooler credentials**, not the direct connection string. The Session Pooler is required for connection stability with Softr.
30
+ - **Row Level Security (RLS) applies at the database level.** Supabase RLS policies are enforced on all queries Softr makes. If records are missing, check your RLS policies.
31
+ - **Layer Softr visibility rules on top of RLS.** Softr's user-group visibility settings work independently of Supabase RLS. Use both for defense-in-depth access control.
32
+ - **Port is always 5432.** Do not use the Supabase API port (typically 443) -- Softr connects via PostgreSQL protocol.
33
+
34
+ ## Best For
35
+ - Developer teams comfortable with PostgreSQL
36
+ - Applications with real-time data requirements
37
+ - Projects needing fine-grained Row Level Security
38
+ - Teams wanting an open-source backend with strong community support