vuex-orm / plugin-graphql

Vuex ORM persistence plugin to sync the store against a GraphQL API.
https://vuex-orm.github.io/plugin-graphql/
MIT License
227 stars 52 forks source link

AWS Appsync support? #86

Closed Alonitor closed 5 years ago

Alonitor commented 5 years ago

Is it possible to use this with AWS Appsync?

phortx commented 5 years ago

I never worked with AppSync. What do you need to make it work?

toadkicker commented 5 years ago

Here's what I came up with for AppSync using an API key. I plan on switching to Cognito user pools in the coming week. I suspect from this thread that it will be much simpler than I expected.

store/index:

import VuexORM from '@vuex-orm/core'
import VuexORMGraphQL from '@vuex-orm/plugin-graphql'
import database from '../database'
import awsmobile from '../aws-exports'

const options = {
  connectionQueryMode: 'nodes',
  debug: process.env.NODE_ENV !== 'production',
  database: database,
  url: awsmobile.aws_appsync_graphqlEndpoint,
  headers: {
    'x-api-key': awsmobile.aws_appsync_apiKey
  }
}

VuexORM.use(VuexORMGraphQL, options)

export const plugins = [
  VuexORM.install(database)
]
cameroncf commented 5 years ago

I got it working with AppSync but quickly ran into a limitation that vuex-orm (at the time) did not fully support UUID (strings) as primary keys. That was a few months ago though, so that may be different now - I haven't looked at recent updates. Might be looking into before you spend too much time on it though.

Alonitor commented 5 years ago

Hi and thank you for your responses! Im learning Vue and building/porting an app/web that I want to build with Vue/vuetify and use AWS Amplify for the back end as much as possible. It's all a proof of concept and learning project so far. I read up on Vuex, Vuex Orm and this GraphQL plugin, and this all sounds very sensible ting to implement in a datadriven Vue app. I was thinking since AWS Amplify uses AWS Appsync for it GraphQL API, and as far as I could read, Appsync GraphQL is based on/made by Appollo and that these where compatible, that this plugin should work out of the box?

@toadkicker : Thank you for this! I tried your setup, but it does not seem to get anything from Appsync. I cannot see any requests going to Appsync? This is my store.js file:

import Vue from 'vue'
import Vuex from 'vuex'
import VuexORM from "@vuex-orm/core";
import VuexORMGraphQL from "@vuex-orm/plugin-graphql";
import awsmobile from './aws-exports'

/* eslint-disable no-console */
Vue.use(Vuex)

const database = new VuexORM.Database()

const options = {
    connectionQueryMode: 'nodes',
    debug: process.env.NODE_ENV !== 'production',
    database: database,
    url: awsmobile.aws_appsync_graphqlEndpoint,
    headers: {
        'x-api-key': awsmobile.aws_appsync_apiKey
    }
}

VuexORM.use(VuexORMGraphQL, options);

export const store = new Vuex.Store({
  plugins: [VuexORM.install(database)]
})

//export default store

console.log(store.state.entities)
console.log(store)
//console.log(store.state.entities.Calculators.data[1].title)

Im probably missing something obvious, so I keep googling and see if I can figure it out.

toadkicker commented 5 years ago

//export default store

commented out?

Also grab the https://github.com/vuejs/vue-devtools

toadkicker commented 5 years ago

@cameroncf where did you see that information? I am not able to find much about it searching for uuid in core and here.

edit: https://github.com/vuex-orm/plugin-graphql/issues/62

phortx commented 5 years ago

and that these where compatible, that this plugin should work out of the box?

Should, but it's not guaranteed due to the fact that this plugin currently has a opinion how the GraphQL Schema should look like :)

Alonitor commented 5 years ago

Ok, I got further here now, and if I get a complete solution I will post it. But for now I seem to be stuck at this error trying to create a new post:

vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in v-on handler (Promise/async): "Error: GraphQL error: Validation error of type UnknownType: Unknown type PostInput
GraphQL error: Validation error of type MissingFieldArgument: Missing field argument input @ 'createPost'
GraphQL error: Validation error of type UnknownArgument: Unknown field argument post @ 'createPost'
GraphQL error: Validation error of type FieldUndefined: Field 'nodes' in type 'ModelCommentConnection' is undefined @ 'createPost/comments/nodes'"

Appsync create mutation should look like this:

export const createPost = `mutation CreatePost($input: CreatePostInput!) {
  createPost(input: $input) {
    id
    title
    content
    publishedAt
    comments {
      items {
        id
        author
        content
        publishedAt
        postId
      }
      nextToken
    }
  }
}
`;

So it seams that the created "PostInput" should be just "input". Is there a way to do this?

toadkicker commented 5 years ago

@Alonitor That is because this plugin's opinion is to favor camelCase instead of TitleCase.

Alonitor commented 5 years ago

Thank you guy's for helping me out. Im probably going to abandon this path for now. It seams that this wont work with AWS appsync out of the box as I had hoped.

Im also really looking for a solution where I dont need to do repetetive tasks regarding the data layer. Ideally it would be nice to define you data classes (model), then have the schema be generated from this, update Appsync and then just related to your object model. But I will look into this again if it will work with appsync.

Anyway good work and keep on it! :)

toadkicker commented 5 years ago

Well I am assigned to work on this for the next several weeks so I will see what I can do. On Apr 18, 2019, 18:21 -0700, Alonitor notifications@github.com, wrote:

