Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphQL Mutations #9406

Closed
juankaram opened this issue Oct 25, 2018 · 10 comments
Closed

GraphQL Mutations #9406

juankaram opened this issue Oct 25, 2018 · 10 comments

Comments

@juankaram
Copy link

@juankaram juankaram commented Oct 25, 2018

馃憢 Maintainers!

Summary

Is it possible to run graphql mutations using the Gatsby graphql wrapper?

Relevant information

I did a little reading of the source code and didn't found any pieces that indicate that this is possible, besides going the apollo client route. If is not possible is totally fine, i just want to make sure before i commit to add the apollo client, etc. Also, adding support will be something you would welcome in a pull request? It seems really valuable to be able to send data back to the server in certain cases, and Gatsby does a phenomenal job simplifying this operations.

Just with the objective of understanding more the line that lead me to think that there is no support is:

new StripNonQueryTransform(),
Did you found any particular issue with letting the mutations go through the schema stitching?

Thank you for your hard work on supporting Gatsby and all the developers that depend on it!

@tommy-dev

This comment has been minimized.

Copy link

@tommy-dev tommy-dev commented Nov 6, 2018

I am stuck too at trying to use mutation from GatsbyJS version 2. I was excited to get AWS Appsync and Gatsby querying data. This is the next missing puzzle.

@juankaram

This comment has been minimized.

Copy link
Author

@juankaram juankaram commented Nov 6, 2018

@tommy-dev This is my current workaround if you are interested:

const GraphQLClient = require("graphql-request").GraphQLClient;
  
  async mutate(mutationField: string) {
    const client = new GraphQLClient(process.env.API_URL, {
      headers: {
        "x-api-key": process.env.API_KEY
      }
    });

    return await client.request(
      `
      mutation Mutation($mutationField: String!){
        applyMutation(mutationField: $mutationField) {
          MutationId
        }
      }`,
      {
        mutationField: "abcde"
      }
    );
  }

mutationField Mutation applyMutation MutationId are placeholders for your graphql schema.

@tommy-dev

This comment has been minimized.

Copy link

@tommy-dev tommy-dev commented Nov 6, 2018

Thanks @juankaram I managed to get mutation to work with AppSync and Dynamodb using Apollo Client. I am pretty new to the graphql world, so a bit curious why you did not go forward with Apollo Client? Example code below for those interested


// @ts-check
import React from "react"
import EventList from "../components/EventList"
import { graphql } from 'gatsby'
import ApolloClient from "apollo-boost";
import gql from "graphql-tag";
import { Mutation } from "react-apollo";

const client = new ApolloClient({
    uri: "...",
    headers: {
        "x-api-key": "..."
    }
});
const ADD_TODO = gql`
your mutation graphql string here
`;


        <Mutation mutation={ADD_TODO} client={client}>
            {(addTodo, { data }) => (
                <div>

                    <div onClick={() => addTodo()}>Add item</div>

                </div>
            )}
        </Mutation>

@LekoArts

This comment has been minimized.

Copy link
Contributor

@LekoArts LekoArts commented Nov 16, 2018

GraphQL only runs at build time and isn't supposed to do Mutations. You can use Apollo Client though, e.g. like the Gatsby store:
https://github.com/gatsbyjs/store.gatsbyjs.org

@LekoArts LekoArts closed this Nov 16, 2018
@tkkrr

This comment has been minimized.

Copy link

@tkkrr tkkrr commented Dec 30, 2018

Hello. I succeeded in mutation execution with AppSync with the following code. ( using Typescript )

// client.ts
import AWSAppSyncClient, { AUTH_TYPE } from 'aws-appsync'
import AppSyncConfig from './aws-exports'



const appSyncClientOptions = {
  url: AppSyncConfig.graphqlEndpoint,
  region: AppSyncConfig.region,
  auth: {
    type: AUTH_TYPE.API_KEY,
    apiKey: AppSyncConfig.apiKey,
    // jwtToken: async () => token, // Required when you use Cognito UserPools OR OpenID Connect. token object is obtained previously
  },
} 

export default new AWSAppSyncClient(appSyncClientOptions)
// page/form.tsx
import * as React from 'react'

import Layout from '../components/layout'
import { CreateBlogMutationVariables, CreateBlogMutation } from '../API'

import client from '../client'
import { createBlog } from '../graphql/mutations'
import gql from 'graphql-tag'


interface formProps {
}

interface formState {
    headline: string,
    content: string,
    date: string,
}

