Model Scripting

Learn about scripting with data models.

CRUD

Create

let newList = new List().title('x');

Read

let listId = '...';
let list = List.get(listId);

// Because of error handling,
// 'list' is always defined at this point
let listTitle = list.title;

Update

// field/property setter:
list.title = 'new title';

// fluent setter:
list.title('new title')
    .otherField('value');

Delete

let listId = '...';
List.get(listId).remove();

html

Model fields have a number of conveniences for working with HTML forms.

Containers

Since containers create a "scope" for working with other data, a container must be selected before any other operations on its contained data.

To execute code in the context of selected container, pass a function to the container's variable. The return value of the function will be passed back as the return value of selecting the container.

Create Child

import {List,Note} from '📦';

let list = new List();

let childNote = list(()=>{
  return new Note().title('My Task');
});

Query Children

/list/tasks/GET.js
// Load the container 'List'
let listId = '...';
let myList = List.get(listId);

// Select myList, and get all Note titles for myList:
let titleStream = myList(()=>{
   return Note.all().map((n)=>n.title));
});

// Output JSON of Note titles:
titleStream;

Or, the most compact form of same above statements:

let listId = '...';
List.get(listId)(()=>Note.all().map((n)=>n.title)));

Dynamic Endpoint

When working with containers from a dynamic endpoint path the container selection has already occurred:

/list/{list}/tasks/GET.js
import {list} from '🔗';

// Output JSON of Note titles:
Note.all().map((n)=>n.title));

Transactions

All model operations (create, update, delete/remove), which happen within a single request are committed together / atomically. There is no way to end up in a state where the first few models have been persisted, but later changes haven't.

Error Handling

In several examples above, note that null or undefined is not used to represent the absence of a result -- for example List.get and Article.slug never return a null/undefined value.

Instead, an exception is thrown if there is no result. This exception can be left unhandled, and therefore propagated up to an error endpoint. Or it can be specifically caught using a special nomenclature:

let thingId = '....';
let thing = null;
try {
    thing = Thing.get(thingId);
} catch ($ModelNotFound){
    // At this point we've specifically caught $ModelNotFound,
    // as opposed to catching an unrelated error/exception.
    thing = new Thing();
}
// At this point 'thing' is always defined.
thing.updateField = 'x';