aws-amplify / amplify-codegen

Amplify Codegen is a JavaScript toolkit library for frontend and mobile developers building Amplify applications.
Apache License 2.0
58 stars 59 forks source link

Can I use the generated GraphQL queries in my Lambda functions? #474

Open PeteDuncanson opened 3 years ago

PeteDuncanson commented 3 years ago

I want to keep everything DRY as I can so would like my Lambda functions to be able to reuse my GraphQL queries. Trouble is I'm not sure how to do that!

I think that my functions when I add them are just uploaded separately in isolation of the rest of my code when I do a amplify push, by that I mean if I change my schema and regenerate everything I don't expect that to get pushed up to my functions but I could be wrong.

I'm currently using a Lambda Layer for shared code so I thought I would be the right place to put my queries but how to get them in there? Copy them to the \amplify\backend\function\myAmazingLayer\opt\ folder of my Layer? Could hook up a build step for that I guess?

It could be I've got work to do and I'm just missing the right imports or even to know which imports to use and their path. Or none of this is automagically available and I have to manually include it in my Layer somehow?

Again gaps in knowledge are telling here as I can't seem to find any information on how to do this. There is stuff out there on how to talk to DynamoDB direct (https://dev.to/dabit3/lambda-function-graphql-resolvers-11cd) in a Lambda but I'd rather go through my GraphQL API I think as I've been bitten before when I've pushed strings of data in to Dynamo directly for it to then blow up one of my AWS GraphQL types when I try to pull it back out via the GraphQL API. As a result I'd like everything to go through the "front door" of my GraphQL API. This doc (https://docs.amplify.aws/lib/graphqlapi/query-data/q/platform/js) covers how to talk to GraphQL in my front-end client code but I'd like to use this in my Lambda too...ideas?

Please can someone enlighten me.

UnleashedMind commented 3 years ago

Just wanted to make sure you are reference the right docs https://docs.amplify.aws/guides/functions/graphql-from-lambda/q/platform/js#signing-a-request-from-lambda You can try to access the GraphQL API without using the Lambda layer

PeteDuncanson commented 3 years ago

I'd not seen that documentation before, you guys have so much of it in so many places!

That seems to be doing roughly what I'm doing now. Only difference is I'm using Axios as my http client which is similar to https://docs.amplify.aws/guides/functions/graphql-from-lambda/q/platform/js#query listed above the section you linked to. Here is my code:

const axios = require("axios");
const { print } = graphql;

const API_KEY = "";
const API_ENDPOINT = "";

/**
 * Helper to set the apikey and endpoint once, call before you use getGraphQLData
 * @param {*} apiKey 
 * @param {*} apiEndpoint 
 */
function initApi( apiKey, apiEndpoint ) {
    API_KEY= apiKey;
    API_ENDPOINT= apiEndpoint;
    console.log( "API Details set", {apiKey, apiEndpoint});
}

/**
 * Gets a JSON resonse from our GraphQL end point
 * @param {*} query 
 * @param {*} inputVariables 
 * @param {*} apiEndpoint (optional if you've previously called initApi())
 * @param {*} apiKey (optional if you've previously called initApi())
 */
async function getGraphQLData(query, inputVariables, apiEndpoint=API_ENDPOINT, apiKey=API_KEY) {
  if ( apiKey === "" || apiEndpoint === "") {
      console.warn( "getGraphQLData: API KEY hasn't been set, did you forget to run init()?");
  }

  const queryData = {
    query: print(query),
  };
  if (inputVariables) {
    queryData.variables = { input: inputVariables };
  }

  const response = await axios({
    url: apiKey,
    method: "post",
    headers: {
      "x-api-key": apiEndpoint,
    },
    data: queryData,
  });

  if (response.status != "200") {
    console.error("Something went wrong with our GraphQL", response);
  }

  if (response.data && response.data.errors) {
    console.error(
      "Something went really wrong with our GraphQL",
      JSON.stringify(response.data.errors, null, 2),
      "inputVariable",
      inputVariables
    );
  }

  return response.data.data;
}

export default { getGraphQLData, initApi };

