Driving Data from Script: The LiveCode Datastore API 
Most people meet the datastore through the collections palette and the visual actions editor. However, underneath that is a full scripting API you can call directly. Once you know it’s there, you can build custom navigation, validation, bulk operations, and “save as draft” flows simply using script. This post walks through the datastore commands and functions, grouped by what you’re trying to do.
First, the one pattern that explains everything
Before the individual commands, it’s worth understanding the convention almost every data-fetching handler shares. The public handlers accept three optional trailing parameters:
pSynchronous(pass true to get the result inline, otherwise the call runs asynchronously)pCallbackHandler(the name of a handler to call when the request finishes)pCallbackContext(any value you want handed back to you).
When the request completes, your callback is invoked with three parameters in this order:
- An error (empty unless something went wrong)
- The data for the request
- The context you originally passed
So a callback looks like this:
on myDataFetched pError, pData, pContext
if pError is not empty then
answer "Something went wrong:" && pError
exit myDataFetched
end if
-- pData holds the result; pContext is whatever you sent in
end myDataFetched
Keep in mind: anything that talks to the server follows it.
Reading records from a collection 
A lot of datastore functions read from a local cache, which means they return immediately — no callback needed.
To read a whole record you use:
datastoreGetDataOfRecordOfCollection(collectionName, recordId)
which returns the record as an array keyed by field name.
To read a single field:
datastoreGetDataOfFieldOfRecordOfCollection(collectionName, fieldName, recordId)
returns just that value.
The catch most people hit is that these take a record ID, not a record number.
To convert use:
datastoreGetRecordIdAtIndex(collectionName, number)
So reading the “name” field of the third record looks like:
local tId, tName
put datastoreGetRecordIdAtIndex("Contacts", 3) into tId
put datastoreGetDataOfFieldOfRecordOfCollection("Contacts", "name", tId) into tName
If you just want a record count:
datastoreGetCachedCollectionRecordCount(collectionName)
gives you the number of records without fetching anything.
Knowing and setting the current record
Each collection tracks a “current record.” Read it with: datastoreCurrentRecordOfCollection(collectionName) — note this returns the record ID.
To turn that into a human-friendly position, pass it through: datastoreGetIndexOfRecordId(collectionName, recordId).
Going the other way, set the current record by ID with: datastoreSetCurrentRecordOfCollection(collectionName, recordId)
typically resolving a number first.
datastoreSetCurrentRecordOfCollection "Contacts", \
datastoreGetRecordIdAtIndex("Contacts", 5)
That single line is exactly what the “Go to record number” action does under the hood, so building your own next/previous buttons is trivial: read the current index, add or subtract one, and set it.
Creating, editing, and deleting records 
To create a record immediately:
datastoreCreateNewRecordInCollection collectionName, goToRecord
adds one, where the second parameter is a boolean controlling whether the current record jumps to the new one.
To edit:
datastoreSetFieldOfCurrentRecord collectionName, fieldName, value
writes a single field on the current record and helpfully, it works whether that record is already committed or still a draft.
To delete the current record use:
datastoreDeleteCurrentRecord collectionName
which also knows to simply discard the record if it happens to be an unsaved draft.
datastoreCreateNewRecordInCollection "Contacts", true
datastoreSetFieldOfCurrentRecord "Contacts", "name", "Ada Lovelace"
datastoreDeleteCurrentRecord "Contacts"
Draft records: editing before you commit 
Instead of writing straight to the server, you can create a draft - an in-memory record the user can edit freely and then either save or throw away.
Create a draft with:
datastoreCreateDraftRecord collectionName
Or pre-populate it using:
datastoreCreateDraftRecordWithData collectionName, dataArray where the array is keyed by field name.
While editing, the same datastoreSetFieldOfCurrentRecord writes into the draft.
To commit a draft record:
datastoreCommitDraftRecord collectionName persists it to the server.
To discard:
datastoreDiscardDraftRecord collectionName drops it with no trace.
There are also bulk versions. datastoreCommitAllDraftRecords and datastoreDiscardAllDraftRecords for “save everything” or “discard everything” buttons.
To drive your UI, two predicates tell you the draft state: datastoreHasDraftRecord(collectionName) and datastoreHasAnyDraftRecords()
A classic use is guarding navigation so users don’t lose unsaved work.
if datastoreHasAnyDraftRecords() then
answer "You have unsaved changes." with "Save" or "Discard"
if it is "Save" then
datastoreCommitAllDraftRecords
else
datastoreDiscardAllDraftRecords
end if
end if
Fetching collections from the server
Finally, when you genuinely need to talk to the server rather than the cache, datastoreGetCollections is the entry point, and it’s the clearest illustration of the async convention from the top of this post. Call it with false for asynchronous, a callback name, and any context; your callback receives the standard error, data, context trio:
datastoreGetCollections false, "collectionsReady", empty
on collectionsReady pError, pCollections, pContext
if pError is empty then
-- pCollections is an indexed array of your collections
end if
end collectionsReady
Coming Next… 
While the scripting API doesn’t replace the visual binding tools for everyday work, having it in your back pocket gives you the knowledge and flexibility to easily customize how you use data within your app and debug where things may be going wrong.
I’ll be covering the script API for dataviews and media assets in upcoming posts so make sure to keep an eye on the forum over the next couple of weeks!