robsontenorio / vue-api-query Goto Github PK
View Code? Open in Web Editor NEW๐ Elegant and simple way to build requests for REST API
Home Page: https://robsontenorio.github.io/vue-api-query
License: MIT License
๐ Elegant and simple way to build requests for REST API
Home Page: https://robsontenorio.github.io/vue-api-query
License: MIT License
like
where (key, operator ,value)
where('status','>' ,1)
Thank.
How can I get the parameters from the router when I load the component in the method mounted and apply them to the model?
I use Vue cli and request api from difference host. it show
Access to XMLHttpRequest at 'http://example.test/api/borrower/1' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
HI,
In short, I'd like to be able to do the following:
// GET /posts/{id_post}/comments
let comments = await this.post.comments().get()
// Pick any comment from list and edit it
let comment = comments[0]
comment.text = 'Changed!'
// PUT /comments/{id_comment}
await comment.save()
// DELETE /comments/{id_comment}
await comment.delete()
and
let post = new Post({title: 'Woo!'})
// POST /posts
await post.save()
// POST /posts/{id_post}/comments
let comment = new Comment({text: 'New one for this post'}).for(post)
comment.text = 'Changed!'
// PUT /comments/{id_comment}
await comment.save()
// DELETE /comments/{id_comment}
await comment.delete()
Basically I want to use nested relationships only for the get
and post
actions, and the base route for find
, update
, and delete
.
The reason for this is because nested routes aren't necessary if you already have the unique ID of the model you want to interact with.
Is there any way to easily override this on all models? It seems like I'd have to re-write quite a bit but maybe I'm missing something!
Right now it doesn't seem possible as once the model is related it keeps the same endpoint.
May be if there's an option to do something like
Model.select( [ 'id', { 'title': 'name' } ] ).get() !
which is like select id, title as name from Model
But it's client side, the server returns name, and the client vue-api-query override with the given as attribute
Is this tied specifically to vue? Is there anything that prevents us from running this in another javascript framework?
Was wondering if this library has graphql support.
It is typical for JSON API's to wrap not only multiple results but also single results in a data
key. (Even Laravel's API Resources default to this behaviour.)
For multiple results (arrays of items), vue-api-query automagically works regardless of whether or not the data
key wrapper is present.
I feel that for single results there should be the same behaviour.
Backward-compatibility can be ensured by adding a configuration option or optional function argument to enable this behaviour. (Similar to withoutWrapping
in Laravel's API Resources. See Eloquent: API Resources)
So, if the backend responds with:
{
"data": {
id: 1,
firstname: "John",
lastname: "Doe",
age: 25
}
}
And withWrapping
is set to true, the behaviour should be the same as if the response was:
{
id: 1,
firstname: "John",
lastname: "Doe",
age: 25
}
Is there a way to get the url with the query without executing the API query?
Maybe I've missed it, but there doesn't seem to be any way to handle endpoints and relationships deeper than two models. For example, given that I have an App
, Build
& Dependency
model where the endpoint to list all dependencies of a given build of a given app looks like this:
/app/34/build/112/dependencies
Is there anything planned, or that I missed to handle this endpoint?
Hi,
First of all, as someone who was pretty new in the Vue game and previously used ugly axios calls in every component, this makes everything much prettier, so thanks for that!
Now, I was wondering, is there a possibility to use callbacks like beforeRequest
& afterRequest
or something that you can either define in the Base Model.js or in any of the Submodels?
Some background to understand my use case:
I am using https://www.npmjs.com/package/vue-loading-overlay which basically draws a Loading spinner over the entire screen while a specific variable is set to true
.
While it works perfectly fine like this:
async mounted() {
/*
* Fetches all Services
*/
this.$parent.isLoading = true;
this.services = await Service.get();
this.$parent.isLoading = false;
},
It would be much prettier with callbacks defined in the models.
Thanks!
Say I want to get the posts for ID user 1, "/users/1/posts". However, I have no need to get the actual user object, "/users/1" first. Is there a built-in way to do this? I didn't have luck trying to run User.find(1).posts().get()
for example. Any other way to accomplish this without the unneeded API call?
In my project I fetch a record "mainRecord". That has a relation to "childRecords".
When I want create now a new childRecord with that,
let childRecord = new this.mainRecord.childRecord({
val: null,
sequence: null
});
childRecord.save()
.then(response => {
})
.catch(error => {
});
it fails with:
Uncaught (in promise) TypeError: Cannot read property 'childRecord' of undefined
Whats wrong with my code? Fetching, updating and deleting of childRecords is working without any problem. Only creating fails, maybe because i am using the wrong syntax.
Line 40 in e0dfdf6
Overriding resource() works but not the default return.
Maybe I have missed it somehow but reading trough your docs I haven't found that you can customise how the parameters are named when sent to the back-end. For example if not working with Spatie ORM package.
Here are my two questions.
vue-query-api
generates URL and names parameters? From your docs// 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()
For example if I would like that filter[status]
is named filter_by[status]
for example.
//GET /posts?filter[status]=ACTIVE,ARCHIVED
let posts = await Post
.whereIn('status', ['ACTIVE', 'ARCHIVED'])
.get()
I wonder if it's possible to send like this:
//GET /posts?filter[status][]=ACTIVE&filter[status][]=ARCHIVED
Thank you.
Hi there!
I try to install version 1.1.1 with patch from peter (967f790) but i get only the response, that 1.1.1 isn't available. I've checked npm and yarn, both has still version 1.1.0.
Can you please submit the version to both repositories?
Thank you!
I have nested models like
User -> hasMany -> Posts.
When I try to fetch my post record like following:
let user = await User.find(1)
let post = await user.posts().find(myId)
my post
will not have _fromResource
property present, therefore it's not possible to update it using save()
function, while if to use get()
- then _fromResource
property is present and and model is aware of its nested resource url. Was it done intentionally or just a bug(can submit PR)? For reference:
find()
- https://github.com/robsontenorio/vue-api-query/blob/master/src/Model.js#L208
get()
- https://github.com/robsontenorio/vue-api-query/blob/master/src/Model.js#L219
Thanks for making this - works great. Though, I have a question about sending DELETE on a collection. I can get it well here:
Model.where('id', payload.id)
.get()
.then(response => {
modelItems = response;
modelItems.delete();
});
modelItems
loads up with the right information. But the destroy
method isn't working. Using Laravel for backend and I have this for destroy()
method:
public function destroy($ids)
{
$model = model::whereIn('id', $ids)->delete();
}
Is it possible to inject custom data into the Model, as is done with the axios?
Today with the axios is done like this:
import axios from 'axios'
import { Model } from 'vue-api-query'
// inject global axios instance as http client to Model
Model.$http = axios
I need something like this:
import axios from 'axios'
import { Model } from 'vue-api-query'
/**
* Adds configured plugin asynchronously.
*/
export default async context => {
// perform a store action manually
await context.store.dispatch('nuxtServerInit', context)
// inject instance into Model
Model.$env = { ...context.store.state.env }
// inject global axios instance as http client to Model
Model.$http = axios
}
But when I use inside the Model like this.$env, this does not work, can you tell me if I'm doing it wrong or is it possible to do this in a different way?
I've defined following 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 Vue.axios.defaults.baseURL.replace(/\/$/, "");
}
// implement a default request method
request(config) {
return this.$http.request(config)
}
}`
Product.js
import Model from './Model'
import ProductLinks from './ProductLink'
export default class Product extends Model {
resource() {
return 'products'
}
ProductLinks() {
return this.hasMany(ProductLink)
}
}
ProductLink.js
import Model from './Model'
export default class ProductLink extends Model {
resource() {
return 'productLinks'
}
}
Now I query:
this.product = await Product.find(6);
which resulting in http://.../products/6, works!
Next:
this.productLinkList = await this.product.ProductLinks().get();
which resulting in http://.../products/undefined/productLinkLists, does NOT work!
Investigation into that problem show really fast the problem: In that case, "product" hasn't a "id" field as primaryKey, instead it has a field "folderId". Quick & Dirty solution could be, rewrite code for using "id" but I think, there should be a option, define a alternative primaryKey for a model (like laravel for example it offers).
I'll dive into the code, maybe I can support a patch for it!
I had trouble defining custom properties on the models, maybe you have a
best-practice approach for me on this, as my current fix is a workaround.
I went and added a custom property to my base class for vue-js-api models
(I have a getter and setter as well):
MyModelBase.js
constructor(...args) {
super(args);
this._myProp = "";
return this;
}
Initializing a Model with the constructor resulted in exceptions in the browser console.
page.vue
// This throws an error in the console, that `.include()` is not a function
await MyModel
.include(['test'])
.first()
.then( response => () {} )
...
I realised later that adding the constructor did something bad.
Consider your Model's constructor:
Model.js
constructor(...atributtes) {
super()
if (atributtes.length === 0) {
this._builder = new Builder(this)
} else {
Object.assign(this, ...atributtes)
}
...
}
The else portion there skips initializing this._builder
- probably because you assume
it is of on of your Models and already has a _builder
. But in this case it's called
from my custom constructor.
I fixed adding _builder in the constructor of my base class, which
is of course not future-proof. It currently works for my use-cases.
// Added after the call to super()
this._builder = new Builder(this);
Q: Am I adding the custom constructor incorrectly? Is there an alternative
way to add properties to this
?
PS: The correct spelling is
attributes
, notatributtes
.
Is this intentional? Or is this an ES6 thing where attributes is a reserved word?
Maybe you are handling an unknown number of butts with atritis?
In that case it should be atriButts :)
Edit: Formatting
Hi!
How could I have a User object without fetching it from the API? I'm using NuxtJS's Auth Module that fetches the current user during the authentication process. I'd like to get all my logged in user's posts, but don't want to call the API for user again. Any idea?
Thanks.
Xavier
Today I updated to package version 1.4.1. Now, when I compile my code, I get:
These dependencies were not found:
* @babel/runtime/helpers/assertThisInitialized in ./node_modules/vue-api-query/build/Model.js
* @babel/runtime/helpers/classCallCheck in ./node_modules/vue-api-query/build/StaticModel.js, ./node_modules/vue-api-query/build/Parser.js and 2 others
* @babel/runtime/helpers/createClass in ./node_modules/vue-api-query/build/Model.js, ./node_modules/vue-api-query/build/StaticModel.js and 2 others
* @babel/runtime/helpers/getPrototypeOf in ./node_modules/vue-api-query/build/Model.js
* @babel/runtime/helpers/inherits in ./node_modules/vue-api-query/build/Model.js
* @babel/runtime/helpers/interopRequireDefault in ./node_modules/vue-api-query/build/index.js, ./node_modules/vue-api-query/build/StaticModel.js and 3 others
* @babel/runtime/helpers/possibleConstructorReturn in ./node_modules/vue-api-query/build/Model.js
* @babel/runtime/helpers/slicedToArray in ./node_modules/vue-api-query/build/Builder.js
To install them, you can run: npm install --save @babel/runtime/helpers/assertThisInitialized @babel/runtime/helpers/classCallCheck @babel/runtime/helpers/createClass @babel/runtime/helpers/getPrototypeOf @babel/runtime/helpers/inherits @babel/runtime/helpers/interopRequireDefault @babel/runtime/helpers/possibleConstructorReturn @babel/runtime/helpers/slicedToArray
I think, there are some dependencies are needed in the "dependencies" section, not "devDependencies".
So far I can reconstruct, the error starts with 1.3.0
I am trying to create a Repository class that receives a Model class in its constructor.
Is there any way to get the resource() string from a Model class?
How do you manage to use await? I'm always getting error Syntax Error: await is a reserved word.
I try to pass a Model to asyncData nuxt and pass it as props to a component.
In the component, I can't access to category.$save(). It seems that Nuxt is remove $https function of the Model. How can we do that?
<component :category="category"/>
data() {
return {
category: {},
}
},
async asyncData({ params}) {
const category = await Category.find(params.id)
return { category }
}
Do I really have to explicitly define base URL again in the base model definition?
My nuxt.js configuration already sets axios parameters like base url etc.
Thankx
Relating to #52 (I'm not allowed to reopen that ticket) I post it as new issue.
My Problem is, that new objects can not created over a relation. For me it looks like a wrong behaviour of vue-api-query. When I do
let childValues= {
val: 'test1',
sequence: 'test2'
};
await this.mainRecord
.childRecord(childValues)
.save()
.then(response => {
...
});
(which I think is the right syntax) I get a exception
Uncaught (in promise) TypeError: Converting circular structure to JSON
When I inspect the error, it is thrown in the axios library in the transformRequest method, line 50 (lib/defaults.js) by stringify method.
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
return JSON.stringify(data);
}
Inspecting with chrome shows me, the content of data is childRecord {_builder: Builder)
which looks to me its wrong. When I do a call with existing data, i see a json like formatted content. Same when I create directly a mainChild: data contains json.
In my sample on the top I would expect, data contains
{
val: 'test1',
sequence: 'test2'
}
but it isn't so somehow the given data is passed wrong.
My defined models:
MainRecord.js
import Model from '../../../Model'
import ChildRecord from './ChildRecord';
export default class Type extends Model {
primaryKey() {
return 'Id';
}
resource() {
return 'mainRecord'
}
ChildRecord() {
return this.hasMany(TypeValueList)
}
}
ChildRecord.js
import Model from '../../../Model'
export default class Type extends Model {
primaryKey() {
return 'Id';
}
resource() {
return 'childRecord'
}
}
// GET /posts/1
let post = await Post.find(1)
This wont let me chain include() function. Any particular reason why this would not work or be possible? I want to have possibility to include relations here or not.
Thank you for making a good module.
I have a question.
It is a dynamic address set in several words.
/post/category-a
, /post/category-b
, /post/category-c
...
'get()' was implemented using 'custom()'.
// GET /api/post/category-a
Post.custom(`/api/post/${categoryCode}`).get();
success
But, how do I 'save', 'delete', 'edit'?
// POST /api/post/category-a
const post = await Post.custom(`/api/post/${categoryCode}`).get();
post.title = 'text'
post.save();
// DELETE /api/post/category-a
// PUT /api/post/category-a
is not working..
Is there a solution?
how can handle validation errors on model.save()
from backend such as laravel
?
i think if this library has a utility for that, it's awesome
Is there currently a way to support the inverse one-to-many relationship?
Ex. Post belongs to User.
My current need is to extract the User from a given Post id.
In JSON API spec this is represented as
GET /posts/1/user
How filter (gt) great than or (lt) less than?
Like spatie/laravel-query-builder example.
$user = QueryBuilder::for($query)
->allowedIncludes('posts', 'permissions')
->where('score', '>', 42) **<< this**
->first();
Reading the code, this is not possible right now.
Updated:
Sure, we can create scoped filters, but, a lot of work for a Great/Less than only.
I have a global Axios error handler set up for all calls that use Axios. However, I want vue-api-query calls to handle their own errors. Normally I would handle turning off the global error handler for a specific call like this:
this.$http.get( "/user/1", { handleErrors: false } )
But I don't see a way to add arbitrary params to the calls, only the pre-defined params like "include", "fields", etc. Maybe I'm just missing something?
Thanks for any help!
One question. Do I need to use "promise" way to get response from Save()
method. Any reason why Save()
would not return API response? Instead of it it returns the model object itself.
Thanks
Hi, first of all, congratulations on the api.
I have a problem in the first test, it is generating without errors but when I execute the method below the error in the line of the message GET: [Vue Warn: Error in v-on handler: "TypeError: Can not read property 'get' of undefined"
methods: {
GetMunicipio() {
let municipios;
municipios
.get()
.then((response) => {
municipios = response;
});
console.log(municipios.nome);
},
How to add custom X-CSRF-TOKEN header to specific requests? I manged to add it when declaring base model, but I was not able to read and dynamically change the CSRF token value from the Vuex store or local storage. Is it possible to add custom headers when making specific request?
Hello, nice package.
One question I have, how can I call the following route? /cities/bucharest/
bucharest
beeing a slug in my table.
I want to query the api with the slug not with the id and the find
method won't work in this case. The custom
method could be a solution for this, but I'll have to do something like this City.custom('cities/' + slug).first()
and it's not that pretty
There could be other solution or should I just stick with the custom method approach?
The current readme informs about .custom()
, for use with GET requests: readme - Define your domain models
.custom()
is currently not meant to work outside of GET, is this correct? Using it for save()
as well is a use-case we would be interested in.
new Example({name:'example'}).custom('alternative/resource').save()
As of version 1.1.0, the above will send a POST request to /Example
instead of alternative/resource
.
Is this absolutely intended? Or could the .custom()
feature be made so .save()
respects it as well?
This could also be a workaround for #16
Line 258 in f9f4cec
BaseURL must be different from your client url in order to see this issue.
example would be:
Suggested fix:
base = this._customResource ? `${this.baseURL()}/${this._customResource}` : base
I could submit a PR if this would help to solve this issue asap.
Also thank you for this amazingly easy to use package.
Thanks for the nice package, but I think still we need to append some custom header with the request for example if we need to send form data with the request like binary files so we need to set the Content-Type header to multipart/form-data or even if we need some other custom headers like X-ApplicationId or etc.
It would be nicer if you add a function for this feature like bellow!
let user = new User(data, headers); user.save();
Thanks
I am trying to figure out what append does? I get the include, but not sure what append does. From the docs:
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()
I want to update the axios config per request and hopefully do something like this
User.get({
configProperty: configValue
}).then(response => {
this.users = response.data;
this.loadingUsers = false;
});
What can I do to achieve this?
when this.executeAfter, this.executeBefore, this.description are Null.
let schedule = await CampaignSchedule
.where('execute_after', this.executeAfter)
.where('execute_before', this.executeBefore)
.where('description', this.description)
.get()
this will generate a request as so
/campaign_schedule?filter[execute_after]=&filter[execute_before]=&filter[description]=
my situation will prefer to have such request, instead.
/campaign_schedule
the reason is filtering with arguments of nothing basically mean without filters.
the question is, is there a clean way to ignore the 'Where' filter, while the filter is Null or undefine (similar to how Axios implement params )?
thanks in advance
The method parameterNames
mentioned in the readme is ignored. I want to change the params to json:api specification (for use with https://github.com/spatie/laravel-json-api-paginate)
import { Model as BaseModel } from 'vue-api-query'
export default class Model extends BaseModel {
// define a base url for a REST API
baseURL () {
return this.$http.defaults.baseURL
}
// Overwrite parameter names because of json:api spec
parameterNames () {
return {
include: 'include',
filter: 'filter',
sort: 'sort',
fields: 'fields',
append: 'append',
page: 'page[number]',
limit: 'page[size]'
}
}
// implement a default request method
request (config) {
return this.$http.request(config)
}
}
Tried to console.log inside parameterNames
and it never gets called (baseUrl
and request
get called)
see https://github.com/spatie/laravel-query-builder#selecting-specific-columns
maybe with api like this
.include('relation', [columns])
or if you dont want to change your current methods maybe smth like this:
.onlyColumns('relation', [columns])
would be a very useful addition ๐
Maybe I misunderstand something, but I get Can not use keyword 'await' outside an async function
error when using this code (I am using it inside a quasar-framework installation):
....
import Project from '../../models/Project'
export default {
mounted() {
let project = await Project.find(1);
}
}
...
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://api.simplefin.local/v1'
}
// implement a default request method
request (config) {
return this.$http.request(config)
}
}
import Model from './Model'
export default class User extends Model {
resource()
{
return 'users'
}
}
import { Model } from 'vue-api-query'
import axios from 'axios'
export default async ({ app, router, Vue }) => {
Model.$http = axios
}
Hi robsontenorio , thanks for your library , I've been using it in a new project . I have a problem when I save a model, how can I get status=200 or success for doing redirections, I'm using laravel-query-builder in my backend.
async sendData(){ let proveedor= await ProveedorModel.find(this.rutProvider) proveedor.razon_social =this.nameProvider proveedor.save() }
I would like to redirect when save is successfully .
Any plans to integrate a vuex store into this library? I.e. once results are retrieved form the API call, it auto saves it to a vuex store?
Hi, great package really good ๐ Came across something that may be worth supporting.
The https://github.com/spatie/laravel-query-builder package has an alternative way of setting up the pagination where you install https://github.com/spatie/laravel-json-api-paginate and the pagination adheres to the JSON API spec.
The pagination supported in this plugin won't handle this out of the box, you can get around it by providing the custom params.
params({ 'page[number]': page, 'page[limit]': limit })
Ideally there would be a toggle to switch to this mode or some way to globally customize the param names used by the Parser to handle it.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.