npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

sequelize-modeler

v1.0.17

Published

Extended model class for sequelize with rest api.

Downloads

34

Readme

#sequelize-modeler

License: MIT npm version node

sequelize-modeler is powerful set of tools built in around Sequelize and Express focused in REST API functionalities such as aliased fields, dynamic database connections, powerful query filtering and formatting

##Install

$ npm install --save sequelize-modeler

##Usage

####Defining a Model:

// SomeModel.js
const Model = require('sequelize-modeler').Model

module.exports = class SomeModel extends Model{
    static init(sequelize, DataTypes, connectionId){
        let config = {
            options:{
                tableName: 'MyTable',
                primaryKey: 'myPrimaryKey',
                dateFormat: 'YYYY-DD-MM HH:mm:ss', // (optional) any moment format
                connectionId,
                sequelize,   
            },
            fields: {
                // field, alias, label and other properties are all optional, you can define your model normaly and they will be auto generated
                MyColumn:{
                    field: 'MyColumn',
                    as: 'MyColumnCustomName',
                    label: 'Name The Column Should be Showed With',
                    type: DataTypes.STRING,
                    defaultValue: 'Some Value',
                    allowNull: false,
                }
                ...
            },
        }
    }
}

####Initializing a Model:

Quick start:

const Catalog = require('sequelize-modeler').Catalog
// initialize your sequelize instance
const sequelize = new Sequelize(...options)

// register your connection to the catalog
// connectionId will be a generated unique id
const connectionId = Catalog.setConnection(sequelize)

// initialize your models
// all models are returned mapped with their classnames and ready to use
const {MyModel, MyOtherModel, ...etc} = Catalog.initialize([...ModelClasses], connectionId)

MyModel.findAll().then(instances => {
    //do stuff here
})

Defining your own connection ids:

Catalog.setConnection('your id',sequelizeInstance1)
Catalog.setConnection('other id',sequelizeInstance2)

using a model:


Getting a custom formatted instance:

For the following Model Fields:

...
    fields:{
        NumberColumn:{
            field: 'NumberColumn',
            as: 'number',
            type: DataTypes.INTEGER,
        },
        DateColumn:{
            field: 'DateColumn',
            as: 'date',
            type: DataTypes.DATE,
            format: 'dddd, MMMM Do YYYY, h:mm:ss a'
        },
    }
...

You have to call the format() method of an instance

let instance = await theModel.findOne({...})
console.log(instance.format())

Output would be

{
    "number": 0,
    "date": "Sunday, February 14th 2010, 3:25:50 pm"
}

Getting your model meta-data:

theModel.getFields()

Output would look like this

[
    {
        "visible": true,
        "filterable": true,
        "key": "columnCustomName", // field.as
        "label": "UI Label",
        "dataType": "decimal",
        "required": true,
        "autoIncrement": false
    },
    {
        "visible": true,
        "filterable": true,
        "key": "otherField",
        "label": "otherField",
        "dataType": "char",
        "required": true,
        "autoIncrement": false
    },
    {
        "visible": true,
        "filterable": true,
        "key": "dateColumn",
        "label": "Date",
        "dataType": "date",
        "format": "dddd, MMMM Do YYYY, h:mm:ss a",
        "required": true,
        "autoIncrement": false
    },
]

Generate an api endpoint:

const router = require('express').Router()
router.use('/theModel',theModel.generateApi())

The .generateApi() method returns an express router with:

  • [GET] '/theModel' responds with a paginated list of all rows
  • [GET] '/theModel/:id' responds with a single row with the matching id (primaryKey)
  • [GET] '/theModel/meta' responds with some meta-data of the model like th field list, number of rows, visible and filterable fields also according to the request query (more on that here)
  • [POST] '/theModel' creates an instance, saves it and returns it (response is false if creation fails)
  • [PUT] '/theModel/:id' edits the instance and returns the new value
  • [DELETE] '/theModel/:id' deletes the instance and responds with true or false accordingly

All enpoints responses and expected data is aliased with the corresponding configuration. Supported query parameters for [GET]:

  • Fields : ?fields=field1,field2,field3 specifies wich fields the instances are be sent with
  • Order : ?order_by=field1(asc),field2(desc),field3 specifies how data should be ordered before pagination, with priority given by the order sent (field1 > field2 > field3). direction defaults to asc
  • Pagination : ?limit=30&offset=2 where limit defines page size, and offset defines number of page, the given example would return rows 31 to 60. limit defaults to 25 and offset defaults to 1
  • Filtering :
    • simple filter : ?foo=bar&exclusive=true will return all rows where foo column conains bar. Useful for filtering data tables or autocomplete inputs
      [{ "foo":"bar" }, { "foo":"barbacue" }, { "foo":"bombardier" }] 
      it also applies to numeric fields ?number=12
      [{ "number":12 }, { "number":33123 }, { "number":12999 }] 
    • exclusive filter : ?foo=bar will return all rows where foo column equals to bar, all values are converted to their respective type with their respective formatting declared in the model configuration
    • power filtering : Allows for complex querying ?field1(and,contains)=value1,field2(and,>)=value2,field3(or,<>)=value3 where the expected format is fieldName(condition,operator)=value, if the power format is matched at least once the api will expect it on each of the fields. numeric fields are supported by all operators.
      • conditions :
        • and
        • or
      • operators :
        • eq : equal to
        • < : lower than
        • > : greater than
        • <eq : lower than or equal to
        • >eq : greater than or equal to
        • <> : not equal to
        • contains : contains
        • !contains : not contains
        • startsWith : starts with
        • !startsWith : not starts with
        • endWith : ends with
        • !endWith : not ends with

