npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vuex-jsonapi

v0.0.3

Published

Library to access JSON API data via Vuex stores

Downloads

6

Readme

vuex-jsonapi

vuex-jsonapi, unsurprisingly, allows you to access data from a JSON API web service via Vuex stores. Because of JSON API's strong conventions, in most cases all you should need to do is tell vuex-jsonapi the base URL of your web service, and which resources to access, and you should be set. No manual web request juggling!

This is a very early proof-of-concept, so THERE IS NO ERROR HANDLING YET, and many features of JSON API are not yet supported. Open a GitHub issue with any other features you'd like to see!

Installation

# npm install --save vuex-jsonapi

Setup

To create a Vuex module corresponding to a resource on the server, call resourceModule():

import { Store } from 'vuex';
import { resourceModule } from 'vuex-jsonapi';
import api from './api';

const store = new Store({
  modules: {
    'widgets': resourceModule({
      name: 'widgets',
      httpClient: api,
    }),
  },
});

If you are accessing multiple resources, you can use mapResourceModules():

import { Store } from 'vuex';
import { mapResourceModules } from 'vuex-jsonapi';
import api from './api';

const store = new Store({
  modules: {
    ...mapResourceModules({
      names: [
        'widgets',
        'purchases',
      ],
      httpClient: api,
    }),
  },
});

The httpClient accepts an object with a signature similar to the popular Axios HTTP client directory. You can either pass in an Axios client configured with your base URL and headers. Note that spec-compliant servers will require a Content-Type header of application/vnd.api+json; you will need to configure your HTTP client to send that.

import axios from 'axios';

const httpClient = axios.create({
  baseURL: 'http://api.example.com/',
  headers: {
    'Content-Type': 'application/vnd.api+json',
    'Authentication': `Bearer ${token}`,
  },
});

const module = resourceModule({
  name: 'widgets',
  httpClient,
})

Or else you can pass in an object that exposes the following methods:

const httpClient = {
  get(path) {
    // ...
  },
  post(path, body) {
    // ...
  },
  patch(path, body) {
    // ...
  },
  delete(path, body) {
    // ...
  },
};

That's all you need to do--the JSON API spec takes care of the rest!

Usage

Working with JSON API data is split into two parts to follow Vuex conventions:

  • Actions are used to request data from the server or update data on the server, storing the results into the module's state.
  • Getters are used to access data from the module's state.

loadAll action / all getter

So, for example, to retrieve all of the records for a resource, dispatch the loadAll action to save them into the store. They can then be accessed using all getter:

this.$store.dispatch('widgets/loadAll')
  .then(() => {
    const widgets = this.$store.getters['widgets/all'];
    console.log(widgets);
  });

If you're accessing these from within a Vue component, you can use Vuex's mapActions and mapGetters as usual:

import { mapActions, mapGetters } from 'vuex';

export default {
  // ...
  methods: {
    ...mapActions({
      loadWidgets: 'widgets/loadAll',
    }),
  },
  computed: {
    ...mapGetters({
      widgets: 'widgets/all',
    }),
  },
  // ...
};

loadById action / byId getter

To retrieve a single record by ID, dispatch the loadById action, then access the byId getter:

this.$store.dispatch('widgets/loadById', { id: 42 })
  .then(() => {
    const widget = this.$store.getters['widgets/byId']({ id: 42 });
    console.log(widget);
  });

However, the beauty of storing your data in Vuex is that if you know the record has already been retrieved, you don't need to load it again. For example, if you've loaded all records on a list screen, and then you click to view the details for a single record, you can just use the getter directly:

const widget = this.$store.getters['widgets/byId']({ id: 42 });
console.log(widget);

loadWhere action / where getter

To filter/query for records based on certain criteria, use the loadWhere action, passing it an object of filter keys and values to send to the server, then pass those same filters to the where getter:

const filter = {
  category: 'whizbang',
};
this.$store.dispatch('widgets/loadWhere', { filter });
  .then(() => {
    const widgets = this.$store.getters['widgets/where']({ filter });
    console.log(widgets);
  });

This doesn’t perform any filtering logic on the client side; it simply keeps track of which IDs were returned by the server side request and retrieves those records.

loadRelated action / related getter

Finally, to load records related via JSON API relationships, use the loadRelated action. A nested resource URL is constructed like categories/27/widgets. (In the future we will look into using HATEOAS to let the server tell us the relationship URL).

const parent = {
  type: 'category',
  id: 27,
};

this.$store.dispatch('widgets/loadRelated', { parent })
  .then(() => {
    const widgets = this.$store.getters['widgets/related']({ parent });
    console.log(widgets);
  });

By default, the name of the relationship on parent is assumed to be the same as the name of the other model: in this case, widgets. In cases where the names are not the same, you can explicitly pass the relationship name:

const parent = {
  type: 'categories',
  id: 27,
};

const relationship = 'purchased-widgets';

this.$store.dispatch('widgets/loadRelated', { parent, relationship })
  .then(() => {
    const widgets = this.$store.getters['widgets/related']({ parent, relationship });
    console.log(widgets);
  });

create

To create records on the server and also store it locally, use the create action. Pass it an object containing an attributes object. This is similar to a JSON API record, but you don't need to specify the type -- the store will add the type.

const recordData = {
  attributes: {
    title: 'My Widget',
  },
};
this.$store.dispatch('widgets/create', recordData);

You can also save relationships by providing a relationships attribute, just like in the JSON API spec:

const recordData = {
  attributes: {
    title: 'My Widget',
  },
  relationships: {
    category: {
      data: {
        type: 'categories',
        id: 42,
      },
    },
  },
};
this.$store.dispatch('widgets/create', recordData);

update

To update records, pass the entire updated record object to the update action:

const widget = this.$store.getters['widgets/byId']({ id: 42 });
widget.attributes.title = 'Updated Title';
this.$store.dispatch('widgets/update', widget);

delete

To delete, pass either a full record or just an object with an ID field:

const widgetIdObject = { id: 42 };
this.$store.dispatch('widgets/delete', widget);

License

Apache 2.0