Giter Club home page Giter Club logo

jsonapify's Introduction

jsonapify

NPM

Build Status Dependencies Coverage Status Gratipay Tips

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

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 (the ones missing the trailing selector) map to a subset of resource objects.
  • 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!

jsonapify's People

Contributors

alex94cp avatar agraboso avatar

Watchers

James Cloos avatar Sebastian Schnock avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.