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 Scopes (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