class Form extends React.Component< formProps , formState> {
    constructor(props:formProps){
        super(props)
        this.state = {
            headline: "",
            content: "",
            date: this.getDateString()
        }
    }

    getDateString = () => {
        const nowDate = new Date()
        const dateString = `${nowDate.getFullYear()}-${nowDate.getMonth()+1}-${nowDate.getDate()}`
        return dateString
    }

    handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
        event.preventDefault()
        this.asyncTask()
        return
    }

    handleChange = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
        const inputId = event.currentTarget.id
        const updateState = {
            headline: inputId === "headline" ? event.currentTarget.value : this.state.headline,
            content:  inputId === "content"  ? event.currentTarget.value : this.state.content,
        }

        this.setState(
            Object.assign(
                this.state,
                updateState
            )
        )
    }

    async asyncTask(): Promise<void> {
        console.log("mutation start")
        try {
            const response = await client.mutate<CreateBlogMutation, CreateBlogMutationVariables>({
                mutation: gql( createBlog ),
                variables: {
                    input: this.state
                }
            })
        } catch (err) {
            console.error(err);
        } finally {
            console.log("mutation end")
        }
    
        return
    }


    render() {
        return <Layout>
            <form onSubmit={this.handleSubmit} action="/">
                <div className="form__headline">
                    <label htmlFor="headline">title</label>
                    <input type="text" id="headline" onChange={this.handleChange} />
                </div>
                <div className="form__content">
                    <label htmlFor="content">content</label>
                    <textarea id="content" rows={3} cols={20} style={{resize:"none"}} onChange={this.handleChange} />
                </div>
                <input type="submit" value="submit"/>
            </form>
        </Layout>
    }
}

export default Form

I referred to this site 鈫 https://aws-amplify.github.io/docs/js/api#run-a-mutation

@tkkrr

This comment has been minimized.

Copy link

@tkkrr tkkrr commented Dec 30, 2018

The code I posted works fine in the development environment...
In other words ... It works in gatsby develop, but cannot pass gatsby build ...
I got the following error ...

Building static HTML for pages/Path/To/Working/Dir/public/render-page.js:9192
        var store = disableOffline ? null : store_1.createStore(function () { return _this; }, function () { resolveClient(_this); }, dataIdFromObject, storage, callback);
                                                                                                             ^

TypeError: resolveClient is not a function
    at /Users/Tucker/Desktop/gatsby-site/public/render-page.js:9192:110
    at persistCallback (/Users/Tucker/Desktop/gatsby-site/public/render-page.js:12194:13)
    at complete (/Users/Tucker/Desktop/gatsby-site/public/render-page.js:51176:19)
    at /Users/Tucker/Desktop/gatsby-site/public/render-page.js:51168:11
    at complete (/Users/Tucker/Desktop/gatsby-site/public/render-page.js:51035:5)
    at /Users/Tucker/Desktop/gatsby-site/public/render-page.js:51009:29
    at Immediate.<anonymous> (/Users/Tucker/Desktop/gatsby-site/public/render-page.js:50906:19)
    at runCallback (timers.js:697:11)
    at tryOnImmediate (timers.js:667:5)
    at processImmediate (timers.js:649:5)
    at process.topLevelDomainCallback (domain.js:121:23)

error Building static HTML for pages failed

See our docs page on debugging HTML builds for help https://gatsby.app/debug-html

  77 |         if (typeof window === 'undefined')
  78 |             library = 'node-fetch';
