Skip to content

Instantly share code, notes, and snippets.

@jkantr
Last active April 10, 2018 18:25
Show Gist options
  • Save jkantr/87a62b5629d616bb9f814be1b0de0494 to your computer and use it in GitHub Desktop.
Save jkantr/87a62b5629d616bb9f814be1b0de0494 to your computer and use it in GitHub Desktop.
Monstrosity of Objection.js model configuration and injection
// app.js
// ...
const models = require('./models/');
const state = {
models,
logger,
errorReporter,
};
// ...
// db/model.js
const { Model } = require('objection');
const knex = require('./connection');
Model.knex(knex);
// for global / base class configuration things
class BaseModel extends Model {
$beforeInsert() {
this.createdAt = new Date().toISOString();
}
$beforeUpdate() {
this.updatedAt = new Date().toISOString();
}
}
module.exports = { Model, BaseModel };
// models/index.js
const baseModels = require('../db/model');
// list of models to initiate and configure - add to this array to add a new model file in this dir
const modelNames = ['User', 'Foo', 'Bar', 'Baz', 'Qux'];
/**
* require all of the models with their base configuration (no relations),
* this is the only place in the application models should be required directly.
*/
const bareModels = modelNames.reduce((acc, modelName) => {
return { ...acc, [modelName]: require(`./${modelName}`)(baseModels) };
}, {});
/**
* now we can configure the model's relations with no circular depedencies
* because they were all previously initiated.
*/
const configuredModels = Object.keys(bareModels).reduce((acc, modelName) => {
return { ...acc, [modelName]: bareModels[modelName].configure(bareModels) };
}, {});
/**
* now we can patch on the Model and BaseModel from objection so they can
* be reused throughout the application
*/
module.exports = { ...configuredModels, ...baseModels };
// models/User/index.js
const $beforeInsert = require('./$beforeInsert');
const relationMappings = require('./relationMappings');
module.exports = ({ Model, BaseModel }) => {
class User extends BaseModel {}
User.tableName = 'users';
User.idColumn = 'user_id';
User.jsonSchema = {
type: 'object',
required: ['email', 'password', 'invitation', 'firstName', 'lastName'],
properties: {
userId: { type: 'integer' },
type: { type: 'string' },
role: { type: ['string', 'null'] },
email: { type: 'string' },
hash: { type: 'string' },
firstName: { type: 'string' },
lastName: { type: 'string' },
},
};
User.configure = (models) => {
const Models = { ...models, Model };
User.relationMappings = relationMappings(Models);
User.prototype.$beforeInsert = $beforeInsert(Models);
return User;
};
return User;
};
// models/User/relationMapping.js
module.exports = (models) => {
const { Model, Foo, Bar, Baz, Qux } = models;
return {
foos: {
relation: Model.HasManyRelation,
modelClass: Foo,
join: {
from: 'users.user_id',
to: 'fooss.user_id',
},
},
bars: {
relation: Model.HasManyRelation,
modelClass: Bar,
join: {
from: 'users.user_id',
to: 'bars.user_id',
},
},
bazs: {
relation: Model.HasManyRelation,
modelClass: Baz,
join: {
from: 'users.user_id',
to: 'basz.user_id',
},
},
quxs: {
relation: Model.HasManyRelation,
modelClass: Qux,
join: {
from: 'users.user_id',
to: 'quxs.user_id',
},
},
};
};
// models/User/$beforeInsert.js
const scrypt = require('scrypt-for-humans');
const { InvalidInvitation, UsedInvitation } = require('../../errors');
module.exports = models => async function userBeforeInsert({ transaction: trx }) {
const { Invitation, User } = models;
// find the invitation
const inv = await Invitation.query(trx).findOne('code', this.invitation);
if (!inv) {
// if no invitation is found
throw new InvalidInvitation();
} else if (inv.isUsed === true) {
// if the invitation was already redeemed
throw new UsedInvitation();
}
await Invitation
.query(trx)
.patch({ isUsed: true })
.where('invitationId', inv.invitationId);
this.type = inv.type;
const hash = await scrypt.hash(this.password);
this.hash = hash;
this.$omit(['password', 'passwordAgain', 'invitation']);
return Object.getPrototypeOf(User.prototype).$beforeInsert();
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment