amplify-category-api icon indicating copy to clipboard operation
amplify-category-api copied to clipboard

Support AppSync JavaScript Resolvers

Open austinamorusoyardstick opened this issue 2 years ago • 40 comments

Is this feature request related to a new or existing Amplify category?

No response

Is this related to another service?

Appsync JavaScript resolvers

Describe the feature you'd like to request

New feature in appsync how to use within amplify?

https://aws.amazon.com/blogs/aws/aws-appsync-graphql-apis-supports-javascript-resolvers/

Describe the solution you'd like

Steps to use within amplify

Describe alternatives you've considered

Manual work around that doesn't change the way we deploy

Additional context

No response

Is this something that you'd be interested in working on?

  • [ ] 👋 I may be able to implement this feature request

Would this feature include a breaking change?

  • [ ] ⚠️ This feature might incur a breaking change

austinamorusoyardstick avatar Nov 18 '22 16:11 austinamorusoyardstick

bump

isaac-elvt avatar Jan 09 '23 17:01 isaac-elvt

Need this soon please!

BBopanna avatar Jan 18 '23 14:01 BBopanna

Third'd - this would be a great feature to have!

james-kuihi avatar Feb 03 '23 16:02 james-kuihi

Would be much appreciated by developers

danfreid avatar Feb 24 '23 14:02 danfreid

We would appreciate this too!

alexboulay avatar May 04 '23 17:05 alexboulay

bump

dklein1211 avatar May 18 '23 17:05 dklein1211

bump!

vaughngit avatar May 23 '23 02:05 vaughngit

bump

tminus1-design avatar May 29 '23 06:05 tminus1-design

bump!!

prashant-joshi-25 avatar May 30 '23 08:05 prashant-joshi-25

➕ 1️⃣

milesnash-sky avatar Jun 07 '23 13:06 milesnash-sky

bump

mithun35h avatar Jul 20 '23 06:07 mithun35h

Honestly changing all the vlt templates to JS would likely enable to Amplify community to issue patches easier and fixes / plugins for issues surrounding resolvers.

austinamorusoyardstick avatar Jul 20 '23 18:07 austinamorusoyardstick

Any plans for this rebuild of the resolvers?

biller-aivy avatar Aug 21 '23 20:08 biller-aivy

bump

PatrykMilewski avatar Aug 23 '23 12:08 PatrykMilewski

bump

jay-herrera avatar Aug 28 '23 17:08 jay-herrera

Seems like AWS phases out of VTL if favor of APPSYNC_JS (source: https://docs.aws.amazon.com/appsync/latest/devguide/resolver-mapping-template-reference.html), image are there any updates on this feature?

barshopen avatar Sep 07 '23 10:09 barshopen

bump

chrisl777 avatar Sep 19 '23 01:09 chrisl777

its a nightmare to do graphql with appsync.. i update to node 16, update my sls to 3, update appsync plugin to support js resolver and, what, the simulator broke everything

giraudvalentin avatar Sep 21 '23 13:09 giraudvalentin

Hi - while we update our documentation to illustrate how to do this with the Amplify CLI, I do want to bring attention to the fact that the GraphQL API category is now available first-class as a CDK construct. In the CDK construct, you can use JavaScript resolvers. Check out our announcement post here: https://aws.amazon.com/blogs/mobile/announcing-aws-amplifys-graphql-api-cdk-construct-deploy-real-time-graphql-api-and-data-stack-on-aws/

renebrandel avatar Oct 12 '23 14:10 renebrandel

Yes please

ElliottAtYardstick avatar Oct 12 '23 17:10 ElliottAtYardstick

This would be lovely, yes please!

travishaby avatar Dec 13 '23 14:12 travishaby

bump

lukasulbing avatar Dec 19 '23 14:12 lukasulbing

bump

Dennis-Dekker avatar Jan 18 '24 13:01 Dennis-Dekker

BUMP

armenr avatar Jan 25 '24 16:01 armenr

bump 🤭

espetro avatar Mar 18 '24 15:03 espetro

@renebrandel - you mentioned there'd be updates to the documentation, but in the mean time, can we get even a rough overview/walkthrough of how to do it "by hand" for existing amplify projects that are cli-dependent/cli-generated?

Please? :)

armenr avatar Mar 18 '24 15:03 armenr

I took a look here: https://docs.aws.amazon.com/appsync/latest/devguide/configuring-resolvers-js.html

And then I got impatient, and thought maybe I'd give this a try. I kinda took a sledgehammer to it, so please forgive me if there are more elegant ways to achieve this.

In case anybody is as masochistic as I am, I think I got pretty close with this.

