Skip to content

Instantly share code, notes, and snippets.

@amk221
Last active May 8, 2018 16:08
Show Gist options
  • Save amk221/87e5f358e5938eb42e822138592e5d00 to your computer and use it in GitHub Desktop.
Save amk221/87e5f358e5938eb42e822138592e5d00 to your computer and use it in GitHub Desktop.
Sequelize JSON API formatter
/**
* Outputs the result of a Sequelize query for JSONAPI
*
* e.g.
* let foo = yield models.Foo.findById(this.params.fooId);
* this.body = output(foo);
*
* Results in:
*
* {
* data: {
* type: 'Foo',
* id: 123,
* attributes: {
* myAttr: 'abcdef'
* },
* relationships: {
* bar: {
* data: {
* id: 456,
* type: 'Bar'
* }
* }
* }
* },
* included: [{
* type: 'Bar',
* id: 546,
* attributes: {
* myAttr: 'ghijkl'
* }
* }]
* }
*
*/
const _ = require('lodash');
const { dasherise } = require('./string');
function output(arg) {
if (Array.isArray(arg)) {
return outputMany(arg);
} else {
return outputOne(arg);
}
}
function getBasic(model) {
return {
type: dasherise(model.Model.name),
id: model.id.toString()
};
}
function getAttributes(model) {
return Object.keys(model.rawAttributes).reduce((attributes, name) => {
let attribute = model.rawAttributes[name];
if (!attribute.references) {
attributes[name] = model[name];
}
return attributes;
}, {});
}
function getAssociations(model) {
return Object.keys(model.Model.associations).reduce((associations, name) => {
let association = model[name];
if (Array.isArray(association)) {
association.forEach(r => associations.push(r));
} else {
associations.push(association);
}
return associations;
}, []);
}
function unique(model) {
return `${model.Model.name}:${model.id}`;
}
function getBasicRelationships(associations) {
return associations.reduce((relationships, association) => {
let name = dasherise(association.Model.name);
relationships[name] = getBasic(association);
return relationships;
}, {});
}
function getIncluded(associations) {
return associations.map(association => {
let data = getBasic(association);
data.attributes = getAttributes(association);
return data;
});
}
function getResult(model, associations) {
let result = getBasic(model);
result.attributes = getAttributes(model);
result.relationships = getBasicRelationships(associations);
return result;
}
function outputOne(model) {
let associations = getAssociations(model);
return {
data: getResult(model, associations),
included: getIncluded(associations)
};
}
function outputMany(models) {
let results = [];
let allAssociations = [];
models.forEach(model => {
let associations = getAssociations(model);
let result = getResult(model, associations);
allAssociations.push(...associations);
results.push(result);
});
return {
data: results,
included: getIncluded(_.uniqBy(allAssociations, unique))
};
}
module.exports = output;
@mvilrokx
Copy link

mvilrokx commented Mar 1, 2018

This is pretty cool.

Question: Am I understanding correctly that this is actually performing 2 queries rather than getting everything in one go (using include in findBy)? Wouldn't that become a DB performance issue if you have many associations in a model?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment