JSORM the isomorphic, framework-agnostic Javascript ORM
Reads
The interface for read operations is a simpler version of the ActiveRecord Query Interface. Instead of generating SQL, we’ll be generating JSONAPI requests.
Basic Finders
Execute queries with .all()
, find()
, or .first()
:
let response = await Post.all()
response.data // array of Post instances
Post.all().then(function(response) {
response.data // array of Post instances
});
GET /posts
let response = await Post.find(123)
response.data // Post instance
Post.find(123).then(function(response) {
response.data // Post instance
});
GET /posts/123
let response = await Post.first()
response.data // Post instance
Post.first().then(function(response) {
response.data // Post instance
});
GET /posts?page[size]=1
Composable Queries with Scopes
The beauty of ORMs is their ability to compose queries. We’ll be doing
this by chaining together Scope
s (query fragments). All of the methods
you see on this page can be chained together - the request will not fire
until the chain ends with all()
, first()
, or find
. Example:
let scope = Post.order({ name: "desc" })
if (someCheckboxIsChecked) {
scope = scope.where({ important: true })
} else {
scope = scope.where({ important: false })
}
scope.all() // request fires
var scope = Post.order({ name: "desc" });
if (someCheckboxIsChecked) {
scope = scope.where({ important: true });
} else {
scope = scope.where({ important: false });
}
scope.all() // request fires
/posts?sort=-name&filter[important]=true
/posts?sort=-name&filter[important]=false
In practice, you’ll probably have some scopes you want to re-use across different contexts. A best practice is to store these scopes as class methods (static methods) in the model:
class Post extends ApplicationRecord {
// ... code ...
static superImportant() {
return this
.where({ ranking_gt: 8 })
.order({ ranking: 'desc' })
.stats({ total 'count' })
}
}
// get 10 super important posts
let scope = Post.superImportant().per(10)
scope.all() // fire query
const Post = ApplicationRecord.extend({
// ... code ...
static: {
superImportant() {
return this
.where({ ranking_gt: 8 })
.order({ ranking: 'desc' })
.stats({ total 'count' })
}
}
})
// get 10 super important posts
var scope = Post.superImportant().per(10);
scope.all() // fire query
/posts?sort=-ranking&stats[total]=count&page[size]=10&filter[ranking_gt]=8
Metadata
The meta information of the JSONAPI response is available as a POJO on the response:
let response = await Post.all()
response.meta // { stats: { total: { count: 100 } } }
await Post.all().then(function(response) {
response.meta // { stats: { total: { count: 100 } } }
})
Promises and Async/Await
The result of all()
, first()
or find
is a Promise. The promise will resolve to a Response
object.
A Response
object has three keys - data
, meta
, and raw
. data
- the one
you’ll be using the most - will be a Model
instance (or array of
Model
) instances. meta
will be the Meta Information returned by the API (mostly used for statistics in our case). raw
is only used to introspect the raw response document.
Post.all().then((response) => {
response.data // array of Post instances
response.meta // js object from the server
response.raw // js response document
})
Post.all().then(function(response) {
response.data // array of Post instances
response.meta // js object from the server
response.raw // js response document
});
/posts
Hopefully you’re running in an environment that supports ES7’s Async/Await. This makes things even easier:
let { data } = await Post.all()
data // array of Post instances
// alternatively
let posts = (await Post.all()).data
posts // array of Post instances
/posts