vuex-crud

0.3.2 • Public • Published

Vuex-CRUD

Build Status codecov npm npm

Introduction

Vuex-CRUD is a library for Vuex which helps you to build CRUD modules easily.

Installation

yarn add vuex-crud

OR

npm install vuex-crud

Vuex-CRUD uses Array.prototype.includes, Object.assign and Promise. Make sure hat your project use polyfill for those if you want to support older browsers! Look at here: https://babeljs.io/docs/usage/polyfill/ for more info.

Basic Usage

  1. Create your first CRUD resource:
// src/store/articles
 
import createCrudModule from 'vuex-crud';
 
export default createCrudModule({
  resource: 'articles'
});
  1. Register your CRUD resource in your store:
// src/store/index.js
 
import Vue from 'vue';
import Vuex from 'vuex';
 
import articles from '@/store/articles';
 
Vue.use(Vuex);
 
export default new Vuex.Store({
  state: {},
 
  modules: {
    articles
  }
});
  1. Use it in your components:
<!-- src/components/Articles -->
 
<template>
  <main>
    <article v-for="article in articleList">
      <h1>{{ article.title }}</h1>
      <p>{{ article.content }}</p>
    </article>
  </main>
</template>
 
<script>
  import { mapGetters, mapActions, mapState } from 'vuex';
 
  export default {
    name: 'articles',
 
    computed: {
      ...mapGetters('articles', {
        articleList: 'list'
      }),
 
      ...mapState([
        'route', // vuex-router-sync
      ]),
    },
 
    methods: {
      ...mapActions('articles', {
        fetchArticles: 'fetchList'
      }),
 
      fetchData() {
        return this.fetchArticles();
      }
    },
 
    watch: {
      $route: 'fetchData',
    },
 
    created() {
      this.fetchData();
    }
  };
</script>
<!-- src/components/Article -->
 
<template>
  <main>
    <article v-if="currentArticle">
      <h1>{{ currentArticle.title }}</h1>
      <p>{{ currentArticle.content }}</p>
    </article>
  </main>
</template>
 
<script>
  import { mapGetters, mapActions, mapState } from 'vuex';
 
  export default {
    name: 'article',
 
    computed: {
      ...mapGetters('articles', {
        articleById: 'byId'
      }),
 
      ...mapState([
        'route', // vuex-router-sync
      ]),
 
      currentArticle() {
        return this.articleById(this.route.params.id);
      }
    },
 
    methods: {
      ...mapActions('articles', {
        fetchArticle: 'fetchSingle'
      }),
 
      fetchData() {
        return this.fetchArticle({
          id: this.route.params.id
        });
      }
    },
 
    watch: {
      $route: 'fetchData',
    },
 
    created() {
      this.fetchData();
    }
  };
</script>

Advanced Usage

The following options are available when creating new Vuex-CRUD module:

import createCrudModule, { defaultClient } from 'vuex-crud';
 
export default createCrudModule({
  resource: 'articles', // The name of your CRUD resource (mandatory)
  idAttribute: 'id', // What should be used as ID
  urlRoot: '/api/articles', // The url to fetch the resource
  state: {}, // Initial state. It will override state generated by Vuex-CRUD
  actions: {}, // Initial actions It will override actions generated by Vuex-CRUD
  mutations: {}, // Initial mutations. It will override mutations generated by Vuex-CRUD
  getters: {}, // Initial getters. It will override getters generated by Vuex-CRUD
  client: defaultClient, // Axios client that should be used for API request
  onFetchListStart: () => {}, // Callback for collection GET start
  onFetchListSuccess: () => {}, // Callback for collection GET success
  onFetchListError: () => {}, // Callback for collection GET error
  onFetchSingleStart: () => {}, // Callback for single GET start
  onFetchSingleSuccess: () => {}, // Callback for single GET success
  onFetchSingleError: () => {}, // Callback for single GET error
  onCreateStart: () => {}, // Callback for POST start
  onCreateSuccess: () => {}, // Callback for POST success
  onCreateError: () => {}, // Callback for POST error
  onUpdateStart: () => {}, // Callback for PATCH start
  onUpdateSuccess: () => {}, // Callback for PATCH success
  onUpdateError: () => {}, // Callback for PATCH error
  onReplaceStart: () => {}, // Callback for PUT start
  onReplaceSuccess: () => {}, // Callback for PUT success
  onReplaceError: () => {}, // Callback for PUT error
  onDestroyStart: () => {}, // Callback for DELETE start
  onDestroySuccess: () => {}, // Callback for DELETE success
  onDestroyError: () => {}, // Callback for DELETE error
  only: [
    'FETCH_LIST',
    'FETCH_SINGLE',
    'CREATE',
    'UPDATE',
    'REPLACE',
    'DESTROY'
  ], // What CRUD actions should be available
  parseList: res => res, // Method used to parse collection
  parseSingle: res => res, // Method used to parse single resource
  parseError: res => res // Method used to parse error
});