I don't think it works yet (it's 4:00 AM here and I need to sleep), but it's a close-enough starting point, in case anyone wants to hack on this together.

👉 If anyone does bother to try this, and is successful (or finds ways to make it work), please share back your solution 😎.

amplify add custom --> name: MyCustomResolvers

cdk-stack.ts

import type { Construct } from 'constructs'
import * as AmplifyHelpers from '@aws-amplify/cli-extensibility-helper'
import * as cdk from 'aws-cdk-lib'
import * as appsync from 'aws-cdk-lib/aws-appsync'
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'
import type { AmplifyDependentResourcesAttributes } from '../../types/amplify-dependent-resources-ref'

export class cdkStack extends cdk.Stack {
  constructor(
    scope: Construct,
    id: string,
    props?: cdk.StackProps,
    amplifyResourceProps?: AmplifyHelpers.AmplifyResourceProps,
  ) {
    super(scope, id, props)
    /* Do not remove - Amplify CLI automatically injects the current deployment environment in this input parameter */
    const _env = new cdk.CfnParameter(this, 'env', {
      type: 'String',
      description: 'Current Amplify CLI env name',
    })

    // Get the project name from AmplifyHelper
    // and create a standard prefix for all resources - "projectName-env"
    const { projectName, envName } = AmplifyHelpers.getProjectInfo()
    const _resourcePrefix = `${projectName}-${envName}`

    /*
    * 🛑 📢
    * There is a known bug (https://github.com/aws-amplify/amplify-cli/issues/13532) that *may* prevent
    * the use of the addResourceDependency function from AmplifyHelpers.
    *
    * If you run into that bug, you can use the following workaround to reference dependent resources in your CDK stack.
    *
    * The formula is: category + resourceName + outputName
    *
    * For a GraphQL API example: apiMyApiNameGraphQLAPIIdOutput
    * For an S3 Storage example: storageMyProjectStorageStorageBucketName
    *
    * Where:
    *   category: api
    *   resourceName: <YOUR_API_NAME>
    *   outputName: GraphQLAPIIdOutput
    *
    * const resourceCategory = 'api'
    * const resourceName = 'myprojectV2AwesomeAPI'
    * const outputName = 'GraphQLAPIIdOutput'
    * const GraphQLAPIIdOutput = resourceCategory + resourceName + outputName
    *
    */

    const apiResourceReference: AmplifyDependentResourcesAttributes = AmplifyHelpers.addResourceDependency(
      this,
      amplifyResourceProps.category,
      amplifyResourceProps.resourceName,
      [
        {
          category: 'api',
          resourceName: 'myprojectV2AwesomeAPI',
        },
      ],
    )

    // Instantiate a reference to the API using the API ID from the Amplify-generated stack
    const api = appsync.GraphqlApi.fromGraphqlApiAttributes(this, 'api', {
      graphqlApiId: apiResourceReference.api.myprojectV2AwesomeAPI.GraphQLAPIIdOutput,
    })

    // Time to get our CloudFormation on - there's no escaping it!
    const graphqlApiId = apiResourceReference.api.myprojectV2AwesomeAPI.GraphQLAPIIdOutput
    const tableName = 'UserTable'
    const importString = `\${${graphqlApiId}}:GetAtt:${tableName}:Name`

    /* Get the name of the table using cloudformation stack outputs
    *  We are attempting to mimic this:
    *
    *   "Fn::ImportValue": {
    *     "Fn::Sub": "${apimyprojectV2AwesomeAPIGraphQLAPIIdOutput}:GetAtt:UserTable:Name"
    *   }
    */

    // Get the name of the table using cloudformation stack outputs - e.g. User-5mt2wm3hzrgptjlq7qcbryxcgi-mybackend
    const UserTableName = cdk.Fn.importValue(cdk.Fn.sub(importString))

    // Instantiate a reference to the DynamoDB table using the table name from the Amplify-generated stack
    const userTableDDB = dynamodb.Table.fromTableName(this, 'table', UserTableName)

    // Create a new AppSync function and pass in the API and the DynamoDB table objects
    const appsyncFuncScanUsersTable = new appsync.AppsyncFunction(this, 'func-scan-users', {
      name: 'scan_users_func_1',
      api,
      dataSource: api.addDynamoDbDataSource('table-for-posts', userTableDDB),
      code: appsync.Code.fromInline(`
          export function request(ctx) {
            return { operation: 'Scan' };
          }

          export function response(ctx) {
            return ctx.result.items;
          }
      `),
      runtime: appsync.FunctionRuntime.JS_1_0_0,
    })

    // Create a new AppSync resolver
    const _resolver = new appsync.Resolver(this, 'custom-resolver', {
      api,
      typeName: 'Query',
      fieldName: 'getPost',
      code: appsync.Code.fromInline(`
          export function request(ctx) {
            return {};
          }

          export function response(ctx) {
            return ctx.prev.result;
          }
      `),
      runtime: appsync.FunctionRuntime.JS_1_0_0,
      pipelineConfig: [appsyncFuncScanUsersTable],
    })
  }
}

