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

friendly

v0.0.20

Published

A no-frills ORM that wraps your data and helps you compose objects. Define relationships between models, expand and collapse their properties, build streams and link data from multiple sources.

Downloads

15

Readme

Friendly ORM

npm version

A no-frills ORM that wraps your data and helps you compose objects. Define relationships between models, expand and collapse their properties, build streams and link data from multiple sources.

Installation

You know the drill.

$ npm install friendly

Use Case

  • You have documents with one-to-many or one-to-one relationships coming from different databases or sources and would like to quickly compose complete objects.
  • You'd like to keep your code clean and have seperation of concerns between your services.
  • You don't have the ability to do database joins or link documents.
  • You want to avoid using bloated database specific ORM's.
  • You want to add the ability to expand and collapse resources on your API or services.

Overview

For full documentation see the API DCOS.

Two primary methods, EXPAND and COLLAPSE. Both methods work on a single parent object or an array of parent objects with nested children. A child property can be a foreign key, an object with a foreign key, or an array of either.

  • Expand => Like the mongoose 'populate' method. Will resolve nested children to their sources and map in a full or partial object.
  • Collapse => Reduces a parent objects children into a minimal representation. Default is just the foreign key, but you can configure it to add any other properties you'd like.

What does it look like?

It looks as easy as it sounds. Convert something like this.

Book: {
  id: 120,
  name: 'Code Complete 2',
  author: 19237
}

Into this...

Book: {
  id: 120,
  name: 'Code Complete 2',
  author: {
    id: 19237,
    name: 'Steve McConnel'
  }
}

Overview

First configure your models for later use. Configuration takes a name, provider, children, and optional collapsables.

  • name => The name of your model. This is also the property name of the object when nested as a child object in a parent.
  • key => (optional) the name of a property in this object that is unique, such as 'id'. This will be passed as the argument to your provider during expansion. If no key is configured, then the full object is passed to the provider.
  • children (optional) => The model names of any children that might be nested in this model. If the childs model name is different than the property name it might be appear as, such as the model 'book' appearing as 'books', then configure 'books' as an alias in the 'book' model.
  • provider => Is a promise returning function which is passed the key (or full object if no key is configured). The provider is called when this model appears as a child in another object and must be expanded.
  • collapsables (optional) => An array of property names to include from this object when this object is included as a child and the parent object calls the 'collapse' method. By default the property specified by 'key' is always included, and all other properties are removed.

So given a Book model that has an id property with a nested author child, our configure method call would look like this:

var friendly = require('friendly');

friendly.createModel({
  name: 'book',
  key: 'id',
  collapsables: 'title', // or ['title', 'publishedDate']
  children: 'author', // or ['author', 'category']
  provider: function(id){
    // return a promise that finds the book
    return findBooksByIdPromise(id);
  }
});

Putting it all together and expanding/collapsing our book with authors would look like this:

var friendly = require('friendly');

friendly.createModel({
  name: 'book',
  key: 'id',
  children: 'author',
  provider: function(id){
    // return a promise that finds the book
    return findAuthorsByIdPromise(id);
  }
});

friendly.createModel({
  name: 'author',
  key: 'id',
  provider: function(id){
    // return a promise that finds the author
    return findBooksByIdPromise(id);
  }
});

var exampleAuthor = {
  id: '19237',
  name: 'Steve McConnel'
};

var exampleBook = {
  id: '203',
  name: 'Code Complete 2',
  author: '19237'
};


friendly.expand('book', exampleBook).then(function(expandedBook){
  console.log(expandedBook);
  /** prints
    {
      id: '203',
      name: 'Code Complete 2',
      author: {
        id: '19237',
        name: 'Steve McConnel'
      }
    }
  */
  var collapsedBook = friendly.expand('book', expandedBook);
  console.log(collapsedBook);
  /** prints
    {
      id: '203',
      name: 'Code Complete 2',
      author: '19237'
    }
  */
});

How many ways can I represent a child object?

4 ways. Pay attention to how the author object changes in these examples. These are assuming you've created both a book and author model, and the author models key is set to 'id'.


// #1 - author as a primitive value
var book = {
  id: '203',
  name: 'Code Complete 2',
  author: '19237'
};
// #2 - author as an object
var book = {
  id: '203',
  name: 'Code Complete 2',
  author: {
    id: '19237'
  }
};
// #3 - multiple authors as an array of primitive values
var book = {
  id: '203',
  name: 'Code Complete 2',
  author: ['19237', '2462']
};
// #4 - multiple authors as an object array of objects
  id: '203',
  name: 'Code Complete 2',
  author: [
    { id: '19237' },
    { id: '2462' },
  ]
};

What about models where I don't want to set a key?

  • When calling expand on a parent object, if it's children don't have keys set for their model, then the entire object will be passed to the provider.
  • When calling collapse on a parent object, if it's children models don't have keys AND collapsables set, then the child will be skipped and left untouched.

What about deep nested children?

Expand/Collapse accepts an optional 'path' as it's third argument. Dot notation can also be used. This is essentially a utility function.

var object = {
  inner: {
    book: {
      name: 'Code Complete 2',
      author: '19237'
    }
  }
};

friendly.expand('book', object, 'inner.book').then(function(expandedBook){
  console.log(expandedBook);
  /** prints
    {
      inner: {
        book: {
          name: 'Code Complete 2',
          author: {
            id: '19237',
            name: 'Steve McConnel'
          }
        }
      }
    }
  */
});

Gotcha's

  • During calls to expand, if a childs provider fails to resolve then the child will be skipped.
  • There is no deep recursion to child objects. That is, if the child object also contains it's own children then will not be resolved. To accomplish this, you can manually add an expand call to your child's provider.
  • Friendly is currently global and createModel can only be called once per model name per application instance.