/vuex-orm-rest

vuex-orm plugin for RESTful backends

Primary LanguageJavaScript

Vuex ORM

Vuex ORM Plugin: vuex-orm-rest

Vuex-ORM brings Object-Relational Mapping to the Vuex Store. vuex-orm-rest lets you communicate with RESTful backends.

The plugin extends the basic model of Vuex-ORM with some helful functions to make CRUD operations such as (save, fetch, fetchAll, update and delete).

You no longer need to access your http client manually. All the comunication happens thru the enhanced Vuex-ORM models.

Dependencies

yarn add vuex
yarn add @vuex-orm/core
yarn add axios

Installation

yarn add vuex-orm-rest

The following exmaple installs the plugin using axios as the HTTP-Client and a vue-router instance.

import Vue from 'vue'
import Vuex from 'vuex'
import VuexORM from '@vuex-orm/core';
import VuexORMRest from 'vuex-orm-rest';
import axios from 'axios';

const client = axios.create({ baseURL: '/api' });
const database = new VuexORM.Database();

VuexORM.use(VuexORMRest, { client });
Vue.use(Vuex);
export default new Vuex.Store({
 plugins: [VuexORM.install(database)],
});

The plugin requires a HTTP-Client to make requests to the backend. The client is passed as an option to the plugin. The following tables shows the association between the client- and CRUD method.

Plugin Client
fetch get
fetchAll get
save post
update patch
replace put
destroy delete

Defining models

Go to https://vuex-orm.github.io/vuex-orm/guide/components/models.html to see how to define models using Vuex-ORM.

Interacting with the backend

Assume we have a user model. Additionally to the entity an apiPath variable has to be defined. The apiPath represents the URL under which the entity is reachable on the REST API.

import { Model } from '@vuex-orm/core';

class User extends Model {
  static entity = 'users';
  static apiPath = 'users';

  static fields () {
    return {
      id: this.attr(null),
      name: this.attr('')
    }
  }
}

Your vuex-orm instance need to know about your model.

database.register(User, {});

fetch(id = null)

Fills the store with a single item by id. Returns a promise with the fetched data.

User.fetch(1);

The fetched user now lies in the store and can be retrieved by using the Vuex-ORM actions.

const user = User.find(1);

fetchAll({ filter = {}, relations = [], replace = false })

Fills the store with a list of items. Returns a promise with the fetched data.

User.fetchAll();

Pass filter to append it as query string to the get request.

User.fetchAll({ filter: { active: true } });

Pass relations to fetch nested structure.

const user = User.find(1);
Comments.fetchAll({ relations: [user] });
// fetches using /user/1/comments

Retrieve the fetched users.

User.all();

save(keys = Object.keys(this.$toJson()))

Saves a user instance using post verb. Returns a promise with the post response.

const user = new User({ name: 'John Doe' });
user.save();

Pass keys as param to define which attributes should be send to the backend.

const user = new User({ name: 'John Doe' });
user.save(['name']);

update(keys = Object.keys(this.$toJson()))

Updates an existing user using patch verb. Returns a promise with the patch response. The update function also accepts a list of keys for every property that should be part of the patch payload.

// Retrieve the user from the store
const user = User.find(1);
user.name = 'Michelangelo';
// This only updates the name property
user.update(['name']);

replace

Replaces a whole user instance using put verb. Returns a promise with the put response.

// Retrieve the user from the store
const user = User.find(1);
user.name = 'Michelangelo';
// This only updates the name property
user.replace();

destroy

Destroys a user using the delete verb. Returns a promise with the delete response.

// Retrieve the user from the store
const user = User.find(1);
user.destroy();

routeURL(type = 'show', options = {})

Generate a route URL with prefix on a model instance.

const user = User.find(1);
user.routeURL('some'); // --> { name: 'some-user', params: { id: 1 } }

Passing options allows for having params such as a hash on the link

const user = User.find(1);
user.routeURL('show', { hash: '#hash' }); // --> { name: 'some-user', params: { id: 1 }, hash: '#hash' }

showURL(options = {})

Generate a route URL with show prefix on a model instance.

const user = User.find(1);
user.showURL(); // --> { name: 'show-user', params: { id: 1 } }

editURL(options = {})

Generate a route URL with edit prefix on a model instance.

const user = User.find(1);
user.showURL(); // --> { name: 'edit-user', params: { id: 1 } }

listKey

Generate unique key for iterations in DOM such as v-for in vuejs.

const user = User.find(1);
user.listKey(); // --> 'user-1'

pickKeys(keys = Object.keys(this.$toJson()))

Serializes all fields by default but the $id of an entity. Using the keys parameter you can define which keys should be serialized.

const user = User.find(1);
user.pickKeys(); // --> { firstname: 'Hugo', lastname: 'Boss' }
user.pickKeys(['firstname']); // --> { firstname: 'Hugo' }

The save and update method uses pickKeys to determine which fields should be send to the backend. You could override this method to always include a certain key:

pickKeys(keys) {
  return super.pickKeys([
    ...keys,
    'this_key_is_always_in_the_payload',
  ]);
}

Caching

Every fetch or fetchAll request first hits the store and returns the entities immediately if found. In the background the http request happens and updates as soon as the response is back. This method will make your UI respond fast on the first hit but may be outdated. But the request in the background will update it as soon as possible.

The caching is enabled by default and can be turned of globally by passing the useCache option.

import VuexORM from '@vuex-orm/core';
import VuexORMRest from 'vuex-orm-rest';

VuexORM.use(VuexORMRest, { useCache: false });

It is also possible to disable the caching for a single request.

User.fetch(1, { useCache: false });

Async queue

The async queue is a utility which helps to manage fetching data. It is possible to execute functions sequentially or in parallel. All the methods from the queue are chainable. The whole queue is executed using the exec method.

Sequence

The sequence method executes functions sequentially. The result from the previous sequence is passed to the next step when resolved.

const res = await Queue
  .sequence(() => Promise.resolve(1))
  .sequence((res) => Promise.resolve(res + 1))
  .exec(); // Evaluates to 2

Parallel

The parallel method executes functions in parallel. The result from the previous parallel is passed to the next step as an array when all functions are resolved.

const res = await Queue
  .parallel(() => Promise.resolve(1))
  .parallel(() => Promise.resolve(2))
  .parallel(() => Promise.resolve(3))
  .exec(); // Evaluates to [1, 2, 3]

It is also possible to mix the parallel and sequence method.

const res = await Queue
  .sequence(() => Promise.resolve(1))
  .parallel((res) => Promise.resolve(res + 1))
  .parallel((res) => Promise.resolve(res + 1))
  .parallel((res) => Promise.resolve(res + 1))
  .sequence((res) => Promise.resolve(res))
  .exec(); // Evaluates to [2, 2, 2]