Git Product home page Git Product logo

ember-jsonapi-resources's Introduction

Ember JSON API Resources

An Ember CLI Addon… a lightweight solution for data persistence in an Ember.js application following the JSON API 1.0 specification (your anti-bikeshedding weapon for API development).

URLs are first class in the JSON API 1.0 specification and first class in the Ember.js Router. Why not make them first class in your persistence solution too? Now you can with the ember-jsonapi-resources addon.

This addon was inspired by finding a simple path to shipping an application implementing the JSON API spec by using the JSONAPI::Resources gem to production.

Whether you adopt the JSON API 1.0 spec or not, this addon is a template for creating a data persistence solution for your Ember.js application that models the domain of your API server. The addon code is rather concise; borrow at will.

Build Status Ember Observer Score Code Climate Test Coverage

Status

This addon is under active development.

This is a NEW project there may be bugs depending on your use of the addon. Please do file an issue if you run into a bug.

Requirements

This data persistence solution for an Ember.js application is a simple approach; however, there are a few things you'll need to understand in order to get up and running.

  • Familiarity with Ember.js, Ember CLI and JSON API
  • You must use Ember CLI to use ember-jsonapi-resources, it's an addon
    • You'll need to uninstall Ember Data from the generated application and configure your app, see the 'usage' section below.
  • Fetch API instead of XMLHttpRequest, see Introduction to fetch()
    • A polyfill is included in this addon

Other Ember.js JSON API Implementations

Why a Stand-Alone Solution?

This is a simple solution for an Ember app that utilizes resources following the JSON API 1.0 specification. It operates on the idea that the objects in the app (resource/model, adapter, serializer, services) simply follow the spec.

URL's are first class in the JSON API 1.0 specification and first class in the Ember.js Router. Why not make them first class in your persistence solution too?

The JSON API specification defines relationships including links objects that providing URLs for related and self, making your API server discoverable.

This implementation takes the posture that your application's resources do not need a complex abstraction but a simple implemenation of a solid specification. So, this project is great for getting started with using the JSON API spec in your Ember.js app.

Also, managing a distributed cache requires flexibility. When a client application receives a representation of a resource from a server, the client should be able to expire that cached object (in the browser application) based on the response headers; or by whatever means the developer chooses.

Questions

Is this a replacement for Ember Data or an interface to it?

Neither. This is a completely separate solution. Ember Data needs foreign keys and provides an abstraction for adaters and serializers. It's solution helps you work with various JSON document formats. Ember JSON API Resources needs URLs and fits a specific specification for an API server without the need for an abstraction.

Does this implement all of the JSON API specification?

Not yet. The happy path for reading, creating, updating/patching, deleting is ready, as well as patching relationships. No extension support has been worked on, e.g. JSON Patch. I would like to do that one day.

Is this lightweight? Relative to what?

Yes. With a server that follows the JSON API specification - it just works. This is a simple solution compared with starting from scratch using AJAX, or adapting Ember Data to work with the URLs. This solution provides a basic, timed caching solution to minimize requests, and leaves a more advanced caching strategy to the developer via a mixin. It does provide a store object that caches deserialized resources.

Are included resources supported (side-loading)?

Yes. When using ?include=relation in a request for a resource, the (related) included resources will be deserialized and cached using the cacheDuration value set on the resource prototype (model).

What does the store actually do?

Caching, and it behaves as expected in the default model hook of a route. The store service is a facade for the services for each resource. Calling this.store.find('entity') in a route's model hook will lookup the service for that entity and call that service's find method. The service is a combination of an adapter, cache mixin, and associated serializer for the same entity. The service is an extension of that adapter with a mixin for the caching strategy for that entity. The default service-cache mixin provides a basic caching plan using a time value for exiration, which is a property of the resource (defaults to 7 minutes).

Example Application

The tests/dummy/app included in this repo is a demo of using ember-jsonapi-resources.

See the commit history on this repo, jr-test, a manual test of using this library in a new Ember CLI app generated with ember new jr-test

Usage

Below are notes on using the ember-jsonapi-resources addon…

Installation

You will need to remove another dependency that injects a store service.

To consume this addon in an Ember CLI application:

ember install ember-jsonapi-resources

Remove dependency for Ember Data in your ember-cli app:

npm rm ember-data --save-dev
bower uninstall ember-data --save

Remove ember-data from both bower.json and package.json then:

bower install
npm install

Resource Generator

Generate a resource (model with associated adapter, serializer and service):

ember generate resource entityName

Use the singular form of the name for your resource (entityName).

The blueprint for a resource re-defines the Ember CLI resource generator. So you'll need to generate an associated route like so:

ember generate route entityName

For generated code exampels, see the tests/dummy/app in this repo.

Store Service

A store service is injected into the routes. This is similar to how Ember Data uses a store, but the resource is referenced in the plural form (like your API endpoint).

