vue-composition-crud
v1.22.1
Published
CRUD API Composables for Vue
Downloads
7
Readme
Vue Composition CRUD
This package supplies modules and classes to allow for easy interaction with a CRUD-based API, including Vue Composition providers.
- Vue Composition CRUD
- Installation
- API Documentation
- Overview of Key Package Contents
- Basic Usage
- Examples
- Typescript
- Testing
Installation
yarn add vue-composition-crud
API Documentation
Full API documentation is created with typedoc
and can be generated by running yarn docs
.
The latest full API documentation is generated as part of the CI/CD pipeline.
Documentation generated on demand is placed in the docs folder.
Overview of Key Package Contents
API
The API
class supplies a layer to provide HTTP(S) operations, which can
be augmented with middleware.
The API
class emits the following events:
authenticated
- when an authentication provider handles the authentication operationauthentication-failed
- when a request fails due to a401
HTTP errordeauthenticated
- when an authentication provider handles the deauthentication operationrequest-failed
- when a request fails due to a different HTTP error
Middleware
Middlware classes are used to provide additional functionality when performing requests. This can include things like:
- performing authentication
- tweaking request and response URLs, headers, and content
- performing logging operations
The built-in middleware currently includes:
SessionAuthenticationMiddleware
- authenticate with a username and password, and then persist the session with a cookie.TokenAuthenticationMiddleware
- authenticate with either a username and password, or an access and/or refresh token, or an API key, and then persist the session by supplying a bearer token header with each request.CsrfTokenMiddleware
- receive a CSRF token from a header, a cookie, or by supplying a custom function to retrieve the value, and then send the CSRF token when making requests by supplying it as a request header or injecting it into the body of the request.TrailingSlashMiddleware
- tweak all outgoing URLs to either include or not include trailing slashes.PageMiddlware
- advanced pagination features to allow you to use the built-in pagination class with APIs that don't supply objects in exactly the same way as expected. This allows for the use of an offset-limit style pagniation system, allows the transformation of query parameters, and allows for the response from the remote server to be mapped to an object that has the shape that this package expects.
Composables
useApi
useApi(options, middlewares) => {
api: Ref<CrudApi>
}
Creates a new Api
instance and returns it as a Ref
.
useGlobalApi
useGlobalApi(options, middlewares) => {
api: Ref<CrudApi>
}
The same as useApi
, but creates a global instance. The first time you
call this, it will create the API instance and initialize the ref. Every
subsequent call to this function simply retrieves the initial ref.
I'd recommend using provide()
and inject()
over a global mechanism, but
some people are into it, so here you go.
useResource
useResource<ItemType>(api, resourceName) => {
isUnloaded: Ref<boolean>,
isLoading: Ref<boolean>,
isLoaded: Ref<boolean>,
isFailed: Ref<boolean>,
error: Ref<Error>,
item: Ref<ItemType>,
createItem(value: ItemType): Promise<ItemType>,
retrieveItem(id: string | number): Promise<ItemType>,
updateItem(id: string | number, value: ItemType): Promise<ItemType>,
deleteItem(id: string | number): Promise<void>,
}
Create a resource to handle Create, Retrieve, Update, and Delete (CRUD) operations for a specific API resource.
The resourceName
parameter is just the API endpoint name, and is appended to
the Base URL that is supplied when you create the Api instance with new Api({...})
or useApi({...})
. For example, if your URL was https://www.example.com/api/users
,
then the baseUrl
would be https://www.example.com/api/
, and the resourceName
would
be users
.
The four boolean refs are used to determine the current state of the resource.
isUnloaded
is true when the resource has not yet been used.isLoading
is true when any CRUD operation is being performed.isLoaded
is true when any CRUD operation completes successfully.isFailed
is true when any CRUD operation fails.error
is the last error object thrown to cause the CRUD operation to fail.item
is set to the content of the resource when a create, retrieve, or update operation completes successfully.item
is cleared when a delete operation completes successfully.createItem
,retrieveItem
,updateItem
, anddeleteItem
call upon the underlying API instance to perform the relevant queries. These queries match toPOST
,GET
,PATCH
, andDELETE
HTTP method operations.
useCollection
useCollection<ItemType>(api, resourceName) => {
isUnloaded: Ref<boolean>,
isLoading: Ref<boolean>,
isLoaded: Ref<boolean>,
isFailed: Ref<boolean>,
error: Ref<Error>,
items: Ref<Array<ItemType>>,
listItems(queryOptions): Promise<Array<ItemType>>,
}
Create a collection to perform list operations against a specific API resource.
Most of the returned refs are the same as in useResource
. The notable differences are
that:
- Instead of a single resource
item
, you have an array ofitems
- Instead of CRUD methods, you have a
listItems
method
The listItems
method allows you to supply queryOptions
, which can be used to
search and filter the resultset on the server side. For example:
const collection = useCollection
collection.listItems({search: "my search text", filter: {"name": "john", "age": "33"}})
usePage
usePage<ItemType>(api, resourceName) => {
isUnloaded: Ref<boolean>,
isLoading: Ref<boolean>,
isLoaded: Ref<boolean>,
isFailed: Ref<boolean>,
error: Ref<Error>,
page: Ref<Page<ItemType>>,
items: Ref<Array<ItemType>>,
listItems(queryOptions): Promise<Page<ItemType>>,
}
Much the same as useCollection
, but instead of expecting an array of items,
this expects a Page
. One page supplies a subset of results, as well as some
extra details like what the next page is, the total number of pages, and any
metadata that the remote server might like to supply.
The items
ref will be updated with the current set of items based on the page
that is being viewed.
The expected structure of a Page
response is:
{
page: {
previous?: number;
current: number;
next?: number;
last?: number;
};
items: Array<ItemType>;
meta?: unknown;
}
Basic Usage
/*
* symbols.ts
*/
// Define the `API` symbol, which is used to provide
// and inject an api instance in Vue components.
export const API = Symbol("API");
<!--
-- App.vue
-->
<template>
...
</template>
<script lang="ts">
import { ref, provide, defineComponent } from "vue";
import { useApi } from "vue-composition-crud";
import { API } from "./symbols.ts"
export default defineComponent({
setup() {
// Create an API instance.
// Note that the `useApi` composition method gives
// you a Ref<CrudApi> instance so to use it you will
// need to call `api.value.xxxx()`
const { api } = useApi({
baseUrl: "https://www.example.com/api/",
});
// Supply the `api` instance to all children using
// the `API` symbol as a key.
provide(API, api);
return {};
}
})
</script>
<!--
-- UserInformation.vue
-->
<template>
<div v-if="resource.isLoading.value">Loading - just a moment...</div>
<div v-if="resource.isFailed.value">Loading failed: {{ resource.error.value }}</div>
<div v-if="resource.isLoaded.value">
<h1>{{ user.name }}</h1>
<h2>{{ user.age }} years old</h2>
<address>{{ user.address }}</address>
<div>
<a :href="'mailto:' + user.email">{{ user.email }}</a>
</div>
</div>
</template>
<script lang="ts">
import { Ref, inject, defineComponent, onMounted } from "vue";
import { useApi, useResource, CrudApi } from "vue-composition-crud";
import { CrudApi } from "vue-composition-crud/api/types";
import { API } from "./symbols.ts"
export default defineComponent({
setup() {
// Inject the `api` instance from the parent context.
// Remember that this is a `Ref<CrudApi>` instance.
const api = inject(API) as Ref<CrudApi>;
// Use the API *value* to set up the useful composition
// elements like lists, pages, and resources.
const resource = useResource(api.value, "users");
// When the component is mounted, load user #123
onMounted(() => {
resource.retrieve(123)
})
// Supply the interesting stuff (e.g., the resource) to
// the component.
return {
resource,
user: resource.item.value,
}
},
})
</script>
Examples
Typescript
This package is developed in typescript, compiled down to javascript, and supplies declaration files.
Testing
This package uses mocha
and chai
for testing.
Tests can be run with yarn test
and code coverage can be generated with yarn coverage
.
The latest coverage reports are generated as part of the CI/CD pipeline.