👋 Maintainers!
Is it possible to run graphql mutations using the Gatsby graphql wrapper?
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: https://github.com/gatsbyjs/gatsby/blob/27dc8f372287c1d1fb12dfd045ddefc386303375/packages/gatsby-source-graphql/src/gatsby-node.js#L92 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!
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.
@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.
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>
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
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
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’t know this error solution at gatsby ...
Please tell me the solution to get rid of this error.
@jlengstorf gave a great talk on this exact topic
https://m.youtube.com/watch?v=wNUg1jpj9T0&feature=youtu.be
Slides: https://jlengstorf.github.io/presentations/beyond-static/
Also Dustin's talk https://github.com/DSchau/beyond-static-fitc
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
@KyleAMathews Thank you !!
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.
Most helpful comment
Wow!! fantastic!!
I can passed
gatsby build
!!I used
ApolloClient
instead ofAWSAppSyncClient
and it worked.