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

falcor-saddle

v0.1.2

Published

Experimental model route generation for Netflix Falcor – giddyup!

Downloads

14

Readme

falcor-saddle

Experimental model route generation for Netflix Falcorgiddyup!

Overview

Netflix Falcor is a library for efficiently querying data across a network. It presents some useful solutions for querying data from multiple datasources, caching, and batching queries for greater performance. Yet, getting Falcor wired up to a back-end model can take a lot of repetitive code.

falcor-saddle is a module that will generate CRUD-like routes for you if you give it a set of methods for accessing your model.

Instead of writing this:

import Todo from 'model-todo';

const routes = {
  {
    route: "todos.length",
    get: (pathSet) => {
      /* repetitive implementation */
    }
  },
  {
    route: "todos[ranges]",
    get: (pathSet) => {
      /* more repetitive implementation */
    }
  },
  {
    route: "todosById[keys]",
    get: (pathSet) => {
      /* yet more repetitive implementation */
    }
  },
  /* plus routes to set, create, and delete todos... */
}

You can write this instead:

import Todo from 'model-todo';
import { createRoutes } from 'falcor-saddle';

const routes = createRoutes({
  routeBasename: 'todos',
  acceptedKeys: ['id', 'createdAt', 'content'],
  getLength: async () => Todo.count().execute(),
  getRange: async (from, to) =>
    Todo.orderBy('key').slice(from, to + 1).run(),
  getById: async (id) => Todo.get(id).run(),
  update: async (oldObj, newObj) => oldObj.merge(newObj).save(),
  create: async (params) => (new Todo(params)).save(),
  delete: async (id) => Todo.get(id).delete()
}),

...and falcor-saddle will create the following routes:

  • todos.length: get()
  • todos[ranges]: get()
  • todosById[keys]: get(), set()
  • todos.create: call()
  • todosById[keys].delete: call()

Usage

The usual means of using falcor-saddle is through its createRoutes() method and pass these routes to falcor-router in a manner similar to this:

import FalcorRouter from 'falcor-router';
const routes = createRoutes({ /* options */ });
const MyRouter = FalcorRouter.createClass(routes);

...and then wire that router up to Express:

/* ... other express imports */
import bodyParser from 'body-parser';
import falcorExpress from 'falcor-express';

const app = express();

app.use('/model.json', bodyParser.urlencoded({extended: false}),
  falcorExpress.dataSourceRoute( (req, res, next) =>
    new MyRouter(req, res, next)
));

/* ... additional app configuration, call to app.listen() ... */

createRoutes

Syntax

createRoutes(options)

Params

options is an object with the following key parameters:

  • routeBasename: String, given a value like foo it will create routes like foo.length, foo[range], fooById[keys], foo.create and foo.delete

  • acceptedKeys: Array of Strings, the keys you'd like your model to expose. A value of ['id', 'date', 'content'] will provide access to the id, date, and content keys from a model object. Make sure your model actually returns those keys ;)

  • getLength: Function or Promise of the form () => length. Must return the number of objects provided by your model.

  • getRange: Function or Promise of the form (from, to) => [modelObject, ...] where from and to are inclusive (i.e. (from=0, to=0) would return an Array containing the model object at interval 0).

  • getById: Function or Promise of the form (id) => modelObject where id is the key used to retrieve a single item from your model collection.

  • update: Function or Promise of the form (oldObj, newParams) => newObj where oldObj will be given as a model instance retrieved by your getById function and newParams will be a plain Object containing the key values to update. As a side-effect, you must save newObj to your model store.

  • create: Function or Promise of the form (newParams) => newObj where newParams will be a plain Object containing the key values used to create the new model instance. As a side-effect, you must save newObj to your model store.

  • delete: Function or Promise of the form (id) => null where id is the key used to retrieve a single item from your model collection. As a side-effect, you must delete the model instance with this id from your model collection.

  • modelIdKey: (optional) String. The primary key used by your model collection. Default: 'id'.

  • modelKeyGetter: (optional) Function of the form (model, key) => model[key]. It's the method used by falcor-saddle to retrieve keys from your model objects outside of the methods you specify. Rarely used.

  • modelIdGetter: (optional) Function of the form (model) => model['id']. It's the method used by falcor-saddle to retrieve the primary key value from your model collection. Default: (model) => modelKeyGetter(model, modelIdKey). Rarely used.

Client Usage

Given a NodeJS server providing routes created by falcor-saddle at http://localhost/api/model.json, how do you use this thing? Some examples are given below.

Creating the Client-side Model (HTTP):

const model = new falcor.Model({source: new HttpDataSource('/api/model.json') });

Length

model.
  getValue('todos.length')
  .then((response) => console.log(`todos.length: ${response}`));

Retrieve Range

model
  .get('todos[0..1]["id", "content"]')
  .then((response) => {
    console.log('todos[0..1]: ' + JSON.stringify(response));
  });

Set (Update) First Item

const aTodoResponse = await model.get('todos[0]["id", "content"]');
const updateTodoReq = { json: { todosById: { } } };
updateTodoReq.json.todosById[aTodoResponse.json.todos['0'].id] = {
  content: 'this is some updated content!'
};
model.
  .set(updateTodoReq)
  .then( (response) => {
    console.log('todos[0]:' + JSON.stringify(response));
  });

Get By Id

// bd8d468d-a330-4a13-b916-9ff46be54f3e is an example primary key value:
const key = 'bd8d468d-a330-4a13-b916-9ff46be54f3e';
model.
  .get(['todosById', [key]])
  .then( (response) => {
    console.log('todosById[key]:' + JSON.stringify(response));
  });

Create

model.
  call('todos.create', [
    // Values for first item to create:
    { content: 'snow day today' }
  ],
    // Fields to retrieve from created items:
    ['id', 'content', 'createdAt']
  )
  .then( (response) => {
        console.log('todos.create: ' + JSON.stringify(response));
  });

Delete

const firstItemId = await model.getValue(['meetings', 0, 'id']);
model
  .call('todosById.delete', [firstItemId])
  .then((response) => {
    console.log('todosById.delete: ', response);
  });

Caveats

Maturity

Falcor itself is currently in preview release. This module, and the capability it provides should be considered experimental.

We wrote it for developing our own applications, but heck, we're not sure if if falcor-saddle is even a good idea. We do know that it's helped us sketch out applications – so, at a minimum, consider it useful for that purpose.

Ranges

The way ranges are handled (e.g. todo[0..n]) is presently rather lazy. When a new item is created with todo.create it is added to the end of the range (i.e. todo[n+1]). When an item is deleted with todo.delete rather than rebuilding, the entire range we merely invalidate the item from the list.

This may change in the future.

Serialization

Although Falcor provides its own serialization, there are some corner cases where it fails: for example, we've observed returning a Date object causes an exception to be thrown. To avoid this, we've created a simple serializer.

Model serialization is implemented in src/serialization.js. Your model's return values are run through this serializer. At present, anything that's not a:

  • Boolean or
  • Number or
  • String or
  • null or undefined

...will be serialized using JSON.stringify().

Releases

Semantic Versioning

falcor-saddle uses Semantic Versioning.

Change Log

CHANGELOG

About

Team

falcor-saddle is an experiment from Parabol. A young company building a human operating-system for teams and organizations.

With other contributions by:

Contributing

We'd love to see this project grow.

Provide Issues, fork to your heart's content, and submit pull requests.

License

MIT