Nested Resources

Vuex-CRUD is designed mainly for flatten APIs like:

/api/articles/
/api/users/1
/api/pages?byBook=1

but it also supports nested resources like:

/api/books/1/pages/10
/api/users/john/tasks/15

However your store will always be flattened and will look similar to this:

{
  books: {
    entities: {
      '1': {
        // ...
      }
    }
  },
  pages: {
    entities: {
      '1': {
        // ...
      },
      '2': {
        // ...
      },
      '3': {
        // ...
      }
    },
    list: ['1', '2', '3']
  },
}

There are 2 possible ways to implement provide custom URL:

  1. Provide custom url for each request:
fetchList({ customUrl: '/api/books/1/pages' });
fetchSingle({ customUrl: '/api/books/1/pages/1' });
create({ data: { content: '...' }, customUrl: '/api/books/1/pages' });
update({ data: { content: '...' }, customUrl: '/api/books/1/pages/1' });
replace({ data: { content: '...' }, customUrl: '/api/books/1/pages/1' });
destroy({ customUrl: '/api/books/1/pages/1' });
  1. Define a getter for custom url:
import createCrudModule from 'vuex-crud';
 
export default createCrudModule({
  resource: 'pages',
  customUrlFn(id, type, bookId) {
    // id will only be available when doing request to single resource, otherwise null
    // type is the actions you are dispatching: FETCH_LIST, FETCH_SINGLE, CREATE, UPDATE, REPLACE, DESTROY
    const rootUrl = `/api/books/${bookId}`;
    return id ? `rootUrl/${id}` : rootUrl;
  }
});

and your requests will look this:

const id = 2;
const bookId = 1;
 
fetchList({ customUrlFnArgs: bookId });
fetchSingle({ id, customUrlFnArgs: bookId });
create({ data: { content: '...' }, customUrlFnArgs: bookId });
update({ id, data: { content: '...' }, customUrlFnArgs: bookId });
replace({ id, data: { content: '...' }, customUrlFnArgs: bookId });
destroy({ id, customUrlFnArgs: bookId });

Custom client

Vuex-CRUD is using axios for API requests. If you want to customize interceptors or something else, then you can do following:

import createCrudModule, { defaultClient } from 'vuex-crud';
import authInterceptor from './authInterceptor';
 
defaultClient.interceptors.request.use(authInterceptor);
 
createCrudModule({
  resource: 'comments',
  client: defaultClient
});

Parsing Data from Your API

You can provide a custom methods to parse data from your API:

import createCrudModule, { defaultClient } from 'vuex-crud';
 
createCrudModule({
  resource: 'articles',
 
  parseList(response) {
    const { data } = response;
 
    return Object.assign({}, response, {
      data: data.result // expecting array of objects with IDs
    });
  },
 
  parseSingle(response) {
    const { data } = response;
 
    return Object.assign({}, response, {
      data: data.result // expecting object with ID
    });
  }
});

Getters

Vuex-CRUD ships with following getters:

  • list() returns list of resources
  • byId(id) returns resource by ID

Actions

Vuex-CRUD ships with following actions (config is configuration for axios):

{
  // GET /api/<resourceName>
  fetchList({ commit }, { config } = {}) {
    // ...
  },
 
  // GET /api/<resourceName>/:id
  fetchSingle({ commit }, { id, config } = {}) {
    // ...
  },
 
  // POST /api/<resourceName>
  create({ commit }, { data, config } = {}) {
    // ...
  },
 
  // PATCH /api/<resouceName>/:id
  update({ commit }, { id, data, config } = {}) {
    // ...
  },
 
  // PUT /api/<resouceName>/:id
  replace({ commit }, { id, data, config } = {}) {
    // ...
  },
 
  // DELETE /api/<resouceName>/:id
  destroy({ commit }, { id, config } = {}) {
    // ...
  },
}

Usage with Nuxt

vuex-crud works with Nuxt modules system as well. You can simply define your Nuxt modules as following:

import createCRUDModule from 'vuex-crud';
 
const crudModule = createCRUDModule({
  resource: 'articles'
});
 
const state = () => crudModule.state;
 
const { actions, mutations, getters } = crudModule;
 
export {
  state,
  actions,
  mutations,
  getters
};

and then use it in your component:

export default {
  computed: {
    ...mapGetters('articles', {
      articleList: 'list'
    })
  },
 
  fetch({ store }) {
    store.dispatch('articles/fetchList');
  }
};

License

The MIT License (MIT) - See file 'LICENSE' in this project

Copyright

Copyright © 2017 Jiří Chára. All Rights Reserved.

Readme

Keywords

none

Package Sidebar

Install

npm i vuex-crud

Weekly Downloads

88

Version

0.3.2

License

MIT

Unpacked Size

58.3 kB

Total Files

15

Last publish

Collaborators

  • jiri