Skip to content

Commit 0a0c5ce

Browse files
authored
Merge pull request #26 from vuex-orm/feature/24-schema
Feature/24 schema
2 parents 8691357 + abdc734 commit 0a0c5ce

26 files changed

+3868
-431
lines changed

dist/vuex-orm-graphql.esm.js

Lines changed: 484 additions & 246 deletions
Large diffs are not rendered by default.

docs/guide/README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@ Vuex-ORM-GraphQL is a plugin for the amazing [Vuex-ORM](https://github.com/vuex-
44
Object-Relational Mapping access to the Vuex Store. Vuex-ORM-GraphQL enhances Vuex-ORM to let you sync your Vuex state
55
via the Vuex-ORM models with your server via a [GraphQL API](http://graphql.org/).
66

7-
The plugin will automatically generate GraphQL queries and mutations based on your model definitions and thus hides
8-
the specifics of Network Communication, GraphQL, Caching, De- and Serialization of your Data and so on from the
9-
developer. Getting a record of a model from the server is as easy as calling `Product.fetch()`. This allows you to write
10-
sophisticated Single-Page Applications fast and efficient without worrying about GraphQL.
7+
The plugin will automatically generate GraphQL queries and mutations based on your model definitions and by
8+
reading your and GraphQL schema from your server. Thus it hides the specifics of Network Communication, GraphQL,
9+
Caching, De- and Serialization of your Data and so on from the developer. Getting a record of a model from the server
10+
is as easy as calling `Product.fetch()`. This allows you to write sophisticated Single-Page Applications fast and
11+
efficient without worrying about GraphQL.
1112

1213

1314
::: warning
14-
You should have basic knowledge of [Vue](https://vuejs.org/), [Vuex](https://vuex.vuejs.org/) and
15-
[Vuex-ORM](https://vuex-orm.github.io/vuex-orm/) before reading this documentation.
15+
You should have basic knowledge of [GraphQL](http://graphql.org/), [Vue](https://vuejs.org/),
16+
[Vuex](https://vuex.vuejs.org/) and [Vuex-ORM](https://vuex-orm.github.io/vuex-orm/) before reading this documentation.
1617
:::
1718

1819

docs/guide/custom-queries/README.md

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ await Post.dispatch('query', { name: 'example', filter: { id: post.id } });
3838
As you can see you have to provide the query name and any further arguments you want to pass. In this case we send
3939
the post id, but this could be anything else. Please also notice that `record.$customQuery` automatically adds the id
4040
of the record into the arguments list. The plugin automatically determines if there are multiple records or a single
41-
record is requests by looking in the arguments hash if there is a `id` field and respectively setups the query.
41+
record is returned by looking in the arguments hash if there is a `id` field and respectively setups the query.
4242

4343
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-GraphQL expects that the `example` query is of type `Post`.
44+
is of the model type. In this example that means, that Vuex-ORM-GraphQL expects that the `example` query is of type
45+
`Post`.
4546

4647
This generates the following query:
4748

@@ -76,7 +77,6 @@ database.
7677
Following fields are allowed:
7778

7879
- `name`: Required. The name of the query.
79-
- `multiple`: Whether the query is of a connection type (multiple records are returned) or not (single record is returned).
8080
- `filter`: Hash map with filters. These are passed as a filter typed variable like in fetch.
8181
- `bypassCache`: Whether to bypass the caching.
8282

@@ -176,7 +176,6 @@ database.
176176
Following fields are allowed:
177177

178178
- `name`: Required. The name of the mutation.
179-
- `multiple`: Whether the mutation is of a connection type (multiple records are returned) or not (single record is returned).
180179
- `args`: Hash map with arguments (variables).
181180

182181

@@ -220,14 +219,5 @@ Following fields are allowed:
220219

221220
## Multiple or single record
222221

223-
Vuex-ORM-GraphQL tries to determine automatically if a single record or a connection (multiple records) is returned by
224-
a query/mutation via checking if a `id` field is set in the filter/args/variables. But sometimes you have a
225-
query/mutation without ID but it still returns a single record or vice versa. For this case you can manually set the
226-
`multiple` field to tell the plugin how the result is shaped:
227-
228-
```javascript
229-
await Post.customQuery({ name: 'example', multiple: true, filter: { id: post.id } });
230-
```
231-
232-
In future versions of this project this might be obsolete because it could consume and analyze the schema to know
233-
how the queries and mutations are shaped.
222+
Vuex-ORM-GraphQL will determine automatically if a single record or a connection (multiple records) is returned by a
223+
query/mutation via checking your GraphQL Schema. How smart is this?

docs/guide/destroy/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ the following GraphQL query is generated:
2323
mutation DeletePost($id: ID!) {
2424
deletePost(id: $id) {
2525
id
26+
title
27+
content
28+
29+
user {
30+
id
31+
email
32+
}
2633
}
2734
}
2835
```

docs/guide/faq/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,23 @@
22

33
[[toc]]
44

5+
## WTF?
6+
7+
Good question, glad you asked! Maybe [this article](https://dev.to/phortx/vue-3-graphql-kj6) helps you. If not, feel
8+
free to join us on our [Slack Channel](https://join.slack.com/t/vuex-orm/shared_invite/enQtMzY5MzczMzI2OTgyLTAwOWEwOGRjOTFmMzZlYzdmZTJhZGU2NGFiY2U2NzBjOWE4Y2FiMWJkMjYxMTAzZDk0ZjAxNTgzZjZhY2VkZDQ)
9+
for any questions and discussions.
10+
11+
12+
## Does Vuex-ORM-GraphQL know my GraphQL Schema?
13+
14+
Yes, it does! Before the first query is sent, the plugin loads the schema from the GraphQL server and extracts different
15+
information like the types of the fields to use, which fields to ignore, because they're not in the schema and whether
16+
custom queries and mutations return a connection or a record.
17+
18+
Further it detects differences between the Vuex-ORM model definitions and the schema.
19+
20+
In the future there will probably be more smart consulting of your GraphQL schema, we're open for suggestions.
21+
522

623
## Is this plugin nativescript-vue compatible?
724

docs/guide/virtual-fields/README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,14 @@
55
It may happen that you want fields in your Vuex-ORM model, which are not in the respective GraphQL Type.
66
We call these "virtual fields" because they are only known to the Vuex-ORM and not to your backend or database.
77

8-
This plugin will automatically query all fields of the model, but when your GraphQL API doesn't know a field, it returns
9-
an error. So we have to prevent the querying of our virtual fields. For that we have the `skipFields` field in our model:
8+
This plugin will automatically detect which fields are not included in the schema and will not query them at all.
109

10+
Let's assume we have a product model with the field `parsedMarkdownContent` which is not known to our GraphQL server:
1111

1212
```javascript{4}
1313
export default class Product extends Model {
1414
static entity = 'products';
1515
16-
static skipFields = ['parsedMarkdownContent'];
17-
1816
static fields () {
1917
return {
2018
id: this.increment(),
@@ -40,4 +38,4 @@ query Posts() {
4038
}
4139
```
4240

43-
As you see the `parsedMarkdownContent` field is not queried.
41+
As you see the `parsedMarkdownContent` field is not queried due to the fact that it's not in the GraphQL Schema.

src/actions/action.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Model from '../orm/model';
66
import State from '@vuex-orm/core/lib/modules/State';
77
import Transformer from '../graphql/transformer';
88
import NameGenerator from '../graphql/name-generator';
9+
import Schema from '../graphql/schema';
910

1011
const inflection = require('inflection');
1112

@@ -24,8 +25,12 @@ export default class Action {
2425
* @returns {Promise<any>}
2526
*/
2627
protected static async mutation (name: string, variables: Data | undefined, dispatch: DispatchFunction,
27-
model: Model, multiple: boolean = false): Promise<any> {
28+
model: Model): Promise<any> {
2829
if (variables) {
30+
const context: Context = Context.getInstance();
31+
const schema: Schema = await context.loadSchema();
32+
33+
const multiple: boolean = schema.returnsConnection(schema.getMutation(name));
2934
const query = QueryBuilder.buildQuery('mutation', model, name, variables, multiple);
3035

3136
// Send GraphQL Mutation

src/actions/fetch.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export default class Fetch extends Action {
1818
*/
1919
public static async call ({ state, dispatch }: ActionParams, params?: ActionParams): Promise<Data> {
2020
const context = Context.getInstance();
21+
await context.loadSchema();
22+
2123
const model = this.getModelFromState(state);
2224

2325
// Filter

src/actions/mutate.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { ActionParams, Arguments, Data } from '../support/interfaces';
22
import Action from './action';
3+
import Context from '../common/context';
4+
import Schema from '../graphql/schema';
35

46
/**
57
* Mutate action for sending a custom mutation. Will be used for Model.mutate() and record.$mutate().
@@ -13,19 +15,19 @@ export default class Mutate extends Action {
1315
* @param {Arguments} args Arguments for the mutation. Must contain a 'mutation' field.
1416
* @returns {Promise<Data>} The new record if any
1517
*/
16-
public static async call ({ state, dispatch }: ActionParams, { args, multiple, name }: ActionParams): Promise<Data> {
18+
public static async call ({ state, dispatch }: ActionParams, { args, name }: ActionParams): Promise<Data> {
1719
if (name) {
20+
const context: Context = Context.getInstance();
21+
const schema: Schema = await context.loadSchema();
1822
const model = this.getModelFromState(state);
1923
args = this.prepareArgs(args);
2024

2125
// There could be anything in the args, but we have to be sure that all records are gone through
2226
// transformOutgoingData()
2327
this.transformArgs(args);
2428

25-
if (multiple === undefined) multiple = !args['id'];
26-
2729
// Send the mutation
28-
return Action.mutation(name, args, dispatch, model, multiple);
30+
return Action.mutation(name, args, dispatch, model);
2931
} else {
3032
throw new Error("The mutate action requires the mutation name ('mutation') to be set");
3133
}

src/actions/query.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Transformer from '../graphql/transformer';
55
import { ActionParams, Data } from '../support/interfaces';
66
import Action from './action';
77
import NameGenerator from '../graphql/name-generator';
8+
import Schema from '../graphql/schema';
89

910
/**
1011
* Query action for sending a custom query. Will be used for Model.customQuery() and record.$customQuery.
@@ -20,15 +21,17 @@ export default class Query extends Action {
2021
* @returns {Promise<Data>} The fetched records as hash
2122
*/
2223
public static async call ({ state, dispatch }: ActionParams,
23-
{ name, multiple, filter, bypassCache }: ActionParams): Promise<Data> {
24+
{ name, filter, bypassCache }: ActionParams): Promise<Data> {
2425
if (name) {
25-
const context = Context.getInstance();
26+
const context: Context = Context.getInstance();
27+
const schema: Schema = await context.loadSchema();
2628
const model = this.getModelFromState(state);
2729

2830
// Filter
2931
filter = filter ? Transformer.transformOutgoingData(model, filter) : {};
3032

31-
if (multiple === undefined) multiple = !filter['id'];
33+
// Multiple?
34+
const multiple: boolean = schema.returnsConnection(schema.getQuery(name));
3235

3336
// Build query
3437
const query = QueryBuilder.buildQuery('query', model, name, filter, multiple, false);

0 commit comments

Comments
 (0)