2.1.0 • Published 1 year ago

@boite-beet/api-client v2.1.0

Weekly downloads
-
License
MIT
Repository
bitbucket
Last release
1 year ago

BEET API Client

Beet's API client for WP and Laravel Nova

Installation

In an existing project, open a command line at the root level and run:

yarn add @boite-beet/api-client

Usage

In src/main.js initialize through Vue's plugin api:

// src/main.js
import BeetAPI from '@boite-beet/api-client'
import store from './store'

Vue.use(BeetAPI, {
  store,
  apiUrl: process.env.VUE_APP_API_URL,
  mode: 'nova',
  initialData: { userInvoices: {} },
  defaultLang: 'fr'
})

Configuration

The configuration object accepts three properties:

PropertyTypeDefaultDetails
storeVuex instancerequiredThis plugin needs to add itself as a module in your project's vuex store.
apiUrlStringrequiredThe backend's base URL. Should be stored in .env files.
modeString'nova'Accepts either 'nova' or 'wp'.
initialDataObject{}An object that will be mixed into the data state every time fetchCoreData is called. This is intended for custom-types that are not returned with the core data, such as private data that requires authentication.
defaultLangStringnullDefine the default lang of the backend. (Used to remove lang from the query with wp-json)

If you are using our Vue project template (which you should ಠ_ಠ) this will already be in place.

Store state structure

The store module's initial state looks like this:

{
  pages: {},
  options: {},
  data: {},
  isReady: false
}
  • The pages object stores all individual page data (home, about, contact, etc.) under a key corresponding to the page's backend slug.
  • The options object contains all shared data (company info, social media, etc.) from the options route.
  • The data object is where all the custom types are stored under a key corresponding to the custom type's backend slug.
  • Finally, isReady is a boolean that will become true once the first call to /data and /options has completed.

The full store path to this state is $store.state.beet but can be accessed more easily through the $beet global injection, detailed below.

Global state

The plugin exposes a global $beet property on all instances of Vue. This property contains all of the same data you'll find in the vuex state except for the data property, which is instead mixed into the object. This means all your custom types are exposed at the root level (eg. $beet.products).

There is also a fetchCoreData method, for retrieving the core data returned from the /data and /options endpoints. This method is detailed below.

Loading core data

The core data, which is found at the /data and /options endpoint in our APIs, is the minimum required dataset needed to ensure the app won't crash due to missing fields.

The ideal place to fetch this data is from src/App.vue seeing as you only want to fetch on first load and when the language changes. The root App.vue component will only be loaded once and is persistent for the rest of the session.

fetchCoreData method

To launch the request to the API, use the fetchCoreData method found on the $beet global injection. It accepts two optional arguments:

  • the first is the current language code, which will be passed as the lang query parameter to the backend. Defaults to undefined and will be omitted from the query.
  • the second is a function to transform the data before storing it. It will receive the response object as its only argument and the returned object will then be stored in the vuex state.

Detecting when core data is loaded

Once the first call to this method has completed, $beet.isReady will become true. This can be used to universally block rendering at the root level to prevent errors caused by missing core data.

Examples

For single language sites
<template>
  <main v-if="$beet.isReady" id="app">
    <RouterView />
  </main>
</template>

<script>
  export default {
    name: 'App',
    beforeMount() {
      this.$beet.fetchCoreData()
    }
  }
</script>
For multi language sites using our LangRouter package
<template>
  <main v-if="$beet.isReady" id="app">
    <RouterView />
  </main>
</template>

<script>
  export default {
    name: 'App',
    watch: {
      '$lang.current': {
        immediate: true,
        handler(lang) {
          this.$beet.fetchCoreData(lang)
        }
      }
    }
  }
</script>

If you're using 'wp' mode, don't forget to setup the 'defaultLang' param in the install of this plugin or wp-json will fail to return a correct response.

Dynamically adding custom types

If for any reason there are custom types that are excluded from the /data route, you can load them manually at any time using the fetchData action which is detailed later.

However the key corresponding to this custom type will not exist in the state until it is fetched. This can cause the app to crash if you try to access items in that dataset before they have finished loading.

To help address this, you can add an initialData property to the options object when you call Vue.use. This will be merged into the data state object every time fetchCoreData is called. With this done, we could safely try to access trainings in this object to see if they existed before attempting to read the data.

Pages

To load the data associated with a given page, use the beetPage component option to indicate which slug to fetch from the API. Once a page's content has been loaded, it will not be fetched again. Instead, the existing content will be returned.

A $page computed property will be created on your component. The property's value will be null until the data has loaded, at which point the object will be accessible through $page. You can leverage this to prevent the content from rendering until the data is ready.

It may however not be desireable to place the v-if attribute on the root element, as this could cause the layout to appear broken or incomplete until the page is loaded. Instead, you can apply the condition to a <template> child element.

Specifying language

If you are using our LangRouter package, the language will be picked up and changes to it will re-fetch content automatically.

In cases where you aren't using the LangRouter, you can specify the language key to watch by adding a beetLang option to your component. This should be a string representing the key of anything reactive accessible on the component's this.

