Adalo Google Sheets integration: sheet as database - SheetDB

How to connect Adalo to Google Sheets

Adalo builds native mobile and web apps without code, and its External Collections feature lets a list, a form or a detail screen work on records that live outside Adalo, in any REST API that returns JSON. A Google Sheet is often the data source people want: a menu, a member list, an inventory, a set of events that someone on the team already maintains in a spreadsheet.

The problem is that Google's own Sheets API does not look like what Adalo expects. It returns ranges of cell values rather than a list of records, it needs OAuth and a Google Cloud project, and it has no "update the row where id is 4" call. SheetDB sits in between: it turns the spreadsheet into a JSON REST API where every row is an object with the column names as keys, and where reading, creating, updating and deleting are plain HTTP calls. That is exactly the shape of API an Adalo External Collection is designed for.

This guide connects an Adalo app to a real demo spreadsheet, this one, exposed at https://sheetdb.io/api/v1/58f61be4dda40. Its first tab has the columns id, name, age and comment, and it resets every 15 minutes, so you can create and delete records while testing.

What you need

  • An Adalo app on a plan that includes External Collections and Custom Actions. Adalo's documentation lists the Professional, Team and Business plans at the time of writing; check Adalo's pricing page for the current rule.
  • A Google Sheet whose first row holds the column names and whose first column is a numeric id. Adalo identifies records by a field called id and supports only numeric ids, so this column is not optional. Column names become JSON keys; keep them lowercase and without spaces.
  • A SheetDB account; the free plan is enough to build and test.

Step 1: prepare the sheet and create the API

  1. Put the column names in row 1 and make column A the id. Number the existing rows 1, 2, 3 and so on. New rows created from the app will get their id automatically, see the create step below.
  2. Sign in to SheetDB with Google, click Create new API, paste the spreadsheet URL and save. You get an API URL in the form https://sheetdb.io/api/v1/<your id>.
  3. Open the URL in a browser. The rows of the first tab come back as a JSON array of objects, which is the format Adalo's Get All endpoint expects:
[
  { "id": "1", "name": "Tom",   "age": "15", "comment": "" },
  { "id": "2", "name": "Alex",  "age": "24", "comment": "" },
  { "id": "3", "name": "John",  "age": "51", "comment": "" },
  { "id": "4", "name": "Steve", "age": "22", "comment": "special" },
  { "id": "5", "name": "James", "age": "19", "comment": "" }
]

Every value is a string, because that is what a cell holds, and Adalo requires the id of an external record to be a JSON number, not text. Adding ?cast_numbers=id,age to the read URLs returns those two columns as numbers, so Adalo detects id and age as Number properties: the id works as the record id, and the age can be sorted and compared. The other columns stay text.

Step 2: add an External Collection in Adalo

  1. In the Adalo editor open the Database panel in the left toolbar, click Add Collection and choose External Collection.
  2. Enter a Collection Name, for example People, and the API Base URL: https://sheetdb.io/api/v1/58f61be4dda40.
  3. If you enabled authentication on the SheetDB API (recommended once you go beyond the demo), click Add Item under the authorization settings and add a Header named Authorization with the value Bearer <your token>, or the Basic Auth header your API settings show. The authentication docs list both options.
  4. Click Next. Adalo shows the five endpoints of a collection, each with a Method and a URL. Adalo pre-fills them from the base URL; replace them with the values in the next step.

Step 3: configure the five endpoints

Adalo substitutes {{id}} in a URL with the id of the record the user is working with. SheetDB addresses rows as /{column}/{value}, so the placeholder goes after /id/. Enter these settings:

