Search code examples
graphqlapollogatsbyaws-appsyncgraphql-tools

Connecting AWS Appsync Graphql Link as a Gatsbyjs Datasource


Working to set up a GatsbyJS plugin that sources from AWS AppSync using IAM auth. I keep getting an error that I can't track down what the problem is.

I don't have a ton of experience with graphql linking, and can't find good documentation on how to do it well in this context.

This is what my plugin gatsby-node.js file looks like:

const uuidv4 = require(`uuid/v4`)
const invariant = require(`invariant`)
const fetch = require(`node-fetch`)
const { createHttpLink } = require(`apollo-link-http`)
const { buildSchema, printSchema } = require(`graphql`)
const { makeRemoteExecutableSchema, transformSchema, introspectSchema, RenameTypes } = require(`graphql-tools`)
const AUTH_TYPE = require('aws-appsync/lib/link/auth-link').AUTH_TYPE
const AWS = require('aws-sdk');
const { createAppSyncLink } = require('aws-appsync');

exports.sourceNodes = async ( { actions, createNodeId, cache, createContentDigest }, options ) => {
  const { createNode } = actions
  delete options.plugins
  const { typeName, fieldName, refetchInterval, HOST, PATH, REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY } = options
  const url = HOST + PATH

  // making sure things are required properly
  invariant(
    typeName && typeName.length > 0,
    `gatsby-source-graphql requires option \`typeName\` to be specified`
  )
  invariant(
    fieldName && fieldName.length > 0,
    `gatsby-source-graphql requires option \`fieldName\` to be specified`
  )

  // Link to appsync
  const fetcherLink = createHttpLink({
    uri: url,
    fetch: fetch
  })

  const type = AUTH_TYPE.AWS_IAM

  AWS.config.update({
    region: REGION,
    credentials: new AWS.Credentials({
      accessKeyId: AWS_ACCESS_KEY_ID,
      secretAccessKey: AWS_SECRET_ACCESS_KEY
    })
  });

  const credentials = AWS.config.credentials;

  const link = createAppSyncLink({
    url: url,
    region: REGION,
    auth: { type, credentials },
    resultsFetcherLink: fetcherLink,
  });

  // Schema Creation
  let introspectionSchema
  const cacheKey = `gatsby-source-graphql-schema-${typeName}-${fieldName}`

  let sdl = await cache.get(cacheKey)

  console.log('sdl', sdl)
  if (!sdl) {
    introspectionSchema = await introspectSchema(link)
    sdl = printSchema(introspectionSchema)
  } else {
    introspectionSchema = buildSchema(sdl)
  }

  await cache.set(cacheKey, sdl)

  const remoteSchema = makeRemoteExecutableSchema({
    schema: introspectionSchema,
    link,
  })

  // Node creation
  const nodeId = createNodeId(`gatsby-source-graphql-${typeName}`)
  const node = createSchemaNode({
    id: nodeId,
    typeName,
    fieldName,
    createContentDigest,
  })
  createNode(node)

  const resolver = (parent, args, context) => {
    context.nodeModel.createPageDependency({
      path: context.path,
      nodeId: nodeId,
    })
    return {}
  }

  const schema = transformSchema(remoteSchema, [
    new StripNonQueryTransform(),
    new RenameTypes(name => `${typeName}_${name}`),
    new NamespaceUnderFieldTransform({ typeName, fieldName, resolver }),
  ])

  addThirdPartySchema({ schema })

  if (process.env.NODE_ENV !== `production`) {
    if (refetchInterval) {
      const msRefetchInterval = refetchInterval * 1000
      const refetcher = () => {
        createNode(
          createSchemaNode({
            id: nodeId,
            typeName,
            fieldName,
            createContentDigest,
          })
        )
        setTimeout(refetcher, msRefetchInterval)
      }
      setTimeout(refetcher, msRefetchInterval)
    }
  }
}

function createSchemaNode({ id, typeName, fieldName, createContentDigest }) {
  const nodeContent = uuidv4()
  const nodeContentDigest = createContentDigest(nodeContent)
  return {
    id,
    typeName: typeName,
    fieldName: fieldName,
    parent: null,
    children: [],
    internal: {
      type: `GraphQLSource`,
      contentDigest: nodeContentDigest,
      ignoreType: true,
    },
  }
}

This is my error message:

TypeError: Cannot read property 'store' of undefined

  • client.js:147 [gatsby-source-appsync]/[aws-appsync]/lib/client.js:147:27

  • client.js:139 ApolloLink.request [gatsby-source-appsync]/[aws-appsync]/lib/client.js:139:23

  • bundle.umd.js:188 ApolloLink.request [gatsby-source-appsync]/[aws-appsync]/[apollo-link]/lib/bundle.umd.js:188:35

  • bundle.umd.js:188 ApolloLink.request [gatsby-source-appsync]/[aws-appsync]/[apollo-link]/lib/bundle.umd.js:188:35

  • bundle.umd.js:188 ApolloLink.request [gatsby-source-appsync]/[aws-appsync]/[apollo-link]/lib/bundle.umd.js:188:35

  • link.js:84 Object.execute [gatsby-source-appsync]/[apollo-link]/lib/link.js:84:18

  • linkToFetcher.js:7 [gatsby-source-appsync]/[graphql-tools]/dist/stitching/linkToFetcher.js:7:56

  • introspectSchema.js:50 [gatsby-source-appsync]/[graphql-tools]/dist/stitching/introspectSchema.js:50:42

  • introspectSchema.js:31 step [gatsby-source-appsync]/[graphql-tools]/dist/stitching/introspectSchema.js:31:23

  • introspectSchema.js:12 Object.next [gatsby-source-appsync]/[graphql-tools]/dist/stitching/introspectSchema.js:12:53

  • introspectSchema.js:6 [gatsby-source-appsync]/[graphql-tools]/dist/stitching/introspectSchema.js:6:71

  • new Promise

  • introspectSchema.js:2 __awaiter [gatsby-source-appsync]/[graphql-tools]/dist/stitching/introspectSchema.js:2:12

  • introspectSchema.js:41 introspectSchema [gatsby-source-appsync]/[graphql-tools]/dist/stitching/introspectSchema.js:41:12

  • gatsby-node.js:60 Object.exports.sourceNodes /workspace/plugins/gatsby-source-appsync/gatsby-node.js:60:33

Haven't worked much with graphql links before. Any help?


Solution

  • For your scenario, it might be better to use only the createAuthLink from aws-appsync, because createAppSyncLink introduces a lot of stuff for offline scenarios that you don't seem to need (also, the full link for appsync depends on apollo's query manager inserting the cache in the operation context).

    Something like this might work for you:

    const { ApolloLink } = require(`apollo-link`)
    const { createAuthLink } = require("aws-appsync");
    
    const link = ApolloLink.from([
        createAuthLink({
            url: url,
            region: REGION,
            auth: { type, credentials },
        }),
        fetcherLink
    ]);