vue-pony

Introduction

vue-pony is an intuitive and elegant library for interfacing with your REST API. It uses an object-oriented syntax inspired by PonyORM, an awesome Python ORM library.

As the name suggests, vue-pony was born as a Vue plugin. However, it can easily be used as a standalone library.

Index

  1. Introduction
  2. Reading data
  3. Writing data
  4. Reference

Installation

vue-pony can be installed with NPM:

$ npm i -S @sneppy/vue-pony

To install the older version use this command instead:

$ npm i -S @sneppy/vue-pony@1

API configuration

In order for vue-pony to work you’ll need to configure the API in the following way:

Throughout this guide we’ll assume that a simple API exists, which consists of a set of users and a set of posts. A JSON representation of each resource is given below:

user:

{
	"id": 1,
	"username": "charlie",
	"email": "charlie.brown@sneppy.com",
	"name": "Charlie Brown"
}

post:

{
	"id": 1,
	"title": "Peanuts",
	"author": 1,
	"created": "1950-10-05T00:00:00+00:00",
	"content": "..."
}

Note how post.author uses the user.id value to refer to the actual user resource.

Moreover, the following endpoints are defined:

endpoint description
GET /user/<alias> Returns user identified by <alias> (id, username or email)
POST /user {params} Creates a new user from {params} and returns it
GET /post/<postid> Returns post identified by <postid>
POST /post {params} Creates a new post from {params} and returns it
GET /user/<userid>/post Returns a list of indices of posts authored by user with id <userid>

Client setup

Somewhere in your project (it may be a good idea to create an api.js file in the src directory), import and create a new instance of Pony:

import Pony from '@sneppy/pony'

export let api = new Pony({
	base: 'http://localhost/api'
})

The Pony constructor accepts an object parameter with the following properties:

Creating models

Each resource is associated to a client model, which is a class that extends api.Model. A model defines the prototype of a resource, its relationships with other entities and the resource endpoint configuration.

export class User extends api.Model
{

}

export class Post extends api.Model
{

}

By default, an empty model maps a resource whose name is equal to the lowercase class name (e.g. User -> user and Post -> post). If the name of the resource differs from the lowercase class name, you can override Model.index to change it:

class User extends api.Model
{
	static get index()
	{
		return 'person'
	}
}

This index is used by Model.uri(alias, path = '') to generate the URI for a given entity. By default, this method appends the entity key after the index:

console.log(Post.uri([1])) // -> '/post/1'
console.log(Post.uri(['post-slug-ef3']), '/commits') // -> '/post/post-slug-ef3/commits'

The optional path = '' argument is a string that is appended at the end of the URI.

If necessary, you can also override Model.uri. For example, assume that each post has a set of commits:

export class PostCommit extends api.Model

A commit is uniquely identified by a composite key <post, hash>, where post is the id of the post it belongs to, and hash is the commit hash. Intead of having commit/<post>/<hash> (which is the default Method.uri output), you may prefer to use the URI /post/<post>/commit/<hash>, which is far more readable and highlights the fact that a commit belongs to that post:

static uri([ postid, commithash ], path = '')
{
	return `/post/${postid}/commit/${commithash}`
}

Array destructuring was used above to destructure the alias parameter.

The entity index is also used by Model.key(alias) to generate a unique key for each entity, with the form <index>:<key>. The key generated by Model.key has no particular usage inside vue-pony (you may use it, for instance, to key the items in a v-for loop).

console.log(PostCommit.key(3, 'e55f6c')) // -> 'postcommit:3,e55f6c'

Entity relationships

vue-pony supports simple entity-to-entity relationships out of the box.

One-way relationships are defined using static properties (or getters if necessary) inside the model class. For instance, each post is authored (belongs to) a single user. We describe this relationship like this:

export class Post extends api.Model
{
	// User that authored this post
	static author = User
}

The name of the static property determines both the name that we’ll be used to access the relationship, and the name of the property of the entity data that has the user index.

On the other side, each user has a set of posts. This type of one-way relationship is defined with api.Set(Type), where Type must be a model:

export class User extends api.Model
{
	// Set of posts authored by this user
	static posts = api.Set(Post)
}

If you try to define both relationships (two-way relationship), you will incur in a circular dependency problem: depending on which model is defined first, one of model will evaluate to undefined (e.g. if you first define User, then Post will be undefined in api.Set(Post); viceversa, if you first define Post, User will be undefined).

To solve this problem, you must use a getter for the first relationship:

export class User extends api.Model
{
	// Set of posts authored by this user
	static get posts()
	{
		// Evaluated only when `posts` is accessed
		return api.Set(Post)
	}
}

export class Post extends api.Model
{
	// User that authored this post
	static author = User
}

Reading data >