Get All Records   GET     https://sheetdb.io/api/v1/58f61be4dda40?cast_numbers=id,age
Get One Record    GET     https://sheetdb.io/api/v1/58f61be4dda40/search?id={{id}}&single_object=true&cast_numbers=id,age
Create a Record   POST    https://sheetdb.io/api/v1/58f61be4dda40
Update a Record   PUT     https://sheetdb.io/api/v1/58f61be4dda40/id/{{id}}
Delete a Record   DELETE  https://sheetdb.io/api/v1/58f61be4dda40/id/{{id}}
  • Get All Records reads the whole tab. Leave Results Key empty: SheetDB returns the array at the top level, not nested under a key.
  • Get One Record uses the search endpoint to find the row whose id matches. single_object=true makes SheetDB return that one row as an object instead of a one-element array, which is what Adalo expects from Get One.
  • Create a Record can stay at its default, a POST to the API URL. Adalo's own Create action sends the record as a flat JSON object, which SheetDB accepts without the data wrapper (create docs), but it cannot put the INCREMENT keyword into a Number property, so rows created this way would have an empty id. The add section below creates rows with a Custom Action instead.
  • Update a Record and Delete a Record address the row by its id. SheetDB accepts PUT and PATCH on the same URL, so Adalo's default PUT is fine (update, delete).

Run the test of the connection. Adalo calls Get All, reads the records and creates the collection's properties from them: id (Number), name, age (Number) and comment. Check that id was detected as a Number and save the collection. The response of Get One for id 4 looks like this:

{ "id": 4, "name": "Steve", "age": 22, "comment": "special" }

Read, add, update, delete

Once the collection exists, the sheet behaves like any other Adalo collection in the components you already know. Here is how each operation maps to the API.

Read: a list and a detail screen

Add a List component to a screen and set What is this a list of? to People. Bind the text elements inside the list item to Current Person → name and → age. Adalo calls Get All when the screen opens and renders one item per row of the sheet. Link the item to a detail screen and, on that screen, use Current Person → comment and the other properties: Adalo fetches that record with Get One.

To show only some rows, use the search endpoint as the Get All URL. This variant lists only rows whose comment is "special":

https://sheetdb.io/api/v1/58f61be4dda40/search?comment=special&cast_numbers=id,age

Search values accept * as a wildcard, ! for negation and >, <, >=, <= for comparisons; sort_by, sort_order, limit and offset are also available (read docs). Filtering on the API side keeps the payload small on mobile; Adalo's own list filters still work on top of it.

Add: create a record with a Custom Action

A new row needs a unique numeric id, and the app cannot compute one: it does not know the highest id in the sheet. SheetDB does, and fills it in when a request sends the keyword INCREMENT (the highest number in the column plus one). The Create action of the External Collection cannot send that keyword, because id is a Number property, so create rows with a Custom Action, which sends whatever JSON body you write:

  1. On a screen, add three Text Input components for the name, the age and the comment, and a Button.
  2. Select the button, click Add Action and choose Custom Action → New Custom Action. Name it Add person, set Method to POST and URL to https://sheetdb.io/api/v1/58f61be4dda40. Under Headers add Content-Type with the value application/json, plus the Authorization header from step 2 if your API is protected.
  3. Add three inputs to the action, name, age and comment, each with an example value, and enter the body below. Replace the example values with the Magic Text of the matching input, and leave INCREMENT as it is, in quotes.
  4. Click Run Test Request. The API answers {"created": 1} and a row with the example values appears at the bottom of the demo sheet. Save the action, then, back on the button, map each input to the Text Input on the screen and add a Link action to the list screen so the user sees the new record.
{
  "data": [
    { "id": "INCREMENT", "name": "Maria", "age": "30", "comment": "" }
  ]
}

Keep the quotes around every value, including the age: a cell is text, and an empty input would otherwise produce invalid JSON. Custom Actions cannot run as the submit action of a Form component, which is why the screen uses separate inputs and a button; they send one record per tap, which is what a form needs.

Update: edit a record

On the detail screen add a Form with the collection People and the action Update → Current Person, or a button with an Update action that sets individual properties. Adalo sends a PUT to /id/4 with the new values; SheetDB finds the row where id is 4, writes the fields it received and answers {"updated": 1}. Cells you do not send keep their values.

Delete: remove a record

