Skip to content

Commit 42ed771

Browse files
committed
simple queries
1 parent ed8c14e commit 42ed771

File tree

10 files changed

+793
-266
lines changed

10 files changed

+793
-266
lines changed

dist/vuex-orm-apollo.esm.js

Lines changed: 497 additions & 207 deletions
Large diffs are not rendered by default.

docs/.vuepress/config.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ themeConfig:
2626
- /guide/push/
2727
- /guide/destroy/
2828
- /guide/custom-queries/
29-
- /guide/custom-mutations/
3029
- /guide/relationships/
3130
- /guide/eager-loading/
3231
- /guide/virtual-fields/

docs/guide/custom-mutations/README.md

Lines changed: 0 additions & 55 deletions
This file was deleted.

docs/guide/custom-queries/README.md

Lines changed: 139 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
1-
# Custom Queries
1+
# Custom Queries and Mutations
22

33
[[toc]]
44

5+
With custom mutations and custom queries we distinguish between model related and model unrelated (simple) custom
6+
quries/mutations. The difference is that model related queries/mutations always are tied to a model, so Vuex-ORM-Apollo
7+
expected that the query/mutation type is the same as the model. A model related custom mutation `upvotePost` is expected
8+
to be of type `Post`. To make this even clearer, all model related queries and mutations are called on a specific Model
9+
or a record of this model.
10+
11+
A simple query or simple mutation is not tied to a model. And so Vuex-ORM-Apollo doesn't expect the result to be of a
12+
specific type. Also the return value is not automatically inserted in the Vuex store.
13+
14+
15+
::: warning
16+
It's not a clean and good solution that the simple queries are also triggered via Vuex action, but currently the only
17+
way. This might be changed in the future, when we find a better solution.
18+
:::
19+
20+
21+
## Model related custom query
522