Closed #86. — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

phortx commented 5 years ago

I don't want to close this. Because in my opinion this plugin should support AWS AppSync and all other GraphQL Schemas.

My idea is to have some kind of Adapter Pattern here which allows to simply configure the schema-layout with presets (appsync, rails-graphql, graphene, and so on) or to simply pass a custom adapter.

I want to have that for version 1.0.0 (which means: ASAP). I'm very busy currently but I wish to put some work into this in the next weeks.

Thank you very much guys for your input and this discussion! :)

Alonitor commented 5 years ago

Ok Grate!! Here are some more thoughts, you may use as you like: It would be nice to take into account AWS Amplify. The Amplify CLI lets you create among other things the GraphQL API (Appsync). Amplify creates some folders in your project under the folder "Amplify". Here you can find ./amplify/backend/api/[yourapp]/schema.graphql This is where you define your schema/model and Amplify will grab this file, create all your backend needs and write all the files you need back into your project (if you ask for it (default is yes)). If you do it will create/update files in src/graphql. Here it will create files containing your queries, mutations, subscriptions as well as a schema.json file.

So I'm thinking two possible top level approaches:

  1. schema.graphql is the hub and somehow we create the model classes based on this.
  2. The model classes is the hub and somehow we can create update the schema.graphql based on this.

And if I understand the concept right, there probably has to be a new connection mode that matches the generated queries, mutations, subscriptions in the src/graphql folder.

toadkicker commented 5 years ago

Here's what I got working:

plugins/aws:

import Vue from 'vue'
import Amplify, * as AmplifyModules from 'aws-amplify'
import { AmplifyPlugin } from 'aws-amplify-vue'
import awsexports from '../aws-exports'

Amplify.configure(awsexports)

Amplify.configure({
  API: {
    graphql_endpoint: awsexports.aws_appsync_graphqlEndpoint,
    graphql_headers: () => ({
      'x-api-key': awsexports.aws_appsync_apiKey
    })
  }
})

Vue.use(AmplifyPlugin, AmplifyModules)

store/index.js:

import VuexORM from '@vuex-orm/core'
import VuexORMGraphQL from '@vuex-orm/plugin-graphql'
import database from '../database'
import awsmobile from '../aws-exports'

const options = {
  connectionQueryMode: 'nodes',
  database: database,
  url: awsmobile.aws_appsync_graphqlEndpoint,
  includeExtensions: true,
  headers: {
    'x-api-key': awsmobile.aws_appsync_apiKey
  },
  debug: process.env.NODE_ENV !== 'production'
}

VuexORM.use(VuexORMGraphQL, options)

export const plugins = [
  VuexORM.install(database)
]

database/index.ts:

import { Database } from '@vuex-orm/core'
import Company from '../models/Company'

database.register(Company)
export default database

models/Company.ts:

import { Model } from '@vuex-orm/core'

export default class Company extends Model {
  static entity = 'companies';

  static primaryKey = 'pKey';

  static fields() {
    return {
      name: this.attr('name'),
      pKey: this.attr('pKey')
    }
  }
}

I'll preface this that I'm using Nuxt to build with, so fetch is specific to them. If you aren't using Nuxt, just change listCompanies() from .all() to .fetch(). Now for the part where Amplify and this plugin come together in Vue:

<amplify-connect :query="companies">
        <template slot-scope="{ loading, data, errors }">
          <div v-if="loading">
            Loading...
          </div>
          <div v-else-if="errors.length > 0">
            Errors {{ errors }}
          </div>
          <div v-else-if="data">
            <div v-for="company in listCompanies" :key="company.pKey">
              <h1>{{ company.name }}</h1>
            </div>
          </div>
        </template>
      </amplify-connect>
</template>
<script>
import { components } from 'aws-amplify-vue'
import Company from '@/models/Company'

export default {
  name: 'CompaniesPage',
  components: { ...components },
  computed: {
    listCompanies() {
      return Company.all()
    }
  },
  fetch() {
    Company.fetch()
  }
}
</script>
phortx commented 5 years ago

I've implemented a basic adapter pattern. Could you take a look if this would help you?

https://github.com/vuex-orm/plugin-graphql/pull/89

I will merge this next week after I've tested it and I'm open for feedback :)

phortx commented 5 years ago

Merged (but not released yet)

douglance commented 4 years ago

@toadkicker How did you customize the function of vuex-orm to match the AppSync schemas?

I am having trouble changing the default name of listed items of nodes to the AppSync default of items.

toadkicker commented 4 years ago

I added a default appsync adapter to the project some time ago. I haven’t had a recent use case for this tool so I can’t confirm if it still works in a live app.

To do that, import the appsync adapter and pass it into query/mutation builder as your template constructor.

Was using https://github.com/atulmy/gql-query-builder to do it

douglance commented 4 years ago

@phortx Is there a way to rename nodes to items when getting lists?

phortx commented 4 years ago

Currently not possible, I think. Please feel free to create a PR :)

cto-leaps commented 3 years ago

hi @toadkicker I have been working for a few months on a Nuxtjs project with Amplify backend and I am only discovering vuex-orm and your graphql plugin 😱 I would have saved a lot of time had I seen this before. At least I think, since I am trying to implement it right now and I seem to run into some 401 issues as I am using Amplify endpoints with IAM authentification. The Introspection fails because of it. And also, do you have an example repo or something working I can look at with this kind of Nuxt /Vuex ORM / Amplify setup?