Search code examples
typescriptaws-cdkaws-appsyncaws-appsync-resolver

How can I bundle Typescript AppSync resolvers in the CDK?


AppSync GraphQL resolvers can be written in JavaScript instead of VTL. AppSync has dev-time helpers for TypeScript resolvers, but we must bundle the code ourselves.

How can I bundle .ts resolvers in the CDK?

We pass the resolver code to the CDK AppsyncFunction construct. But we can't directly pass a my-resolver.ts file. Instead, appsync.Code.fromAsset needs my-resolver.js. Right now there isn't a batteries-included esbuild local bundling option like the one NodejsLambda construct has.

const resolverFunc = new appsync.AppsyncFunction(this, "MyResolverFunc", {
  name: "my_resolver",
  api,
  dataSource: dynamoDataSource,
  code: appsync.Code.fromAsset(
    path.join(__dirname, "../path/to/my_resolver.ts") // <- ❌ need a .js file here
  ),
  runtime: appsync.FunctionRuntime.JS_1_0_0,
});

Solution

  • For now, a esbuild script run during the CDK synth process seems the simplest option.

    [Step 1] Add a esbuild script to package.json. The bundling script, presented below in non-stringified form for clarity, outputs one .mjs file for each .ts resolver input file. The settings follow the example in the AppSync docs:

    esbuild path/to/resolvers/*.ts \
      --bundle \
      --sourcemap=inline \                  # optional
      --sources-content=false \             # optional, but recommended with sourcemaps
      --platform=node \
      --target=esnext \
      --format=esm \
      --external:@aws-appsync/utils \
      --out-extension:.js=.mjs \            # optional, output files with the esm .mjs extension
      --outdir=dist                         # where to put the bundled .js artefacts
    

    [Step 2] Tell the CDK to use the script with the build arg in the command line (cdk synth --build myBundleScript) or in cdk.json config file (build: myBundleScript).

    [Step 3] Finally, point the AppsyncFunction construct's code prop to an asset built from the Javascript:

    const func = new appsync.AppsyncFunction(this, "MyResolverFunc", {
       name: "my_resolver",
       api,
       dataSource,
       code: appsync.Code.fromAsset("dist/my_resolver.mjs"), // ✅
       runtime: appsync.FunctionRuntime.JS_1_0_0,
    });
    

    Alternatives? There is currently no built-in CDK bundler for AppSync resolvers. The NodejsLambda bundler seems like it would work, but it is not an exported member. Finally, while the AssetStaging construct has bundling options, it only emits a .zip archive, rather than the .js or .mjs file that AppSync is looking for (see GitHub issue).