package.json

{
  "name": "custom-resource",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "build": "tsc",
    "watch": "tsc -w",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "@aws-amplify/cli-extensibility-helper": "^3.0.25",
    "aws-cdk-lib": "~2.80.0",
    "constructs": "^10.3.0"
  },
  "devDependencies": {
    "typescript": "^5.4.2"
  },
  "resolutions": {
    "aws-cdk-lib": "~2.80.0"
  }
}

armenr avatar Mar 19 '24 00:03 armenr

^^ This gets close, but it won't work. There's 16 hours of my life I won't get back. Maybe someone can figure out what I'm doing wrong by trying it themselves. I'm good and stuck.

Abandon all hope, ye who enter here. Waiting for any help or instruction from @renebrandel & our Amplify friends... 🫠

armenr avatar Mar 19 '24 08:03 armenr

Hi - @armenr - sorry for the dropping the ball here. So here's a working sample that should work for you.

For the JS resolvers, I build a quick "echo this message" resolver example. The graphQL schema looks like this:

input AMPLIFY { globalAuthRule: AuthRule = { allow: public } } # FOR TESTING ONLY!

type Todo @model {
  id: ID!
  name: String!
  description: String
}

type Query {
  echo(message: String): String # your custom queries here
}

The custom stack looks like this:

import * as cdk from 'aws-cdk-lib';
import * as AmplifyHelpers from '@aws-amplify/cli-extensibility-helper';
import * as appsync from 'aws-cdk-lib/aws-appsync';
import { AmplifyDependentResourcesAttributes } from '../../types/amplify-dependent-resources-ref';
import { Construct } from 'constructs';

const jsResolverTemplate = `
export function request(ctx) {
  return {
    payload: null
  }
}

export function response(ctx) {
  return ctx.arguments.message
}
`

export class cdkStack extends cdk.Stack {
  constructor(
    scope: Construct,
    id: string,
    props?: cdk.StackProps,
    amplifyResourceProps?: AmplifyHelpers.AmplifyResourceProps
  ) {
    super(scope, id, props);
    /* Do not remove - Amplify CLI automatically injects the current deployment environment in this input parameter */
    new cdk.CfnParameter(this, 'env', {
      type: 'String',
      description: 'Current Amplify CLI env name'
    });

    // Access other Amplify Resources
    const retVal: AmplifyDependentResourcesAttributes =
      AmplifyHelpers.addResourceDependency(
        this,
        amplifyResourceProps.category,
        amplifyResourceProps.resourceName,
        [
          {
            category: 'api',
            resourceName: 'gen1jsresolver'
          }
        ]
      );

    const resolver = new appsync.CfnResolver(this, 'CustomResolver', {
      // apiId: retVal.api.new.GraphQLAPIIdOutput,
      // https://github.com/aws-amplify/amplify-cli/issues/9391#event-5843293887
      // If you use Amplify you can access the parameter via Ref since it's a CDK parameter passed from the root stack.
      // Previously the ApiId is the variable Name which is wrong , it should be variable value as below
      apiId: cdk.Fn.ref(retVal.api.gen1jsresolver.GraphQLAPIIdOutput),
      fieldName: 'echo',
      typeName: 'Query', // Query | Mutation | Subscription
      code: jsResolverTemplate,
      dataSourceName: 'NONE_DS', // DataSource name
      runtime: {
        name: 'APPSYNC_JS',
        runtimeVersion: '1.0.0'
      }
    });
  }
}

Do you mind sharing more about the exact error you're facing? My guess is that this line is probably where the error is coming from:

      graphqlApiId: apiResourceReference.api.myprojectV2AwesomeAPI.GraphQLAPIIdOutput,

My guess is that the resolution of the reference doesn't happen correctly, thus the API won't be referenced. It should be this:

      graphqlApiId: cdk.Fn.ref(apiResourceReference.api.myprojectV2AwesomeAPI.GraphQLAPIIdOutput),

renebrandel avatar Mar 19 '24 21:03 renebrandel

@armenr - also cut a docs PR https://github.com/aws-amplify/docs/pull/7087

renebrandel avatar Mar 19 '24 21:03 renebrandel