Then I've my queries in another file (called queries.js) like so:

const gql = require("graphql-tag");

const createAddress = gql`
mutation createAddress($input: CreateAddressInput!) {
  createAddress(input: $input) {
    id
  }
}
`;

const createCustomer = gql`
mutation createCustomer($input: CreateCustomerInput!) {
  createCustomer(input: $input) {
    id
  }
}
`;

export default { createAddress, createCustomer }

I've added the above to the Lambda Layer that my functions use as I'm requiring stuff that I don't think is available by default within a Lambda environment. Is the example you linked too using a Layer or are all those requires available out of the box for a Lambda I wonder? That might clean up my code a bit if that is the case.

Back to my original query though, I wondered if I could/should reuse the auto-generated mutations from Amplify within my Lambda and if so how to get them in there? So the issues isn't how to use GraphQL within a Lambda but more how to use my auto-generated queries/mutations from Amplify within a Lambda? The above works but having my creation mutations duplicated in several spots (and having to hand roll them) makes me feel funny :)

kaustavghosh06 commented 3 years ago

@PeteDuncanson Yeah, right now we don't autogenerate/codegen the mutations/queries to a specific lambda function and you'd have to manually copy the auto-generated mutations/queries to the Lambda functon directory. I'll mark this as an enhancement for our team to look at.

jgroom33 commented 3 years ago

aws-amplify/amplify-cli#5598 specifies this use case and potential feature request

nickday commented 3 years ago

I had this same issue, and got round it by having a functionUpdate.sh shell script like this:

echo "Compiling GraphQL..."
amplify api gql-compile

echo "Generating GraphQL statements..."
amplify codegen

echo "Copying lib/package.json to src/package.json in each function..."
find amplify/backend/function -type d -path "*/lib" -maxdepth 2 -exec cp src/lib/package.json {}/../src/ \;

echo "Copying lib/*.js to lib/lib/*.js in each function..."
find amplify/backend/function -type d -path "*/lib" -maxdepth 2 -exec rsync -r --delete src/lib/*.js {}/lib \;

echo "Copying graphql folder to lib in each function..."
find amplify/backend/function -type d -path "*/lib" -maxdepth 2 -exec rsync -r --delete src/graphql {} \;

echo "Done!"

and added it into the scripts section of package.json:

"functionUpdate": "./functionUpdate.sh",

This compiles/generates the GraphQL and copies the src/graphql folder over into the lib folder of each function (I'm using the lib/src folder structure as documented at https://docs.amplify.aws/cli/function/build-options). When babel transpiles the function it'll also transpile the GraphQL files ready for importing into the lambda function. (That script is also copying over some shared libraries and package requirements into each function, which for my specific use-case was an easier solution than using lambda layers.)

The only downside of this is that the queries will be returning all fields, where I may only need say id to be returned - but until that becomes a performance issue (unlikely) this at least saves having to duplicate queries/mutations in several places.

jgroom33 commented 3 years ago

I attempted the same using babel, but was unsuccessful. We're using javascript, not ts, so the build compilation is not necessary. This script accomplishes the same:

sed '1s/^/"use strict";const gql = require("graphql-tag");Object.defineProperty(exports, "__esModule", {value: true});/' src/graphql/mutations.js > amplify/backend/function/gqlLayer/opt/mutations.js
sed -i 's/\/\* GraphQL \*\/ /gql/g' amplify/backend/function/gqlLayer/opt/mutations.js
sed -i 's/export const /exports./g' amplify/backend/function/gqlLayer/opt/mutations.js

Thanks for the inspiration.

phani-srikar commented 3 years ago

This feature should be supported once we implement the plugins based architecture, where you can specify multiple docs generation tasks where each of them writes to a different directory. Link to RFC: https://github.com/aws-amplify/amplify-cli/issues/6898

josefaidt commented 2 years ago

Transferring to codegen for better assistance

cjsilva-umich commented 12 months ago

This would be really helpful. I don't suppose there's been any implementation?

cjsilva-umich commented 7 months ago

It's been over three. years.

PeteDuncanson commented 7 months ago
image