623
You may sometimes want to send custom GraphQL query. We support this via the `query` action. However please notice that
724
the convenienceMethods here are named `customMutation` and `$customMutation` due to a name conflict with the `query()`
@@ -23,8 +40,8 @@ the post id, but this could be anything else. Please also notice that `record.$c
2340
of the record into the arguments list. The plugin automatically determines if there are multiple records or a single
2441
record is requests by looking in the arguments hash if there is a `id` field and respectively setups the query.
2542

26-
A custom query is always tied to the model, so the plugin expects the return value of the custom query is of the model
27-
type. In this example that means, that Vuex-ORM-Apollo expects that the `example` query is of type `Post`.
43+
A model related custom query is always tied to the model, so the plugin expects the return value of the custom query
44+
is of the model type. In this example that means, that Vuex-ORM-Apollo expects that the `example` query is of type `Post`.
2845

2946
This generates the following query:
3047

@@ -55,3 +72,122 @@ Variables:
5572

5673
Like for all other operations, all records which are returned replace the respective existing records in the Vuex-ORM
5774
database.
75+
76+
77+
## Model unrelated simple query
78+
79+
There might be cases when you just want to send a plan graphql query without having this plugin doing magic things.
80+
81+
Simple Queries allow to do that. Let's assume we do have a `status` query in our GraphQL API which let ask for the
82+
status of all subsystems:
83+
84+
```javascript
85+
const query = `
86+
query status {
87+
backend
88+
smsGateway
89+
paypalIntegration
90+
}`;
91+
92+
const result = store.dispatch('entities/simpleQuery', { query, variables: {}, bypassCache: true });
93+
```
94+
95+
The result contains a hash which is shaped like the request:
96+
97+
```javascript
98+
{
99+
backend: true,
100+
smsGateway: false,
101+
paypalIntegration: true
102+
}
103+
```
104+
105+
Nothing is inserted in the Vuex store.
106+
107+
108+
## Model related custom mutation
109+
110+
Along with the CRUD mutations you may want to send custom GraphQL mutations. We support this via the `mutate` action:
111+
112+
```javascript
113+
const post = Post.query().first();
114+
await post.$mutate({ mutation: 'upvotePost' });
115+
116+
// is the same as
117+
await Post.mutate({ mutation: 'upvotePost', id: post.id });
118+
119+
// or
120+
await Post.dispatch('mutate', { mutation: 'upvotePost', id: post.id });
121+
```
122+
123+
As you can see you have to provide the mutation name and any further arguments you want to pass. In this case we send
124+
the post id, but this could be anything else. Please also notice that `record.$mutate` automatically adds the id
125+
of the record into the arguments list. The plugin automatically determines if there are multiple records or a single
126+
record is requests by looking in the arguments hash if there is a `id` field and respectively setups the query.
127+
128+
A model related custom mutation is always tied to the model, so the plugin expects the return value of the custom query
129+
is of the model type. In this example that means, that Vuex-ORM-Apollo expects that the `upvotePost` mutation is of type
130+
`Post`.
131+
132+
This generates the following query:
133+
134+
135+
```graphql
136+
mutation UpvotePost($id: ID!) {
137+
upvotePost(post: $id) {
138+
id
139+
userId
140+
content
141+
title
142+
143+
user {
144+
id
145+
email
146+
}
147+
}
148+
}
149+
```
150+
151+
Variables:
152+
153+
```json
154+
{
155+
"id": 42
156+
}
157+
```
158+
159+
Like for all other operations, all records which are returned replace the respective existing records in the Vuex-ORM
160+
database.
161+
162+
163+
## Model unrelated simple mutation
164+
165+
Like simple custom queries, you can also send simple custom mutations. The action (`simpleQuery`) stays the same.
166+
Let's assume we do have a `sendSms` mutation (this is a bad example, never setup your app like this please!) in our
167+
GraphQL API which let us send a SMS.
168+
169+
```javascript
170+
const query = `
171+
mutation SendSms($to: string!, $text: string!) {
172+
sendSms(to: $to, text: $text) {
173+
delivered
174+
}
175+
}`;
176+
177+
const result = store.dispatch('entities/simpleQuery', {
178+
query,
179+
variables: { to: '+4912345678', text: 'GraphQL is awesome!' }
180+
});
181+
```
182+
183+
The result contains a hash which is shaped like the request:
184+
185+
```javascript
186+
{
187+
sendSms: {
188+
delivered: true
189+
}
190+
}
191+
```
192+
193+
Nothing is inserted in the Vuex store.

src/actions/simple-mutation.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { ActionParams } from '../support/interfaces';
2+
import Action from './action';
3+
import NameGenerator from '../graphql/name-generator';
4+
import Context from '../common/context';
5+
6+
/**
7+
* SimpleMutation action for sending a model unrelated simple mutation.
8+
*/
9+
export default class SimpleMutation extends Action {
10+
/**
11+
* @param {DispatchFunction} dispatch Vuex Dispatch method for the model
12+
* @param {string} query The query to send
13+
* @param {Arguments} variables
14+
* @returns {Promise<any>} The result
15+
*/
16+
public static async call ({ dispatch }: ActionParams, { query, variables }: ActionParams): Promise<any> {
17+
if (query) {
18+
variables = this.prepareArgs(variables);
19+
const result = await Context.getInstance().apollo.simpleMutation(query, variables);
20+
return result.data;
21+
} else {
22+
throw new Error("The simpleMutation action requires the 'query' to be set");
23+
}
24+
}
25+
}

src/actions/simple-query.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { ActionParams } from '../support/interfaces';
2+
import Action from './action';
3+
import NameGenerator from '../graphql/name-generator';
4+
import Context from '../common/context';
5+
6+
/**
7+
* SimpleQuery action for sending a model unrelated simple query.
8+
*/
9+
export default class SimpleQuery extends Action {
10+
/**
11+
* @param {DispatchFunction} dispatch Vuex Dispatch method for the model
12+
* @param {string} query The query to send
13+
* @param {Arguments} variables
14+
* @param {boolean} bypassCache Whether to bypass the cache
15+
* @returns {Promise<any>} The result
16+
*/
17+
public static async call ({ dispatch }: ActionParams, { query, bypassCache, variables }: ActionParams): Promise<any> {
18+
if (query) {
19+
variables = this.prepareArgs(variables);
20+
const result = await Context.getInstance().apollo.simpleQuery(query, variables, bypassCache);
21+
return result.data;
22+
} else {
23+
throw new Error("The simpleQuery action requires the 'query' to be set");
24+
}
25+
}
26+
}

src/graphql/apollo.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Context from '../common/context';
55
import { Arguments, Data } from '../support/interfaces';
66
import Transformer from './transformer';
77
import Model from '../orm/model';
8+
import gql from 'graphql-tag';
89

910
/**
1011
* This class takes care of the communication with the graphql endpoint by leveraging the awesome apollo-client lib.
@@ -67,4 +68,13 @@ export default class Apollo {
6768
// Transform incoming data into something useful
6869
return Transformer.transformIncomingData(response.data as Data, model, mutation);
6970
}
71+
72+
public async simpleQuery (query: string, variables: Arguments, bypassCache: boolean = false): Promise<any> {
73+
const fetchPolicy: FetchPolicy = bypassCache ? 'network-only' : 'cache-first';
74+
return this.apolloClient.query({ query: gql(query), variables, fetchPolicy });
75+
}
76+
77+
public async simpleMutation (query: string, variables: Arguments): Promise<any> {
78+
return this.apolloClient.mutate({ mutation: gql(query), variables });
79+
}
7080
}

src/support/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface ActionParams {
2424
id?: number;
2525
data?: Data;
2626
args?: Arguments;
27+
variables?: Arguments;
2728
bypassCache?: boolean;
2829
query?: string;
2930
}

src/vuex-orm-apollo.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import Context from './common/context';
33
import { Components } from '@vuex-orm/core/lib/plugins/use';
44
import { Destroy, Fetch, Mutate, Persist, Push } from './actions';
55
import Query from './actions/query';
6+
import SimpleQuery from './actions/simple-query';
7+
import SimpleMutation from './actions/simple-mutation';
68

79
/**
810
* Main class of the plugin. Setups the internal context, Vuex actions and model methods
@@ -25,6 +27,9 @@ export default class VuexORMApollo {
2527
private static setupActions () {
2628
const context = Context.getInstance();
2729

30+
context.components.rootActions.simpleQuery = SimpleQuery.call.bind(SimpleQuery);
31+
context.components.rootActions.simpleMutation = SimpleMutation.call.bind(SimpleMutation);
32+
2833
context.components.subActions.fetch = Fetch.call.bind(Fetch);
2934
context.components.subActions.persist = Persist.call.bind(Persist);
3035
context.components.subActions.push = Push.call.bind(Push);

0 commit comments

Comments
 (0)