forked from hauru/sequelize-to-json
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
610 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Changelog | ||
|
||
## 0.9.0 | ||
|
||
Initial relase. Needs in-depth testing! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,250 @@ | ||
# sequelize-to-json | ||
Serialization of Sequelize models into JSON-compatible objects | ||
|
||
A fast, simple JSON serializer for your [Sequelize](https://github.com/sequelize/sequelize) models. Turns model instances into plain JSON-friendly objects that can be safely embedded in API responses. Suitable for serializing large numbers of instances at once. | ||
|
||
Serialization can be performed according to named _schemes_, of which any number can be defined for a model. A scheme specifies the properties to be be included or excluded in the result, and can also provide schemes to use for associated models. | ||
|
||
`sequelize-to-json` requires Node 4.4.x or later. | ||
|
||
## Installation | ||
|
||
Install it within your project's tree using NPM: | ||
|
||
`npm install --save sequelize-to-json` | ||
|
||
The module doesn't install any dependiences. It just ships with a custom minimized `lodash` build (to be removed in future releases). | ||
|
||
## Usage | ||
|
||
A nice walkthrough has yet to be written. For now, just a crude example. | ||
|
||
Assuming we already have a working Sequelize instance providing us with `User` and `BlogPost` models and at least the `author` relation: | ||
|
||
```js | ||
const | ||
Serializer = require('sequelize-to-json'), | ||
db = require('./db'), // our Sequelize instance, with models and relations already defined | ||
BlogPost = db.model('BlogPost'), | ||
User = db.model('User'); | ||
|
||
// our serialization scheme for the `BlogPost` model with the associated `User` (stored in the `author` field) | ||
const scheme = { | ||
// include all own properties and the associated `User` instance | ||
include: ['@all', 'author'], | ||
// let's exclude from the above the primary key and all foreign keys | ||
exclude: ['@pk', '@fk'], | ||
assoc: { | ||
// scheme to be used for the associated `User` instance | ||
author: { | ||
// include just a selection of fields (URLs are method calls but they could be implemented as VIRTUAL attributes as well) | ||
include: ['fullName', 'aboutMe', 'getProfileUrl', 'getAvatarUrl'], | ||
// let's assign better names to properties obtained via instance methods | ||
as: { getProfileUrl: 'profileUrl', getAvatarUrl: 'avatarUrl' } | ||
} | ||
} | ||
}; | ||
|
||
// now fetch some posts, with authors, and serialize them | ||
BlogPost.findAll({ | ||
include: { model: User, as: 'author' }, | ||
// ... | ||
}).then(function(posts) { | ||
// serialize all the items efficiently | ||
let postsAsJSON = Serializer.serializeMany(posts, BlogPost, schema); | ||
|
||
// serialize just the first item | ||
let serializer = new Serializer(BlogPost, schema); | ||
let postAsJSON = serializer.serialize(posts[0]); | ||
// ... | ||
}); | ||
``` | ||
|
||
Resulting JSONified post objects would look like this (assuming `BlogPost` contains the `title` and `content` fields): | ||
|
||
```json | ||
{ | ||
title: ..., | ||
content: ..., | ||
..., | ||
author: { | ||
fullName: ..., | ||
aboutMe: ..., | ||
profileUrl: ..., | ||
avatarUrl: ... | ||
} | ||
} | ||
``` | ||
|
||
See the [Reference](#reference) below for the complete description of `sequelize-to-json` features. | ||
|
||
## Reference | ||
|
||
### The `Serializer` class | ||
|
||
Represents a serializer. | ||
|
||
Serializer objects can process any number of instances of a given model using the specified scheme. Both the model class and the scheme are tied to a serializer object; you'll need to instantiate a new serializer for each model / scheme pair. | ||
|
||
This class is the only thing exported by the module. It can be accessed directly by requiring `sequelize-to-json`. | ||
|
||
(The class doesn't actually have a name of it's own; i'm using here the name `Serializer` just for clarity. You can import it under any name you wish.) | ||
|
||
#### `Serializer(model, [scheme, [options]])` | ||
<a id="serializer-serializer"></a> | ||
|
||
Creates a new serializer. | ||
|
||
Arguments are: | ||
|
||
* **`model`** - Sequelize model class whose instances are to be serialized | ||
* **`scheme`** - scheme to be used for serialization. Can be an object, a string or anything falsy. | ||
* **`options`** - object containing additional options (see [Options](#options)) | ||
|
||
If `scheme` is a string, the scheme definition will be searched for in `model.serializer.schemes`. If `scheme` is falsy, following steps will be performed to identify the serialization scheme: | ||
|
||
1. If `model.serializer.defaultScheme` is set, it will be interpreted as the name of scheme to use (from `model.serializer.schemes`). | ||
2. If there's no `model.serializer.defaultScheme`, the scheme is checked for in `model.serializer.schemes.default`. | ||
3. If there's no scheme named `default` in `model.serializer.schemes`, the global default scheme is used (see below). | ||
|
||
For info on defining schemes, see the [schemes section](#defining-schemes). | ||
|
||
<a id="serializer-serialize"></a> | ||
#### `#serialize(instance, [cacheObj])` | ||
|
||
Serializes a single model instance into a JSON-friendly object, according to the scheme provided in constructor. | ||
|
||
**`instance`** must be an instance of the proper model class, otherwise the method will throw an error. | ||
|
||
**`cacheObj`** should only be used if the `serialize` method is to be called repeatedly, eg. when processing a collection of model instances. In such cases, the `cacheObj` argument should be a reference to an existing plain object, which will serve as a cache for storing `Serializer` instances created recursively to process associated model instances. | ||
|
||
For example: | ||
|
||
```js | ||
let serializer = new Serializer(SomeModel, someScheme); | ||
let cache = {}, json = []; | ||
|
||
for(let item of items) { | ||
json.push(serializer.serialize(item, cache)); | ||
} | ||
``` | ||
|
||
Without passing `cache` in the `.serialize` call (or with `cache` being set inline to `{}`!) the performance will drop significantly since `Serializer` objects will have to be re-created for each item. | ||
|
||
<a id="serializer-serialize-many"></a> | ||
#### `.serializeMany(instances, model, [scheme, [options]])` | ||
|
||
Convenience static method for serializing collections of model instances. It uses the `Serializer` object cache internally so that serializers are not re-created for associated model instances. See [`#serialize()`](#serializer-serialize) for details. | ||
|
||
The `model`, `scheme` and `options` parameters are passed to the `Serializer` constructor. | ||
|
||
<a id="serializer-encode-to-json"></a> | ||
#### `.encodeToJSON(obj, [options])` | ||
|
||
Converts regular Javascript objects into JSON-friendly format. This function is used by default for the conversion of values returned by Sequelize. | ||
|
||
**`obj`** is the object (value) to be encoded. **`options`** can be used to fine-tune the encoder. | ||
|
||
Currently supported options: | ||
|
||
* **`bufferEncoding`** - how to encode binary `Buffer` contents into a string. The value gets passed to Node's `Buffer#toString()`. Default: `base64` | ||
|
||
Notes on how values are converted: | ||
|
||
* Strings, booleans, numbers and nulls are copied as is | ||
* Arrays and objects are processed recursively | ||
* Dates are stringified using `#toString()` | ||
* Buffers are stringified using `#toString()` with encoding specified in options | ||
|
||
### Defining schemes | ||
|
||
#### Scheme object | ||
|
||
Scheme are defined as plain objects. They can contain following fields: | ||
|
||
* **`include`** - a list of attributes to be included in resulting objects. Defaults to all attributes (`['@all']`). See [Attribute lists](#attribute-lists) for details. | ||
* **`exclude`** - a list of attributes to be excluded from the result. Filtering is applied to the attribute list defined by `include`. Defaults to an empty list. See [Attribute lists](#attribute-lists) for details. | ||
* **`assoc`** - an object containing schemes for associated model instances. Object's keys should correspond to names of attributes holding related instances (Sequelize's `as`). Values can be either scheme names or scheme objects. They will be passed to the `Serializer` constructor when creating serializers for associated instances. | ||
* **`as`** - can be used to rename attributes in output. Should be an object mapping model attribute names to names we would like to have in JSON. Useful for naming properties obtained from method calls (eg. to have `postExcerpt` instead of `getPostExcerpt`). | ||
* **`options`** - serializer [options](#options). They will override options passed to the `Serializer` constructor. | ||
|
||
#### Attribute lists | ||
|
||
Both the `include` and the `exclude` fields of a scheme definition should contain a list of model's attributes. But, these lists can be a little more than just plain arrays of attribute names. They can also contain: | ||
|
||
* method names (to be called with no arguments), | ||
* regular instance properties, | ||
* `@`-prefixed selectors that expand to subsets of model attributes sharing certain features. | ||
|
||
`sequelize-to-json` provides support for following attribute selectors: | ||
|
||
* **`@all`** - all attributes defined for the model | ||
* **`@assoc`** - all associations in the model | ||
* **`@pk`** - the primary key (if present) | ||
* **`@fk`** - all foreign keys | ||
* **`@doc`** - all document-type fields: `JSON`, `JSONB`, `HSTORE` | ||
* **`@blob`** - all `BLOB`s | ||
* **`@virtual`** - all `VIRTUAL` attributes | ||
* **`@auto`** - all attributes auto-generated by Sequelize (eg. `created_at`, `updated_at`...) | ||
|
||
Note: model attribute values are obtained by calling `.get()` on the model instance. This means custom getters get executed. | ||
|
||
|
||
#### Schemes inside models | ||
|
||
In order to keep things clean, serialization schemes can be kept inside models. This is done by adding a static `serializer` property to the model class. You can either set this property directly or just put it in `instanceMethods` when defining the model. | ||
|
||
Supported `serializer` fields are: | ||
|
||
* **`schemes`** - an object containing available serialization schemes (keys are names and values are scheme objects) | ||
* **`defaultScheme`** - name of the default scheme for this model. The name should exist as a proper key in `schemes`. | ||
|
||
### Options | ||
|
||
Following options can be passed to the `Serialize` constructor: | ||
|
||
* **`encoder`** - a function used to convert JS types to JSON-friendly format. It should accept an object to be encoded and can be also passed options. Default: [`Serializer.encodeToJSON()`](#serializer-encode-to-json) | ||
* **`undefinedPolicy`** - what to do with attributes that are `undefined` for the instance. Allowed policies are `Serializer.SKIP` (exclude from output), `Serializer.SET_NULL` (set their values to `null`) and `Serializer.FAIL` (throw an error). Default: `Serializer.SKIP` | ||
* **`copyJSONFields`** - whether values stored as JSON (`JSON`, `JSONB` etc.) should be copied directly without passing them through `encoder`. Having it on will improve performance a bit. Default: `true` | ||
* **`simpleDates`** - whether `DATEONLY` fields should be encoded in `YYYY-MM-DD` format with timezone offset applied. If set to `false`, they will be stringified as full date-times. Default: `true` | ||
* **`encoderOptions`** - options to be passed to `encoder` (as the second argument) | ||
|
||
## Tips | ||
|
||
### Adding serialization methods to models | ||
|
||
To save on some typing and `require`ing across the application code, you can add serialization methods globally to all your models. Just use `define.classMethods` and `define.instanceMethods` options during Sequelize instantiation: | ||
|
||
```js | ||
const db = new Sequelize(..., { | ||
//... | ||
define: { | ||
classMethods: { | ||
serializeMany: function(data, scheme, options) { | ||
return Serializer.serializeMany(data, this, scheme, options); | ||
}, | ||
//... | ||
}, | ||
instanceMethods: { | ||
serialize: function(scheme, options) { | ||
return (new Serializer(this.Model, scheme, options)).serialize(this); | ||
}, | ||
//... | ||
} | ||
}, | ||
//... | ||
}); | ||
|
||
``` | ||
|
||
Now you can use them like so: | ||
|
||
```js | ||
// for single object | ||
let json = myModelInstance.serialize('someScheme'); | ||
// for many objects | ||
let json = MyModel.serialize(instances, 'someScheme'); | ||
``` | ||
|
||
Of course you can provide the scheme as an object and pass serializer options as the last argument for both methods. | ||
|
||
|
Oops, something went wrong.