This package helps you quickly to build requests for REST API. Move your logic and backend requests to dedicated classes. Keep your code clean and elegant.
🔥 If you use Laravel, this package matches perfectly with spatie/laravel-query-builder.
Give me the result for a given criteria, include some entities, append extra fields and order the result!
// GET /posts?filter[status]=ACTIVE&include=user,category&append=likes&orderBy=-created_at,category_id
let posts = await Post
.where('status', 'ACTIVE')
.include('user', 'category')
.append('likes')
.orderBy('-created_at', 'category_id')
.get()
Just give me the first occurrence from response:
// GET /posts?filter[status]=ACTIVE
let post = await Post
.where('status', 'ACTIVE')
.first()
Nice! Now I want a specific object:
// GET /posts/1
let post = await Post.find(1)
Edit this and send it back:
// PUT /posts/1
post.title = 'Awsome!'
post.save()
Let's create a new object and post it:
let post = new Post()
// or
let post = new Post({title: 'Cool!'})
// POST /post
post.title = 'Another one'
post.save()
We can use relationships:
// GET /users/1
let user = await User.find(1)
// GET users/1/posts
let posts = await user
.posts()
.get()
yarn add vue-api-query
Create a plugin ~/plugins/vue-api-query.js
// inject global axios instance as http client to Model
import { Model } from 'vue-api-query'
export default function (ctx, injext) {
Model.$http = ctx.$axios
}
And register it on nuxt.config.js
plugins: [
'~plugins/vue-api-query'
]
Set up on src/main.js
[...]
import axios from 'axios'
import { Model } from 'vue-api-query'
// inject global axios instance as http client to Model
Model.$http = axios
[...]
Your base model should extend from vue-api-query
Model. Use base models is good practice in order to abstract configurations from your domain models.
models/Model.js
import { Model as BaseModel } from 'vue-api-query'
export default class Model extends BaseModel {
// define a base url for a REST API
baseURL () {
return 'http://my-api.com'
}
// implement a defult request method
request (config) {
return this.$http.request(config)
}
}
Just extends from your base model... and done!
It automatically pluralizes based on class name. So, REST API base resource for User
class would be /users
.
models/User.js
import Model from './Model'
export default class User extends Model {
}
If you need to customize the resource name just implement the resource()
method.
import Model from './Model'
export default class User extends Model {
resource()
{
return 'userz'
}
}
Of course you can add extra methods and computed properties like this:
import Model from './Model'
export default class User extends Model {
// computed properties are reactive -> user.fullname
get fullname()
{
return `${this.firstname} ${this.lastname}`
}
// method -> user.makeBirthday()
makeBirthday()
{
this.age += 1
}
}
You can set up relationships:
import Model from './Model'
import Post from './Post'
export default class User extends Model {
posts () {
return this.hasMany(Post)
}
}
It's ok if in some situations you need to call a custom resource from a already defined model. You can override dynamically the default resource calling custom()
method.
// GET /posts
let posts = await Post.get()
// GET /posts/latest
let latest = await Post
.custom('posts/latest')
.first()
/models/Post.js
import Model from './Model'
export default class Post extends Model {
// done :)
}
/models/User.js
import Model from './Model'
import Post from './Post'
export default class User extends Model {
posts () {
return this.hasMany(Post)
}
// computed properties :)
get fullname()
{
return `${this.firstname} ${this.lastname}`
}
// methods :)
makeBirthday()
{
this.age += 1
}
}
If the backend responds with ...
// response from API for /users/1
{
id: 1,
firstname: "John",
lastname: "Doe",
age: 25
}
We can do this:
//GET /users/1
let user = await User.find(1)
console.log(user.fullname) // John Doe
user.makeBirthday()
user.save()
Then save()
method will send back the new payload:
// PUT /users/1
{
firstname: "John",
lastname: "Doe",
age: 26 //<--- changed
}
Play with relationships:
// GET /users/1
let user = await User.find(1)
// GET /users/1/posts
let posts = await user.posts().get()
// Yes, you can do that before getting the posts
let posts = await user
.posts()
.where(...)
.append(...)
.include(...)
.orderBy(...)
.get()
You also can do that:
//GET /posts?filter[status]=ACTIVE,ARCHIVED
let posts = await Post
.whereIn('status', ['ACTIVE', 'ARCHIVED'])
.get()
If you like the "promise way" just do it like this:
// single object
let user
User
.where('status', 'ACTIVE')
.first()
.then(response => {
user = response
})
// array of objects
let users
User
.where('status', 'ACTIVE')
.get()
.then(response => {
users = response
// or (depending on backend response)
users = response.data
})
And in some page/component:
<template>
User:
<code>
{{ user }}
</code>
Posts from user:
<code>
{{ posts }}
</code>
</template>
<script>
import User from '@/models/User'
export default {
data()
{
return {
user: {},
posts: {}
}
},
async mounted()
{
this.user = await User.find(1)
this.posts = await this.user.posts().get()
}
}
</script>
// GET /users?sort=firstname&page=1&limit=20
let users = await User
.orderBy('firstname')
.page(1)
.limit(20)
.$get() // sometimes you will prefer $get()
This package automatically handles the response from backend and convert it into an instance of a such Model.
If your backend responds with a single object as a ROOT ELEMENT like this:
{
id: 1,
firstname: 'John',
lastname: 'Doe',
age: 25
}
So, find()
and first()
methods automatically will convert the backend response into an instace of User
model.
let user = await User.find(1)
//or
let user = await User.first()
// will work, because an instance of User was created from response
user.makeBirthday()
This WILL NOT be converted into User
model, because the main data is not the root element.
user: {
id: 1,
firstname: 'John',
lastname: 'Doe',
age: 25
}
An array of items from backend would be converted in the same way, ONLY if it responds in these formats:
let user = await User.get()
// works - array of object is the root element
[
{
id: 1,
firstname: 'John',
lastname: 'Doe',
age: 25
},
{
id: 2,
firstname: 'Mary',
lastname: 'Doe',
age: 22
}
]
// works - `data` exists in the root and contains the array of objects
{
data: [
{
id: 1,
firstname: 'John',
lastname: 'Doe',
age: 25
},
{
id: 2,
firstname: 'Mary',
lastname: 'Doe',
age: 22
}
],
someField: '',
anotherOne: '',
}
// Normally you would handle the response like this
let response = User.get()
let users = response.data
// or like this
const { data } = User.get()
let users = data
// but you can use the "fetch style request" with "$get()"
let users = await User
.where('status', 'ACTIVE')
.$get() // <---- HERE
This WILL NOT be converted into an array of User
model.
{
users: [
{
id: 1,
firstname: 'John',
lastname: 'Doe',
age: 25
},
{
id: 2,
firstname: 'Mary',
lastname: 'Doe',
age: 22
}
],
someField: '',
anotherOne: '',
}
-
Inspiration from milroyfraser/sarala.
-
Elegancy from DavidDuwaer/coloquent.
Why another package if we have those? Because currently (march, 2018) they restricted backend response to JSON API specification.
Twitter @robsontenorio