{"id":17204513,"url":"https://github.com/bboure/middy-appsync","last_synced_at":"2025-04-13T21:23:44.345Z","repository":{"id":40795526,"uuid":"225232971","full_name":"bboure/middy-appsync","owner":"bboure","description":"A middy middleware for AWS AppSync","archived":false,"fork":false,"pushed_at":"2023-01-05T02:05:52.000Z","size":1617,"stargazers_count":22,"open_issues_count":18,"forks_count":2,"subscribers_count":2,"default_branch":"beta","last_synced_at":"2025-03-27T11:43:33.724Z","etag":null,"topics":["appsync","aws","aws-appsync","aws-lambda","graphql","middleware","middy","serverless"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bboure.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["bboure"]}},"created_at":"2019-12-01T21:23:16.000Z","updated_at":"2024-07-14T04:15:05.000Z","dependencies_parsed_at":"2023-02-03T05:16:36.335Z","dependency_job_id":null,"html_url":"https://github.com/bboure/middy-appsync","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bboure%2Fmiddy-appsync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bboure%2Fmiddy-appsync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bboure%2Fmiddy-appsync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bboure%2Fmiddy-appsync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bboure","download_url":"https://codeload.github.com/bboure/middy-appsync/tar.gz/refs/heads/beta","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248287758,"owners_count":21078774,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appsync","aws","aws-appsync","aws-lambda","graphql","middleware","middy","serverless"],"created_at":"2024-10-15T02:22:15.191Z","updated_at":"2025-04-13T21:23:44.312Z","avatar_url":"https://github.com/bboure.png","language":"TypeScript","funding_links":["https://github.com/sponsors/bboure"],"categories":[],"sub_categories":[],"readme":"[Middy](http://middy.js.org/) Middleware for [Aws AppSync](https://aws.amazon.com/appsync/).\n\n# Install\n\n```bash\nnpm install --save middy-appsync\n```\n\n**Note**\n\nV1 of this plugin is currently in beta. The old `0.1.0` version is not recommended. You should use the beta instead, but be aware that it may include breaking changes at any time without notice. It is not recommended on production.\n\n```bash\nnpm install --save middy-appsync@beta\n```\n\n# Purpose\n\n## Controlled errors\n\nThrowing Errors/Exceptions when you identify a _controlled error_ in your app can be unnecessarely noisy. They cause your Lambda function to **fail** and return an error.\nErrors like `NotFoundException` or `UnauthorizedException` should _not_ raise alarms in your monitoring tools, but still cause the process to end, and AppSync (GraphQL) to return the Error to the client.\n\nThis middleware will catch any controlled error and handle them for you. Your Lambda function will succeed, but AppSync will return the error to the client.\nAny error that extends `AppSyncError` (a class exported by this library) will be treated like such.\n\nIf you still want to monitor these kind of errors, you can use the [error-logger](https://github.com/middyjs/middy/tree/master/packages/error-logger) and monitor it via a Cloudwatch metric for example\n\n## Granular errors with BatchInvoke\n\nWhen dealing with BatchInvoke, you might have errors that affect a few items of the batch only. This middleware follows the recommendations from the official [AppSync documentation](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-lambda-resolvers.html#returning-individual-errors).\n\nBy returning an `AppSyncError` as part of your batch, you can have granular control on which items are in error, and which are successful.\n\n# Usage\n\n## VTL response template\n\nThe middleware wraps the response of your handler function in a special object of the following shape:\n\n```ts\n{\n  data: any, // successfull data or error data\n  errorType: string,\n  errorMessage: string,\n  errorInfo: any,\n}\n```\n\nA sucessful response from the handler will be placed in the `data` property, while any Error/Exception extending `AppSyncError` will be caught and its corresponding attributes will be placed in the `error*` properties.\n\nAny other unhandled error still will be thrown by your Lambda.\n\nYou will need a special VTL response template that handles this response format accordingly.\nYou should use the following response template:\n\n```velocity\n#if($context.error)\n  $util.error(\"Internal Error\", \"InternalError\", $ctx.result)\n#elseif($context.result \u0026\u0026 $context.result.errorMessage)\n  $utils.error($context.result.errorMessage, $context.result.errorType, $context.result.data, $context.result.errorInfo)\n#else\n  $utils.toJson($context.result.data)\n#end\n```\n\n- the first condition takes care of unhandled errors\n- the second, controlled errors (like `NotFoundException`, `UnauthorizedException` or custom`AppSyncError`)\n- the else, handles successful results.\n\n## Handler usage\n\n```js\nconst middy = require('@middy/core');\nconst { appSync } = require('middy-appsync');\n\nconst doStuff = (event, context, callback) =\u003e {\n  return {\n    field1: 'Foo',\n    field2: 'Bar',\n  };\n};\n\nconst handler = middy(doStuff).use(appSync());\n\nmodule.exports = { handler };\n```\n\nThis example will return the following response to the VTL response mapping template:\n\n```js\n{\n  data: {\n    field1: 'Foo',\n    field2: 'Bar',\n  }\n}\n```\n\n## Error handling\n\nWhen a _controlled_ error occurs during the execution of your handler, you want to send basic information to the client. You can do so by filling the `message`, `errorType` and `errorInfo` fields of the error section of the GraphQL response.\n\nYou can acheive that with the an `AppSyncError` object in different ways:\n\n### With the callback argument\n\n- `throw` an `AppSyncError`.\n- return it as the `error` argument of the callback\n- return it as the `response` argument of the callback\n\n### With promises/async\n\n- `throw` an `AppSyncError`.\n- return it as the rejected value\n- return it as the resolved value\n\nExample:\n\n```js\nconst middy = require('@middy/core');\nconst { appSync, AppSyncError } = require('middy-appsync');\n\nconst doStuff = (event) =\u003e {\n  throw new AppSyncError('Record not found', 'NotFoundError');\n};\n\nconst handler = middy(doStuff).use(appSync());\n\nmodule.exports = { handler };\n```\n\nThis will generate the following response:\n\n```js\n{\n  errorMessage: 'Resource not found',\n  errorType: 'NotFoundError',\n  data: null,\n  errorInfo: null\n}\n```\n\nAnd your GraphQL response will look like so:\n\n```json\n{\n  \"data\": {\n    \"demo\": null\n  },\n  \"errors\": [\n    {\n      \"path\": [\"demo\"],\n      \"data\": null,\n      \"errorType\": \"NotFound\",\n      \"errorInfo\": null,\n      \"locations\": [\n        {\n          \"line\": 2,\n          \"column\": 3,\n          \"sourceName\": null\n        }\n      ],\n      \"message\": \"Resource not found\"\n    }\n  ]\n}\n```\n\n## BatchInvoke support\n\nThe middleware supports [ resolvers](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-lambda-resolvers.html#advanced-use-case-batching). If it detects that\nthe `event` object is an array, it will expect an array as the `response` from the handler and\nwrap each of its elements into a response object.\n\nIf the response is not an array or its length is different from the `event`'s length, it will throw an Error.\n\n```js\nconst middy = require('@middy/core');\nconst { appSync } = require('middy-appsync');\n\n// event is an array\nconst doStuff = (event, context, callback) =\u003e {\n  callback(null, [\n    { title: 'Foo', content: 'Bar' },\n    { title: 'Bizz', content: 'Bazz' },\n  ]);\n};\n\nconst handler = middy(doStuff).use(appSync());\n\nmodule.exports = { handler };\n```\n\nWill output\n\n```js\n[\n  {\n    data: {\n      title: 'Foo',\n      content: 'Bar',\n    },\n  },\n  {\n    data: {\n      title: 'Bizz',\n      content: 'Bazz',\n    },\n  },\n];\n```\n\n## BatchInvoke error handling\n\nJust like for normal handlers, throwing an `AppSyncError` or returning it as the result of your Lambda, will generate an error. It is worth noting that, by doing so, the error will be replicated to **all** elements of the batch (making the full batch invalid).\n\nYou can also have granular control over which elements of the batch are valid or have errors. To do so, you can return an `AppSyncError` for the invalid elements in your batch.\n\nExample:\n\n```js\nconst middy = require('@middy/core');\nconst { appSync, AppSyncError } = require('middy-appsync');\n\nconst doStuff = (events) =\u003e {\n  return [\n    new AppSyncError('Post not found', 'NotFound'), // first element is Invalid\n    { title: 'Bizz', content: 'Bazz' }, // second element is valid\n  ];\n};\n\nconst handler = middy(doStuff).use(appSync());\n\nmodule.exports = { handler };\n```\n\nWill output\n\n```js\n[\n  {\n    errorMessage: 'Post not found',\n    errorType: 'NotFound',\n    data: null,\n    errorInfo: null,\n  },\n  {\n    data: {\n      title: 'Bizz',\n      content: 'Bazz',\n    },\n  },\n];\n```\n\nThis will result in an Error for the first element, and a valid response for the second one.\n\n# Generic Exceptions\n\nThis library also comes with Exceptions classes for common use cases:\n\n- `UnauthorizedException`\n- `NotFoundException`\n\n# Custom Exceptions\n\nYou can define your own custom Exceptions by extending the `AppSyncError` class.\n\n```ts\nexport class MyException extends AppSyncError {\n  constructor() {\n    super('This is my error', 'MyException');\n  }\n}\n```\n\n# Caveats\n\nAppSync currently does not implement the latest June2018 GraphQL specs for the [Errors](https://graphql.github.io/graphql-spec/June2018/#sec-Errors) entry.\n\nThis middleware sticks to AppSync's current implementation, using the `message`, `errorType`, `data` and `errorInfo`, entries.\nThere is an [open issue](https://github.com/aws/aws-appsync-community/issues/71) on AppSync for this.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbboure%2Fmiddy-appsync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbboure%2Fmiddy-appsync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbboure%2Fmiddy-appsync/lists"}