Adding extra middlewares : Middlewares can be added to all different generated endpoints either before or after them, if the response should be sent, just add next:true to the options and the reponse will be carried to the next middleware as req.body._carried_ this can be either an instance, a list of instances, an error, or undefined depending on the endpoint and if was successful or failed.

const router = require('express').Router()

// we will add this middleware before the generated get endpoint
let justReds = (req, res, next) => {
    req.query.color = 'red'
    req.query.exclusive = false
    // do whatever you want here
    next()
}

router.use('/theModel',theModel.generateApi({
    middlewares:{
        get:[{place:'before', handler: justReds}]
    }
}))

Example using carried data:

const router = require('express').Router()

const afterMiddleware = (req, res, next) => {
    let createdInstanceOrError = req.body._carried_
    // handle you output here
}

router.use('/theModel',theModel.generateApi({
    next: true,
    middlewares:{
        post:[{place:'after', handler: afterMiddleware}]
    }
}))

Other options :

config.model - Model to be used (Model | Function | string) config.connection - Connection to be used when model is string (Connection | string)

Examples of dynamic model:

const { Model, Catalog } = require('sequelize-modeler')
const modelList = require('path/to/list/of/model/classes') // list of not initialized models

const connection = Catalog.getConnection('myId') // active connection

for(let model of modelList){
    // models will be initialized in their respective generated routers
    router.use(`${model.name}`,Model.generateApi({connection, model}))// an api fo every model 🚀 
}

Using the model name:

const { Model, Catalog } = require('sequelize-modeler')

let model = 'SomeModelName'
let connection = Catalog.getConnection('myId') // active connection

router.use(`${model}`,Model.generateApi({connection, model}))

Using a Function to infer the model :

const { Model, Catalog } = require('sequelize-modeler')

const model = (req,res,next) => { // req, res, next passed from express
    let model
    // do stuff...
    // populate model with some initialized model
    return model
}

// note that connection is no longer required, since you return an initialized model

router.use(`${model}`,Model.generateApi({model}))

config.failed - callback to be excecuted when creation fails

const router = require('express').Router()

const failed = (req,res,next) => {
    // instance can be undefined
    // this will be excecuted when something expected fails, like an insert, or edition
}

router.use('/theModel',theModel.generateApi({failed}))

config.failed - callbback to be excecuted in case of an internal error, by deafult res.status(500).send('Internal Server Error')

const router = require('express').Router()

const error = (req,res,next,error) => {
    // this will be excecuted when something unexpected like sequelize throwing an error
}

router.use('/theModel',theModel.generateApi({error}))

config.empty - Map of values to assign to null fields of each different dataType, or for every dataType

// empty map
{
    integer: number
    decimal: number, 
    bigint: number, 
    float: number, 
    real: number, 
    double: number,
    string: string,
    text: string,
    citext: string,
    date: Moment,
    dateonly: Moment,
    boolean: boolean,
    enum: [any],
    array: [any],
    json: Object,
    jsonb: Object,
    blob: BlobPart,
    uuid: string,
    cidr: any,
    inet: any,
    macaddr: any,
    range: [any],
    geometry: any,
    all: any,
}

config.apiname - Name used to build href of models, all instances have a href: http://example.com/apiname/:id, you get to change the apiname part of it

config.next - Toggles the excecution of next(), defaults to false

config.middlewares - Extra optional middlwares to be added {[{place:'before'|'after', handler:Middleware}]} place indicates if middlware must be excecuted before or after the generated one, see example above

config.get - Toggles the [GET] method, defaults to true

config.getOne - Toggles the [GET] /:id endpoint, defaults to true

config.post - Toggles the [POST] method, defaults to true

config.put - Toggles the [PUT] method, defaults to true

config.delet - Toggles the [DELETE] method, defaults to true

config.meta - Toggles the /meta endpoint, defaults to true

config.fields - List of fields to be used by the model, all instances and meta-data will be mapped to the given field list

const router = require('express').Router()

// you don't need to put every atribute of each field, just what you want
let fields = [
    {
        "key": "Column1",
        "as": "one",
        "dataType": "decimal",
    },
    {
        "key": "Column2",
        "as": "two",
        "dataType": "date",
        "format": "dddd, MMMM Do YYYY, h:mm:ss a",
    },
    {
        "key": "Column56",
        "as": "thisCoulmnDoesNotExist",
        "dataType": "string",
    }

]

router.use('/theModel',theModel.generateApi({fields}))

Output instances would be like this:

{
    one: 15,
    two: "Sunday, February 14th 2010, 3:25:50 pm",
    thisCoulmnDoesNotExist: null,
    href: "http://example.com/theModel/15"
}