Flitter uses a lightweight ORM to provide MongoDB models (flitter-orm
). Model schemas are defined in files and loaded when Flitter initializes. MongoDB connection info is specified in the environment configuration. The schema for these models provides a rich way of defining structure in MongoDB.
Defining a Model
We can use the ./flitter
command to generate a new model. Let's create a basic model to keep track of a particular user's blog:
./flitter new model blog:Blog
This will create a new model file, app/models/blog/Blog.model.js
with the following template:
const Model = require('flitter-orm/src/model/Model')
/*
* Blog Model
* -------------------------------------------------------------
* Put some description here!
*/
class Blog extends Model {
static get schema() {
// Return a schema here.
return {
some_field: String,
}
}
// Static and instance methods can go here
}
module.exports = exports = Blog
The static getter for schema
tells Flitter's ORM how to format the data on save. There are a few primitive types, and more advanced embedded types you can use. See the flitter-orm
schema page for more info, but let's fill in the Blog
model's schema:
const Model = require('flitter-orm/src/model/Model')
const { ObjectId } = require('mongodb')
/*
* Blog Model
* -------------------------------------------------------------
* Put some description here!
*/
class Blog extends Model {
static get schema() {
// Return a schema here.
return {
name: String,
owner_user_id: ObjectId,
active: { type: Boolean, default: true },
follower_user_ids: [ObjectId],
}
}
// Static and instance methods can go here
}
module.exports = exports = Blog
Here, we defined a few useful fields. The name
field will coerce to a string, the owner_user_id
refers to the ObjectId of the user model record that owns the Blog, the active
flag tells us if the Blog is enabled (it defaults to true
), and an array of ObjectId's of the user model records of users that follow that blog.
This is a cursory overview of the format for schemata. You can find more information about it on the flitter-orm
doc pages.
Relationships
There is an implicit relationship here between a Blog and a User (as defined by the owner_user_id
field). We can make this relationship explicit (and therefore accessible directly) using the Model
class' built-in relationship operators:
const Model = require('flitter-orm/src/model/Model')
const { ObjectId } = require('mongodb')
// Require the user model for this application.
// This is usually provided by the flitter-auth package.
const User = require('../auth/User.model')
/*
* Blog Model
* -------------------------------------------------------------
* Put some description here!
*/
class Blog extends Model {
static get schema() {
// Return a schema here.
return {
name: String,
owner_user_id: ObjectId,
active: { type: Boolean, default: true },
follower_user_ids: [ObjectId],
}
}
// Static and instance methods can go here
async owner() {
// Define the relationship to the User model where Blog.owner_user_id corresponds to User._id (the ObjectId).
return this.belongs_to_one(User, 'owner_user_id', '_id')
}
}
module.exports = exports = Blog
We defined a method, owner
, that defines the relationship of the Blog to the User model and retrieves a single User record by matching the owner_user_id
field on the Blog model. The relationships are covered in more detail in the flitter-orm
docs.
Using Models
When Flitter starts, it loads each of the schemata and injects dependencies into each of the models. These models can then be accessed by requiring the model file, or (preferrably) through the global models
service using their Flitter canonical names. Here's an example of querying a Blog instance from the Flitter shell:
(flitter) ➤ Blog = _services.models.get('blog:Blog')
(flitter) ➤ User = _services.models.get('auth:User')
(flitter) ➤ glmdev_user = await User.findOne({uid: 'glmdev'})
User {
uid: 'glmdev',
provider: 'flitter',
data: '{}',
uuid: 'cf699085-9806-4ca3-bcf6-e804ecaf2343',
roles: [],
permissions: [],
_id: 5e3312c12685b72f9b61935e
}
(flitter) ➤ new_blog = new Blog({name: "Garrett's Blog", owner_user_id: glmdev_user._id})
(flitter) ➤ await new_blog.save()
Blog {
name: "Garrett's Blog",
owner_user_id: 5e3312c12685b72f9b61935e,
follower_user_ids: [],
_id: 5e3313302685b72f9b61935f,
active: true
}
(flitter) ➤ await new_blog.owner()
User {
uid: 'glmdev',
provider: 'flitter',
data: '{}',
uuid: 'cf699085-9806-4ca3-bcf6-e804ecaf2343',
roles: [],
permissions: [],
_id: 5e3312c12685b72f9b61935e
}
(flitter) ➤ .exit
Here, we queried an instance of the User
model, and created a new Blog instance associated with the user and saved it. Then, we were able to use the owner
relationship we created to fetch the associated user.
By the way: that bit there was the Flitter shell. It's like the standard Node REPL, except it is started after your application has started. It allows you to interact with your application's services, models, controllers, and configuration from the command line and is a very valuable development tool. You can start it using the built-in
./flitter
command:
./flitter shell
Or, if you want to be able to use async/await from the shell (which makes working with Flitter nicer), you can start it with
await
support:
node --experimental-repl-await flitter shell
Next: Controllers