From 3c33d8c3fa46d08e73ed8e8d363635ede67728ba Mon Sep 17 00:00:00 2001 From: Guillaume Chau Date: Tue, 20 Sep 2016 00:50:22 +0200 Subject: [PATCH] API update, Readme --- README.md | 215 +++++++++++++++++++++++++++++----------------- lib/vue-plugin.js | 16 ++-- package.json | 6 +- 3 files changed, 148 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index bfcaac93..2dbac468 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,12 @@ Integrates [apollo](http://www.apollostack.com/) in your vue components with declarative queries. +[See a simple Apollo server example](https://github.com/Akryum/apollo-server-example) + ## Installation - npm install vue-apollo + npm install --save vue-apollo apollo-client ## Usage @@ -13,11 +15,15 @@ Integrates [apollo](http://www.apollostack.com/) in your vue components with dec ```javascript import Vue from 'vue'; -import ApolloClient from 'apollo-client'; -import VueApollo from 'vue-apollo' +import ApolloClient, { createNetworkInterface, addTypename } from './apollo-client'; +import VueApollo from 'vue-apollo'; const apolloClient = new ApolloClient({ - /* ... */ + networkInterface: createNetworkInterface({ + uri: 'http://localhost:8080/graphql', + transportBatching: true, + }), + queryTransformer: addTypename, }); Vue.use(VueApollo, { @@ -33,28 +39,34 @@ To declare apollo queries in your Vue component, add an `apollo` object : new Vue({ apollo: { // Apollo specific options - } + }, }); ``` -You can access the [apollo-client](http://docs.apollostack.com/apollo-client/index.html) instance with `this.$apollo.client` in all your vue components. +You can access the [apollo-client](http://dev.apollodata.com/core/apollo-client-api.html) instance with `this.$apollo.client` in all your vue components. ### Queries -In the `data` object, add an attribute for each property you want to feed with the result of an Apollo query. +In the `query` object, add an attribute for each property you want to feed with the result of an Apollo query. #### Simple query +Use `gql` to write your GraphQL queries: + +```javascript +import gql from 'graphql-tag'; +``` + Put the [gql](http://docs.apollostack.com/apollo-client/core.html#gql) query directly as the value: ```javascript apollo: { // Non-reactive query - data: { + query: { // Simple query that will update the 'hello' vue property - hello: gql`{hello}` - } -} + hello: gql`{hello}`, + }, +}, ``` Don't forget to initialize your property in your vue component: @@ -63,9 +75,18 @@ Don't forget to initialize your property in your vue component: data () { return { // Initialize your apollo data - hello: '' - } -} + hello: '', + }, +}, +``` + +Or with the `ES2015` syntax: + +```javascript +data: () => ({ + // Initialize your apollo data + hello: '', +}), ``` Server-side, add the corresponding schema and resolver: @@ -85,12 +106,12 @@ export const resolvers = { Query: { hello(root, args, context) { return "Hello world!"; - } - } + }, + }, }; ``` -For more info, visit the [apollo doc](http://docs.apollostack.com/apollo-server/index.html). +For more info, visit the [apollo doc](http://dev.apollodata.com/tools/). You can then use your property as usual in your vue component: @@ -113,7 +134,7 @@ You can add variables (read parameters) to your `gql` query by declaring `query` // Apollo-specific options apollo: { // Non-reactive query - data: { + query: { // Query with parameters ping: { // gql query @@ -122,24 +143,25 @@ apollo: { }`, // Static parameters variables: { - message: 'Meow' - } - } - } -} + message: 'Meow', + }, + }, + }, +}, ``` -You can use the following apollo options in the object: +You can use the apollo options in the object, like: - `forceFetch` - `fragments` + - ... -See the [apollo doc](http://docs.apollostack.com/apollo-client/queries.html#query) for more details. +See the [apollo doc](http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient\.query) for more details. For example, you could add the `forceFetch` apollo option like this: ```javascript apollo: { - data: { + query: { // Query with parameters ping: { query: gql`query PingMessage($message: String!) { @@ -149,10 +171,10 @@ apollo: { message: 'Meow' }, // Additional options here - forceFetch: true - } - } -} + forceFetch: true, + }, + }, +}, ``` Don't forget to initialize your property in your vue component: @@ -161,9 +183,18 @@ Don't forget to initialize your property in your vue component: data () { return { // Initialize your apollo data - ping: '' - } -} + ping: '', + }; +}, +``` + +Or with the `ES2015` syntax: + +```javascript +data: () => ({ + // Initialize your apollo data + ping: '', +}), ``` Server-side, add the corresponding schema and resolver: @@ -183,8 +214,8 @@ export const resolvers = { Query: { ping(root, { message }, context) { return `Answering ${message}`; - } - } + }, + }, }; ``` @@ -209,7 +240,7 @@ Use a function instead to make the parameters reactive with vue properties: // Apollo-specific options apollo: { // Non-reactive query - data: { + query: { // Query with parameters ping: { query: gql`query PingMessage($message: String!) { @@ -219,12 +250,12 @@ apollo: { variables() { // Use vue reactive properties here return { - message: this.pingInput - } - } - } - } -} + message: this.pingInput, + }; + }, + }, + }, +}, ``` This will re-fetch the query each time a parameter changes, for example: @@ -255,7 +286,7 @@ These are the available advanced options you can use: // Apollo-specific options apollo: { // Non-reactive query - data: { + query: { // Advanced query with parameters // The 'variables' method is watched by vue pingMessage: { @@ -266,8 +297,8 @@ apollo: { variables() { // Use vue reactive properties here return { - message: this.pingInput - } + message: this.pingInput, + }; }, // We use a custom update callback because // the field names don't match @@ -298,13 +329,13 @@ apollo: { watchLoading(isLoading, countModifier) { // isLoading is a boolean // countModifier is either 1 or -1 - } - } - } -} + }, + }, + }, +}, ``` -If you use ES2015, you can also write the `update` like this: +If you use `ES2015`, you can also write the `update` like this: ```javascript update: data => data.ping @@ -312,29 +343,27 @@ update: data => data.ping ### Reactive Queries -*For now, the reactivity in apollo is quite limited, since you can only do polling.* +For more info, see the [apollo doc](http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient\.watchQuery). -For more info, see the [apollo doc](http://docs.apollostack.com/apollo-client/core.html#watchQuery). - -Add your queries in a `watch` object instead of `data`: +Add your queries in a `watchQuery` object instead of `data`: ```javascript // Apollo-specific options apollo: { // Reactive query - watch: { + watchQuery: { // 'tags' data property on vue instance tags: { - query: gql`{ + query: gql`query tagList { tags { id, label } }`, - pollInterval: 300 // ms - } - } -} + pollInterval: 300, // ms + }, + }, +}, ``` You can use the apollo options, for example: @@ -342,8 +371,9 @@ You can use the apollo options, for example: - `returnPartialData` - `pollInterval` - `fragments` + - ... -See the [apollo doc](http://docs.apollostack.com/apollo-client/queries.html#watchQuery) for more details. +See the [apollo doc](http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient\.watchQuery) for more details. You can also use the advanced options detailed above, like `result` or `watchLoading`. @@ -378,7 +408,7 @@ for (let i = 0; i < 42; i++) { function addTag(label) { let t = { id: id++, - label + label, }; tags.push(t); return t; @@ -388,41 +418,70 @@ export const resolvers = { Query: { tags(root, args, context) { return tags; - } - } + }, + }, }; ``` ### Mutations -Mutations are queries that changes your data state on your apollo server. For more info, visit the [apollo doc](http://docs.apollostack.com/apollo-client/core.html#Mutations). +Mutations are queries that changes your data state on your apollo server. For more info, visit the [apollo doc](http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient\.mutate). ```javascript methods: { addTag() { - // Mutate the tags data - // You can also use this.$apollo.client.mutate + // We save the user input in case of an error + const newTag = this.newTag; + // We clear it early to give the UI a snappy feel + this.newTag = ''; + // Call to the graphql mutation this.$apollo.mutate({ - mutation: gql`mutation AddTag($label: String!) { + // Query + mutation: gql`mutation ($label: String!) { addTag(label: $label) { - id, + id label } }`, // Parameters variables: { - label: this.tagLabel - } + label: newTag, + }, + // Update the cache with the result + // 'tagList' is the name of the query declared before + // that will be updated with the optimistic response + // and the result of the mutation + updateQueries: { + tagList: (previousQueryResult, { mutationResult }) => { + // We incorporate any received result (either optimistic or real) + // into the 'tagList' query we set up earlier + return { + tags: [...previousQueryResult.tags, mutationResult.data.addTag], + }; + }, + }, + // Optimistic UI + // Will be treated as a 'fake' result as soon as the request is made + // so that the UI can react quickly and the user be happy + optimisticResponse: { + __typename: 'Mutation', + addTag: { + __typename: 'Tag', + id: -1, + label: newTag, + }, + }, }).then((data) => { // Result console.log(data); - this.tagLabel = ''; }).catch((error) => { // Error console.error(error); + // We restore the initial user input + this.newTag = newTag; }); - } -} + }, +}, ``` Server-side: @@ -461,7 +520,7 @@ for (let i = 0; i < 42; i++) { function addTag(label) { let t = { id: id++, - label + label, }; tags.push(t); return t; @@ -471,14 +530,14 @@ export const resolvers = { Query: { tags(root, args, context) { return tags; - } + }, }, Mutation: { addTag(root, { label }, context) { console.log(`adding tag '${label}'`); return addTag(label); - } - } + }, + }, }; ``` diff --git a/lib/vue-plugin.js b/lib/vue-plugin.js index a0487040..9432c71e 100644 --- a/lib/vue-plugin.js +++ b/lib/vue-plugin.js @@ -1,4 +1,4 @@ -import _ from 'lodash'; +import omit from 'lodash.omit'; let apolloClient = null; @@ -59,7 +59,7 @@ class DollarApollo { } function generateApolloOptions(variables) { - const apolloOptions = _.omit(options, [ + const apolloOptions = omit(options, [ 'variables', 'watch', 'update', @@ -187,16 +187,16 @@ function prepare() { if (apollo) { // One-time queries with $query(), called each time a Vue dependency is updated (using $watch) - if (apollo.data) { - for (let key in apollo.data) { - this.$apollo.option(key, apollo.data[key], false); + if (apollo.query) { + for (let key in apollo.query) { + this.$apollo.option(key, apollo.query[key], false); } } // Auto updating queries with $watchQuery(), re-called each time a Vue dependency is updated (using $watch) - if (apollo.watch) { - for (let key in apollo.watch) { - this.$apollo.option(key, apollo.watch[key], true); + if (apollo.watchQuery) { + for (let key in apollo.watchQuery) { + this.$apollo.option(key, apollo.watchQuery[key], true); } } } diff --git a/package.json b/package.json index 1e764615..21fa0e21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-apollo", - "version": "1.0.0", + "version": "1.0.0-beta1", "description": "Vue apollo integration", "main": "index.js", "scripts": { @@ -21,7 +21,7 @@ "url": "https://github.com/Akryum/vue-apollo/issues" }, "homepage": "https://github.com/Akryum/vue-apollo#readme", - "dependencies": { - "lodash": "^4.15.0" + "peerDependencies": { + "apollo-client": "^0.4.15" } }