export default {
  name: 'Home',
  beetPage: 'home',
  beetLang: '$i18n.locale'
}

If both the LangRouter's $lang.current and beetLang are defined, beetLang takes precedence.

Example

<!-- src/views/Home.vue -->
<template>
  <div :class="['home', { '-loading': !$page }]">
    <template v-if="$page">
      <header class="header">
        <h1 class="header__title">{{ $page.title }}</h1>
      </header>

      <!-- ... -->
    </template>
  </div>
</template>

<script>
  export default {
    name: 'Home',
    beetPage: 'home'
  }
</script>

Actions

There are two configurable actions which are intended to be used in your app.

fetchData

This action is used to fetch an index of a custom type's items and store them. This is useful for indexes that are not returned in the core dataset. The action payload is an array of arguments following this usage pattern:

this.$store.dispatch('fetchData', [dataKey, axiosConfig, dataParser])
Arguments
ArgumentRequiredDetails
dataKeyyesThe key under which the dataset is stored in state.data
axiosConfigyesAn axios request config object
dataParsernoA function to transform the data before it is saved
Example

This example would load the response from /events into data.events.

this.$store.dispatch('fetchData', ['events', { url: '/events' }])

fetchSingle

This action is used to fetch a specific item of a custom type and store it. This can be particularly useful when the index only returns a subset of the item's contents and needs to be completed. The returned object will be mixed into the current value if it exists, preserving any non-duplicate keys. The action payload is an array of arguments following this usage pattern:

this.$store.dispatch('fetchSingle', [dataKey, singleKey, axiosConfig, dataParser])
Arguments
ArgumentRequiredDetails
dataKeyyesThe dataset key under which the item is stored in state.data
singleKeyyesThe key under which the item is stored in the dataset
axiosConfigyesAn axios request config object
dataParsernoA function to transform the data before it is saved
Example

This example would load the response from /events/party into data.events.party.

this.$store.dispatch('fetchSingle', ['events', 'party', { url: '/events/party' }])

Notes

Axios request config

Only the baseURL property will be automatically set to the API's URL if it is not provided. This allows you to take full control of how the data is fetched. For example, you can add authentication headers to the request by passing the headers property. See the axios docs for more information.

Detecting request completion

Both of them return a Promise which can be used to know when the data has been loaded and saved in the state:

export default {
  name: 'PrivateTraining',
  props: {
    slug: { type: String, required: true }
  },
  data() {
    return { isDataReady: false }
  },
  beforeMount() {
    const arguments = [
      'privateTrainings',
      this.slug,
      { url: `/trainings/${this.slug}` }
    ]

    this.$store.dispatch('fetchSingle', arguments)
      .then(() => {
        this.isDataReady = true
      })
  }
}

Development

Compilation/build is done using Rollup.js.

Installation

Clone the package and install the dependencies.

git clone git@bitbucket.org:boitebeet/beet-api-client.git
cd beet-api-client
yarn

Scripts

  • build - compiles the package into the dist/ directory.
  • start - compiles the package every time the source changes.

Using local package in a project

Yarn has built in functionality that allows you to link a local package to a project. The "package" is the library that will be installed via npm and the "project" is the app/website in which you want to test it.

In the package's directory

yarn link

This only needs to be done once, the link remains available until you run yarn unlink. There is no danger or issue created by leaving the link permanently.

In your project's directory

yarn link @boite-beet/api-client

Now, if you run yarn start in the package and yarn start in your project, your project's development server will detect when the package has changed (compile on save) and reload.

Unlinking from your project

Once you are done working on the package and have published, it is recommended to unlink it from your project and reinstall the dependancy to verify that everything has deployed correctly.

To do this, run these commands in your project:

yarn unlink @boite-beet/api-client
yarn add @boite-beet/api-client@latest

Publishing

This requires you to be a member of the boite-beet org on npm and for you to be logged in to your npm account (using the npm login command).

Publish from master

Make sure you are in the master branch and that all your changes have been merged.

git checkout master
git merge develop

Update the version

Run the yarn version command and enter the new version as prompted. This will also create a version tag in git. Versions should follow the semver pattern of major.minor.patch

  • Patch: bugfixes and inconsequential changes
  • Minor: new features
  • Major: breaking changes

Push changes including version tags

git push --follow-tags

Publish to npm

npm publish

Merge the new package version into develop

git checkout develop
git merge master
2.1.0

1 year ago

2.0.0

2 years ago

1.2.9

2 years ago

1.2.10

2 years ago

1.2.11

2 years ago

1.2.0

2 years ago

1.1.1

2 years ago

1.0.2

2 years ago

1.1.0

2 years ago

1.0.1

2 years ago

1.2.8

2 years ago

1.2.7

2 years ago

1.2.6

2 years ago

1.2.5

2 years ago

1.2.4

2 years ago

1.2.3

2 years ago

1.0.5

2 years ago

1.2.2

2 years ago

1.0.4

2 years ago

1.2.1

2 years ago

1.0.3

2 years ago

1.0.0

3 years ago