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

@mazeltov/model

v1.0.6

Published

Mazeltov model library

Downloads

1

Readme

Requirements

This tool is designed to work with postgres (even though knexjs is used). It may work just fine with MySQL but

Defining a Model (Basic Example)

You have to make sure a table exists in your Postgres DB with these columns ahead of time.

const {
  modelFromContext,
  // these are action producers
  creator,
  getter,
  lister,
  updator,
  remover,
  iser,
} = require('./modelLib');

const iface = modelFromContext({
  // DB must be knexjs using postgres. Also should handle snake to camel case conversion
  // See Mazeltov app for example
  db,
  // it is recommended instead to pass @mazeltov/logger from higher context
  logger: global.console,
  entityName: 'fooOwner',
  // default of 'id' if not specified. Can be array for composite keys
  key: 'personId',
  selectColumns: [
    'personId',
    'favoriteFoo',
    'fooName',
    'createdAt',
    'updatedAt',
  ],
  createColumns: [
    'personId',
    'fooName',
    'favoriteFoo',
  ],
  updateColumns: [
    'favoriteFoo',
  ],
}, [
  creator,
  getter,
  lister,
  updator,
  remover,
  iser,
]);

(async () => {

  const newFoo = await iface.create({
    personId: 12,
    fooName: 'Giuseppe',
    favoriteFoo: 'Test',
  });

  console.log(newFoo);

})();

What is a model?

Lets start with MVC.

In the MVC paradigm, you have three components, a Model, a View, and a Controller

  • A controller accepts input from a previous view
  • The controller maps the input from the view into what the model needs
  • The model uses the data to "model" business logic and write to DB
  • A new view is produced (could be html, json, shell output)
  • The cycle repeats when the view passes data back to the controller (user submits html form or types shell input)

In this paradigm, the model should be the same regardless of whether

  • input comes from a CLI interface
  • input comes from an HTTP request
  • input comes from a message broker (like RabbitMQ)

I Need To Do X

There are always going to be special cases where much much more is needed and that is okay! There are plenty of ways to override each generated model method and insert hooks to change the arguments and result from the method call.

Transactions

This is the recommended pattern for transaction handling (when using multiple actions across models.


// someOtherModel would be passed from ctx.models
const someOtherModel = /*...*/

const fooModel = modelFromContext(/*...*/);

const createFoo = async ( args = {}, passedTrx = null) => {

  const trx = passedTrx === null ? await db.transaction() : passedTrx;

  try {
    await fooModel.create(args, trx);
    await someOtherModel.create(args, trx);
    trx.commit();
  } catch (e) {
    trx.rollback();
  }

};

Decorator Pattern (Recommended Approach)

If you need to extend the default behavior of the actions (create, get, update, remove, list), you can decorate default model

const {
  modelFromContext,
  creator,
  getter,
  lister,
  updator,
  remover,
  iser,
} = require('./modelLib');

module.exports = (ctx) => {

  const iface modelFromContext({
    ...ctx,
    entityName: 'fooOwner',
    key: 'personId',
    selectColumns: [
      'personId',
      'favoriteFoo',
      'fooName',
      'createdAt',
      'updatedAt',
    ],
    createColumns: [
      'fooName',
      'favoriteFoo',
    ],
    updateColumns: [
      'favoriteFoo',
    ],
  }, [
    creator,
    getter,
    lister,
    updator,
    remover,
    iser,
  ]);

  // You can wrap the default method here. This is what's meant by decorating. While ES6 class decorators
  // would be nice, the functional paradigm is generally used in code based.
  const createFooOwner = async ( args = {} ) => {

    // do something custom with the args
    args.favoriteFoo = args.favoriteFoo + ' is da best!';

    return iface.create(args);

  };

  // You must splat the default interface here
  return {
    ...iface,
    // It is advised to export a shorthand and fully qualified method name
    createFooOwner,
    create: createFooOwner,
  };

}

Action Hooks (An Alternative)

  • onWill{Action} : This will transform args before getting sent to database. For example, you can change which columns are inserted/updated for create/update. You can change what gets used in where clause of list. You can also use this just for general side-effects before the action is performed, but you MUST return the first argument of your callback.

  • on{Action}Result This modifies the result returned. This shouldn't be used for standard actions like list, get, create, update, remove, but could be used for a very custom action. (isMostSpecial)

Overwriting Context (ctx)

When calling modelFromContext, you can override the context JUST for a specific method:

module.exports = (ctx) => modelFromContext({
  ...ctx,
  entityName: 'fooOwner',
  key: 'personId',
  selectColumns: [
    'personId',
  ],
}, [
  creator,
  getter,
  lister,
  remover,
  iser,
  // here is where you pass an array with the function as key 0
  // and the context override as key 1
  [
    updator,
    {
      fnName: 'markFinished',
      defaultUpdateArgs: { isfinished: true }
    }
  ],
  [
    updator,
    {
      fnName: 'markUnfinished',
      defaultUpdateArgs: { isfinished: false }
    }
  ]
]);
``

Roadmap

Things that could always help

  • More warnings for misconfigurations
  • More integration and unit test coverage