Add a button with the action Delete → Current Person, followed by a Link action back to the list. Adalo sends DELETE …/id/4 and the API responds {"deleted": 1}; the row disappears from the sheet. Because SheetDB clears its cache after every write, the list shows the change as soon as Adalo reloads it.

Tips

  • Several tabs. One SheetDB API covers the whole spreadsheet. Add ?sheet=Orders (or &sheet=Orders after other parameters) to the five URLs to build a second External Collection on another tab.
  • Cache. Read responses are cached for 15 seconds by default, adjustable per API (cache docs). Writes made through the app bypass it; edits typed into the spreadsheet by hand appear in the app within the cache time.
  • Permissions. If the app only reads, disable Create, Update and Delete in the API settings; keep Read and Search on, because Get One uses the search endpoint. If it writes, enable authentication and put the header in the collection, as in step 2, so the URL alone cannot modify the sheet.
  • Quota. Each screen load that shows the list is one request, each record action another. The limits page lists the rate limits; filtering with search and caching keep the count down.
  • Empty cells. An empty cell comes back as an empty string, not null. Use Adalo's visibility conditions ("is not empty") when a field may be blank.

Limitations: when not to use a sheet as your Adalo database

  • User accounts and private data. Adalo's Users collection, login and per-user permissions only work with Adalo's own database. An External Collection returns the same rows to every user who can open the screen, so keep personal data in Adalo and use the sheet for shared content.
  • Relationships. A sheet has no relationship properties. Store the related row's id in a column and use a second External Collection, or denormalise the data.
  • Volume. Get All returns every row of the tab (or every matching row). Hundreds of rows are fine on a phone; tens of thousands are not. The guide on Google Sheets as a database goes through the limits of Google Sheets itself.
  • Text ids. Adalo requires numeric ids. If your sheet already uses codes such as SKU-102, add a numeric id column next to them.

Within those limits the combination is hard to beat for small businesses and internal tools: the team keeps editing the spreadsheet they know, the Adalo app reads and writes the same rows, and the API is shared with your website, a Bubble app or an n8n or Make automation.

FAQ

Does Adalo integrate with Google Sheets directly?

Not natively. Adalo reads outside data through External Collections, which need a REST API that returns a JSON array of records with a numeric id. Google's Sheets API does not return that shape and needs OAuth, so you put SheetDB in between: it exposes the sheet as exactly the kind of API Adalo expects.

Why does my sheet need an id column?

Adalo identifies every record of an External Collection by a field named id and requires it to be a number; text ids and UUIDs are not supported. The id is what Adalo appends to the Get One, Update and Delete URLs. Add an id column as the first column of the sheet, return it as a number with cast_numbers=id in the read URLs, and let SheetDB fill it with INCREMENT when the app creates a record through the Custom Action.

Can I filter an Adalo list by a column in the sheet?

Yes, in two places. In the External Collection use the search endpoint as the Get All URL, for example /search?comment=special, so only matching rows are loaded, or add Adalo's own filters to the list component after the rows arrive. Server-side filtering keeps the response small and the app fast.

Does the Adalo Google Sheets integration cost anything?

External Collections and Custom Actions are paid Adalo features (Adalo's documentation lists the Professional, Team and Business plans at the time of writing). On the SheetDB side the free plan is enough to build and test the app; every list load and every record action is one request.

Will changes in the sheet show up in the app right away?

Records the app creates, updates or deletes through the API clear the SheetDB cache, so the app sees them immediately. Edits made by hand in the spreadsheet appear within the cache duration, 15 seconds by default, adjustable in the API settings.

Can the same sheet feed my Adalo app and my website?

Yes. The API URL is the same for every client, so a table on your website, a WordPress page and the Adalo app all read and write the same rows. That is a common setup for a small business: staff edit the sheet, customers use the app.

Give your Adalo app a Google Sheet backend

Sign in with Google, paste the spreadsheet URL and use the API URL as the base URL of an External Collection. Free plan, no credit card, no Google Cloud project.

Create free API

Have question?

If you have any questions feel free to ask us via chat or .