This is the interface for the store which is a facade for the service for a specific resource. Basically you call the store methods and pass in the resource name, e.g. 'posts' which interacts with the service for your resource.

An example route:

import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    return this.store.find('post');
  }
});

The store object will pluralize the type to lookup the resources' service objects.

Resource Services

Each resource has an associated service that is used by the store.

A resource service is a combination of an adapter, serializer and cache object. The service is also injected into the resource (model) objects.

The caching plan for a service is simply a mixin that can be easily customized. To begin with, the resource (model) prototype and the service-cache mixin work together to provide a basic plan.

The services are "evented" to facilitate close to real time updates.

An attr of the resource is a computed property to the actual attribute in an attributes hash on the resource (model) instance.

When an attr is set and the value has changed an attributeChanged event is triggered on the resource's service object. By default, the adapter listens to this event and handles it with a call to updateResource.

The resource adapter's updateResource method sends a PATCH request with only the data for the changed attributes.

You might want to buffer changes on the resource that is passed into a component using ember-state-services and ember-buffered-proxy, or you could just re-define the resource's adapter prototype so that initEvents returns a noop instead of listening for the attributeChanged event.

Resource (Model)

Here is the blueprint for a resource (model) prototype:

import Ember from 'ember';
import Resource from 'ember-jsonapi-resources/models/resource';
import { attr, hasOne, hasMany } from 'ember-jsonapi-resources/models/resource';

export default Resource.extend({
  type: '<%= entity %>',
  service: Ember.inject.service('<%= resource %>'),

  /*
  title: attr(),
  date: attr(),

  author: hasOne('author'),
  comments: hasMany('comments')
  */
});

The commented out code is an example of how to setup the relationships.

The relationships are async using promise proxy objects. So when a template accesses the resource's relationship a request is made for the relation.

Configuration

You may need configure some paths for calling your API server.

Example config settings: tests/dummy/config/environment.js

var ENV = {
// …
  EmberENV: {
    MODEL_FACTORY_INJECTIONS: true
  },
  APP: {
    API_HOST: '/',
    API_HOST_PROXY: 'http://api.pixelhandler.com/',
    API_PATH: 'api/v1',
  },
  contentSecurityPolicy: {
    'connect-src': "'self' api.pixelhandler.com",
  }
// …
};

Also, once you've generated a resource you can assign the URL.

See this example: tests/dummy/app/adapters/post.js

import ApplicationAdapter from 'ember-jsonapi-resources/adapters/application';
import config from '../config/environment';

export default ApplicationAdapter.extend({
  type: 'post',

  url: config.APP.API_PATH + '/posts',

  fetchUrl: function(url) {
    const proxy = config.APP.API_HOST_PROXY;
    const host = config.APP.API_HOST;
    if (proxy && host) {
      url = url.replace(proxy, host);
    }
    return url;
  }
});

The example above also includes a customized method for the url. In the case where your API server is running on it's own domain and you use a proxy with your nginx server to access the API server on your same domain at /api then the JSON documents may have it's own link references to the original server so you can replace the URL as needed to act as if the API server is running on your same domain.

Example JSON API 1.0 Document

{
  "data": [{
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON API paints my bikeshed!"
    },
    "links": {
      "self": "http://example.com/articles/1"
    },
    "relationships": {
      "author": {
        "links": {
          "self": "http://example.com/articles/1/relationships/author",
          "related": "http://example.com/articles/1/author"
        },
        "data": { "type": "people", "id": "9" }
      },
      "comments": {
        "links": {
          "self": "http://example.com/articles/1/relationships/comments",
          "related": "http://example.com/articles/1/comments"
        },
        "data": [
          { "type": "comments", "id": "5" },
          { "type": "comments", "id": "12" }
        ]
      }
    }
  }]
}

For more examples see my API's resources:

The api.pixelhandler.com server is running the JSONAPI::Resources gem. It follows the JSON API 1.0 spec.

Contributing / Development

Clone the repo, install the dependencies:

  • git clone this repository
  • npm install
  • bower install

Running

To run the app in /tests/dummy use a proxy url for a live API

Running Tests

  • ember test
  • ember test --server

A good way to get to know more about how this addon works is to review the tests, see source code for the unit tests: tests/unit.

Building

  • ember build

For more information on using ember-cli, visit http://www.ember-cli.com/

Documentation

Online documentation, build from source: generated docs

Docs are generated from source using yuidoc.

To view the docs during development:

  • yuidoc ./addon/* -c yuidoc.json --server 3333 (you can append a port number e.g. --server 8888, the default port is 3000)

To generate docs for the gh-pages branch:

  • yuidoc ./addon/* -c yuidoc.json

ember-jsonapi-resources's People

Contributors

pixelhandler avatar barelyknown avatar ember-tomster avatar

Watchers

James Cloos avatar BTX avatar  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.