Elegant and simple way to build requests for REST API
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.
Basic usage
Give me the result for a given criteria, include some entities, append extra fields and order the result!
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:
let post = await Post
.where('status', 'ACTIVE')
.first()
Nice! Now I want a specific object:
let post = await Post.find(1)
Edit this and send it back:
post.title = 'Awsome!'
post.save()
Ops, delete it!
post.delete()
Let's create a new object and post it:
let post = new Post()
post.title = 'Another one'
let post = new Post({title: 'Cool!'})
post.save()
We can use relationships:
let user = await User.find(1)
let posts = await user
.posts()
.get()
Installation
yarn add vue-api-query
NUXT
Create a plugin ~/plugins/vue-api-query.js
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'
]
VUE
Set up on src/main.js
[...]
import axios from 'axios'
import { Model } from 'vue-api-query'
Model.$http = axios
[...]
Configuration
Define a base model
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 {
baseURL () {
return 'http://my-api.com'
}
request (config) {
return this.$http.request(config)
}
}
Define your domain models
Just extends from your base model, implement the resource()
method... and done!
models/User.js
import Model from './Model'
export default class User extends Model {
resource()
{
return 'users'
}
}
But, if your model does not work with default primary key ('id'),you need to override the primaryKey()
method:
import Model from './Model'
export default class User extends Model {
primaryKey()
{
return 'someId'
}
}
Of course you can add extra methods and computed properties like this:
import Model from './Model'
export default class User extends Model {
get fullname()
{
return `${this.firstname} ${this.lastname}`
}
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.
let posts = await Post.get()
let latest = await Post
.custom('posts/latest')
.first()
Full example
/models/Post.js
import Model from './Model'
export default class Post extends Model {
resource()
{
return 'posts'
}
}
/models/User.js
import Model from './Model'
import Post from './Post'
export default class User extends Model {
resource()
{
return 'users'
}
posts () {
return this.hasMany(Post)
}
get fullname()
{
return `${this.firstname} ${this.lastname}`
}
makeBirthday()
{
this.age += 1
}
}
If the backend responds with ...
{
id: 1,
firstname: "John",
lastname: "Doe",
age: 25
}
We can do this:
let user = await User.find(1)
console.log(user.fullname)
user.makeBirthday()
user.save()
Then save()
method will send back the new payload:
{
firstname: "John",
lastname: "Doe",
age: 26
}
You also can do that:
let posts = await Post
.whereIn('status', ['ACTIVE', 'ARCHIVED'])
.get()
If you like the "promise way" just do it like this:
let user
User
.where('status', 'ACTIVE')
.first()
.then(response => {
user = response
})
let users
User
.where('status', 'ACTIVE')
.get()
.then(response => {
users = 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>
Relationships
let user = await User.find(1)
let posts = await user.posts().get()
let posts = await user
.posts()
.where(...)
.append(...)
.include(...)
.orderBy(...)
.get()
If you like nested relationships ...
let comments = await this.post.comments().get()
let comment = comments[0]
comment.text = 'Changed!'
await comment.save()
await comment.delete()
And just for convenience you can POST or PUT with any payload to backend:
await this.posts.comments().attach(payload)
await this.posts.comments().sync(payload)
let users = await User
.orderBy('firstname')
.page(1)
.limit(20)
.get()
Selecting fields
Just want only some fields?
let post = await Post
.select(['title', 'content'])
.get()
With related entities:
let post = await Post
.select({
posts: ['title', 'content'],
user: ['age', 'firstname']
})
.include('user')
.get()
TIP: If you are using spatie/laravel-query-builder, when using related entities, you must pass extra fields:
let post = await Post
.select({
posts: ['title', 'content', 'user_id'],
user: ['id', 'age', 'firstname']
})
.include('user')
.get()
Response from backend
This package automatically handles the response from backend and convert it into an instance of a such Model.
Single object
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)
let user = await User.first()
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
}
Array of objects
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()
[
{
id: 1,
firstname: 'John',
lastname: 'Doe',
age: 25
},
{
id: 2,
firstname: 'Mary',
lastname: 'Doe',
age: 22
}
]
{
data: [
{
id: 1,
firstname: 'John',
lastname: 'Doe',
age: 25
},
{
id: 2,
firstname: 'Mary',
lastname: 'Doe',
age: 22
}
],
someField: '',
anotherOne: '',
}
let response = User.get()
let users = response.data
const { data } = User.get()
let users = data
let users = await User.$get()
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: '',
}
Thanks
Why another package if we have those? Because currently (march, 2018) they restricted backend response to JSON API specification.
Contact
Twitter @robsontenorio