@boite-beet/api-client
v2.1.0
Published
A Nova and WP REST API client for Beet's VueJS 3 projects.
Downloads
52
Readme
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:
| Property | Type | Default | Details |
| ----------- | ------------- | ---------- | ------------------------------------------------------------ |
| store | Vuex instance | required | This plugin needs to add itself as a module in your project's vuex store. |
| apiUrl | String | required | The backend's base URL. Should be stored in .env
files. |
| mode | String | 'nova'
| Accepts either 'nova'
or 'wp'
. |
| initialData | Object | {}
| 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. |
| defaultLang | String | null | Define 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 becometrue
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
| Argument | Required | Details |
| ------------- | :------: | --------------------------------------------------------- |
| dataKey
| yes | The key under which the dataset is stored in state.data
|
| axiosConfig
| yes | An axios request config object |
| dataParser
| no | A 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
| Argument | Required | Details |
| ------------- | :------: | -------------------------------------------------------------- |
| dataKey
| yes | The dataset key under which the item is stored in state.data
|
| singleKey
| yes | The key under which the item is stored in the dataset |
| axiosConfig
| yes | An axios request config object |
| dataParser
| no | A 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 [email protected]:boitebeet/beet-api-client.git
cd beet-api-client
yarn
Scripts
build
- compiles the package into thedist/
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