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

jsonapify

v1.6.9

Published

A library for the development of JSON APIs

Downloads

20

Readme

jsonapify

NPM

Build Status Dependencies Coverage Status Gratipay Tips

jsonapify is a library to assist the development of JSON-API compatible APIs with NodeJS.

You can see jsonapify in action in this repo.

Why jsonapify?

  • Simple: jsonapify is designed around simplicity. Easy things are easy to do, hard things are possible. If you feel something can be made simpler, by all means file an issue!
  • Unintrusive: ExpressJS, Restify, Connect,... No matter, jsonapify integrates nicely.
  • Interoperable: By offering a common-interface across your APIs, jsonapify lets your users build great things on top of them. If you don't know yet about the JSON-API specification, you should read about it and all the oportunities it has to offer.
  • Well tested: jsonapify is designed from the start with unit testing in mind. Reliability is at the core of what we do.

Declaring resources

jsonapify detaches mongoose models from the actual representation of the resources. This allows for a lot of flexibility: as a matter of fact, declaring a non-readable field is this elegant:

var User = require('../models/User');

var userResource = new jsonapify.Resource(User, {
	type: 'users',
	id: new jsonapify.Property('_id'),
	attributes: {
		email: new jsonapify.Property('email'),
		password: {
			value: new jsonapify.Property('password'),
			readable: false,
		},
	},
});

jsonapify.Runtime.addResource('User', userResource);

ES6 in action

This is how the previous example would look in ES6:

import {Property, Resource, Runtime} from 'jsonapify';
import User from '../models';

const userResource = new Resource(User, {
	type: 'users',
	id: new Property('_id'),
	attributes: {
		email: new Property('email'),
		password: {
			value: new Property('password'),
			readable: false,
		},
	},
});

Runtime.addResource('User', userResource);

Navigating resources

HATEOAS is one of the most important principles of the REST phylosophy. jsonapify makes interconnecting your resources a piece of cake:

var User = require('../models/User');

var userResource = new jsonapify.Resource(User, {
	type: 'users',
	id: new jsonapify.Property('_id'),
	links: {
		self: {
			value: new jsonapify.Template('/users/${_id}'),
			writable: false,
		},
	},
});

jsonapify.Runtime.addResource('User', userResource);

Linking resources

As someone said, "nobody is an island". Resources are not islands either. Linking resources in jsonapify is as easy as you'd expect:

var User = require('../models/User');
var roleResource = require('./roles').resource;

var userResource = new jsonapify.Resource(User, {
	type: 'users',
	id: new jsonapify.Property('_id'),
	relationships: {
		role: new jsonapify.Ref('Role', 'role'),
	},
});

jsonapify.Runtime.addResource('User', userResource);

Note: related resources are not subresources. Subresources are resource-like objects so tightly linked to their parent resource that they can't exist on their own. jsonapify does not support access of related resources as subresources. This is by-design.

Exposing resources

We all know about DRY. But then, why do we keep writing the same endpoint boilerplate again and again? jsonapify offers all CRUD operations as connect-compatible middleware. That means plugging a new endpoint is as simple as it gets:

app.get('/users/', [
	jsonapify.enumerate('User'),
	jsonapify.errorHandler()
]);

Middleware and resource addressing

Everything in REST is a resource. Resources can have subresources, too. That means that you can apply a READ operation (GET verb in REST terms) to a subresource. Let's see how resource addressing works in jsonapify.

  • Resource chains come in the form of [(typename, [selector])+].
  • Resource chain selectors are applied at request-time, and are used to select a subset of objects of the preceeding resource type.
  • At this moment, selectors can get info from:
    • Request params: jsonapify.param(...)
    • Request query params: jsonapify.query(...)
    • Resource parent object: jsonapify.parent(...)
  • There are partial and full resource chains. A full resource chain maps to a single resource object, whereas a partial resource chain maps to a subset of resource objects. The same chain can be considered full or partial depending on the middleware (partial for enumerate, and full for create, read, update and the like).
  • Some jsonapify operations require full resource chains (ie: READ, UPDATE,...), while others require partial resource chains (only CREATE at this moment). Therefore, the same resource chain may be interpreted as a full or a partial one depending on the context.

For example, a READ operation with the following resource chain, directed to the URI '/groups/{group}/users/{user}', would retrieve a resource object of type 'User', with group == parent._id and name == user, where parent is the group the user logically belongs to:

/* full chain */ [
	'UserGroup', {
		name: jsonapify.param('group'),
	},
	'User', {
		group: jsonapify.parent('_id'),
		name: jsonapify.param('user'),
	},
]

On the other hand, a CREATE operation with the following resource chain, directed to the same URI, would store a resource object of type 'User', with group == parent._id and the rest of the properties from the request body:

/* partial chain */ [
	'UserGroup', {
		name: jsonapify.param('group'),
	},
	'User', {
		group: jsonapify.parent('_id'),
	},
]

Note: While jsonapify subresource addressing is already functional, it is not polished enough to be considered production-ready (think of error reporting, usability...) If you ever encounter a bug, please file an issue and it will get assigned a high priority.

Transaction filters

In addition to all of the above, jsonapify also offers transaction filters. These filters enable per-request functionality, such as pagination, sparse-fields, sorting... The most common transaction filters are enabled by default, so you don't have to worry.

Credits

This library wouldn't have been possible without all the great work by the people of the JSON-API specification. Thank you guys, you're awesome!