> 79 |         throw new Error("\nfetch is not found globally and no fetcher passed, to fix pass a fetch for\nyour environment like https://www.npmjs.com/package/" + library + ".\n\nFor example:\nimport fetch from '" + library + "';\nimport { createHttpLink } from 'apollo-link-http';\n\nconst link = createHttpLink({ uri: '/graphql', fetch: fetch });");
     | ^
  80 |     }
  81 | };
  82 | export var createSignalIfSupported = function () {


  WebpackError:

  - index.js:79 checkFetcher
    [lib]/[apollo-link-http-common]/lib/index.js:79:1

  - httpLink.js:43 Module.createHttpLink
    [lib]/[apollo-link-http]/lib/httpLink.js:43:17

  - client.js:125 Object../node_modules/aws-appsync/lib/client.js.exports.createAppSyncLink
    [lib]/[aws-appsync]/lib/client.js:125:1

  - client.js:184 new AWSAppSyncClient
    [lib]/[aws-appsync]/lib/client.js:184:1

  - client.ts:19 Module../src/client.ts
    lib/src/client.ts:19:16

  - bootstrap:19 __webpack_require__
    lib/webpack/bootstrap:19:1


  - bootstrap:19 __webpack_require__
    lib/webpack/bootstrap:19:1


  - bootstrap:19 __webpack_require__
    lib/webpack/bootstrap:19:1

  - sync-requires.js:9 Object../.cache/sync-requires.js
    lib/.cache/sync-requires.js:9:55

  - bootstrap:19 __webpack_require__
    lib/webpack/bootstrap:19:1

  - static-entry.js:9 Module../.cache/static-entry.js
    lib/.cache/static-entry.js:9:22

  - bootstrap:19 __webpack_require__
    lib/webpack/bootstrap:19:1

  - bootstrap:83
    lib/webpack/bootstrap:83:1

I don鈥檛 know this error solution at gatsby ...
Please tell me the solution to get rid of this error.

@KyleAMathews

This comment has been minimized.

@tkkrr

This comment has been minimized.

Copy link

@tkkrr tkkrr commented Dec 31, 2018

Wow!! fantastic!!
I can passed gatsby build !!

I used ApolloClient instead of AWSAppSyncClient and it worked.

// client.ts
import AppSyncConfig from './aws-exports'

import ApolloClient from 'apollo-client'
import { createHttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context'
import 'isomorphic-fetch' // Comment out this line results in an error ...
import { InMemoryCache } from 'apollo-cache-inmemory';

const httpLink = createHttpLink({
  uri: AppSyncConfig.graphqlEndpoint,
})

const authLink = setContext( (_, { headers } ) => {
  return {
    headers: {
      ...headers,
      "x-api-key": AppSyncConfig.apiKey
    }
  }
})

export const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
});
// gatsby-ssr.js  and  gatsby-browser.js
import React from 'react';
import { ApolloProvider } from 'react-apollo';
import { client } from './src/client';

export const wrapRootElement = ({ element }) => (
    <ApolloProvider client={client}>
        {element}
    </ApolloProvider>
);
import * as React from 'react'

import Layout from '../components/layout'
import { CreateBlogMutationVariables, CreateBlogMutation } from '../API'

import { createBlog } from '../graphql/mutations'
import gql from 'graphql-tag'
import { Mutation } from 'react-apollo';


interface formProps {
}

interface formState {
    headline: string,
    content: string,
    date: string,
}

class BlogMutation extends Mutation<CreateBlogMutation, CreateBlogMutationVariables> {}

class Form extends React.Component< formProps , formState> {
    constructor(props:formProps){
        super(props)
        this.state = {
            headline: "",
            content: "",
            date: this.getDateString()
        }
    }

    getDateString = () => {
        const nowDate = new Date()
        const dateString = `${nowDate.getFullYear()}-${nowDate.getMonth()+1}-${nowDate.getDate()}`
        return dateString
    }

    handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
        event.preventDefault()
        return
    }

    handleChange = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
        const inputId = event.currentTarget.id
        const updateState = {
            headline: inputId === "headline" ? event.currentTarget.value : this.state.headline,
            content:  inputId === "content"  ? event.currentTarget.value : this.state.content,
        }

        this.setState(
            Object.assign(
                this.state,
                updateState
            )
        )
    }


    render() {
        return <Layout>
            <BlogMutation mutation={ gql(createBlog) }>
                {( addBlog, { loading, error }) => (
                <form 
                    onSubmit={ e => {
                        this.handleSubmit(e)
                        addBlog({variables: {input: this.state }})
                    }}
                    action="/">
                    <div className="form__headline">
                        <label htmlFor="headline">title</label>
                        <input type="text" id="headline" onChange={this.handleChange} />
                    </div>
                    <div className="form__content">
                        <label htmlFor="content">content</label>
                        <textarea id="content" rows={3} cols={20} style={{resize:"none"}} onChange={this.handleChange} />
                    </div>
                    <input type="submit" value="submit"/>
                    {loading && <p>loading...</p>}
                    {error && <p>Erorr: {error.message}</p>}
                </form>
                )}
            </BlogMutation>
        </Layout>
    }
}

export default Form
@tkkrr

This comment has been minimized.

Copy link

@tkkrr tkkrr commented Dec 31, 2018

@KyleAMathews Thank you !!

@Kwasula

This comment has been minimized.

Copy link

@Kwasula Kwasula commented Apr 16, 2019

I cant find any mutation delete,update, subscription example for gatsby-source-firebase-firestore. I can query a post document with five elements . Id, Name, description, date, town. But I have still problem with mutation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants
You can鈥檛 perform that action at this time.