{"id":13626476,"url":"https://github.com/guerrerocarlos/graphql-lambda","last_synced_at":"2025-02-25T04:20:54.327Z","repository":{"id":57142052,"uuid":"349752224","full_name":"guerrerocarlos/graphql-lambda","owner":"guerrerocarlos","description":null,"archived":false,"fork":false,"pushed_at":"2022-03-07T18:06:53.000Z","size":106,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-07T21:36:45.769Z","etag":null,"topics":[],"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/guerrerocarlos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-03-20T14:46:30.000Z","updated_at":"2022-05-25T14:04:38.000Z","dependencies_parsed_at":"2022-09-05T18:51:11.729Z","dependency_job_id":null,"html_url":"https://github.com/guerrerocarlos/graphql-lambda","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guerrerocarlos%2Fgraphql-lambda","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guerrerocarlos%2Fgraphql-lambda/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guerrerocarlos%2Fgraphql-lambda/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guerrerocarlos%2Fgraphql-lambda/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/guerrerocarlos","download_url":"https://codeload.github.com/guerrerocarlos/graphql-lambda/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240600170,"owners_count":19827124,"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":[],"created_at":"2024-08-01T21:02:20.066Z","updated_at":"2025-02-25T04:20:54.302Z","avatar_url":"https://github.com/guerrerocarlos.png","language":"TypeScript","readme":"[![npm version](https://badge.fury.io/js/graphql-lambda.svg)](https://badge.fury.io/js/graphql-lambda)\n\nThis is a AWS Lambda integration of GraphQL Server with Subscriptions support.\n\nHeavily inspired on [aws-lambda-graphql](https://github.com/michalkvasnicak/aws-lambda-graphql) module.\n\nAlso based on Apollo Server (a community-maintained open-source GraphQL server that works with many Node.js HTTP server frameworks).\n\n```shell\nnpm install graphql-lambda\n```\n\n# Working Examples\n\n## Ready-to-deploy (`serverless deploy`) examples:\n\n-   [graphql-lambda-nexus-example](https://github.com/guerrerocarlos/graphql-lambda-nexus-example) uses [nexus](https://nexus.js.org/)\n-   [graphql-lambda-sdl-example](https://github.com/guerrerocarlos/graphql-lambda-sdl-example) uses GraphQL SDL\n\n# Deploying with AWS Serverless Framework\n\n## GraphQL SDL Example:\n\n### Define GraphQL Schemas\n\nFollowing the same GraphQL SDL setup as in [graphql-lambda-sdl-example](https://github.com/guerrerocarlos/graphql-lambda-sdl-example), define your Schemas and subscriptions in a file called `schema.ts`\n\n```ts\nimport { pubSub, withFilter } from \"../lambda\";\n\ntype MessageType = 'greeting' | 'test';\n\ntype Message = {\n  text: string;\n  type: MessageType;\n};\n\ntype SendMessageArgs = {\n  text: string;\n  type: MessageType;\n};\n\nexport const typeDefs = /* GraphQL */ `\n  enum MessageType {\n    greeting\n    test\n  }\n  type Message {\n    id: ID!\n    text: String!\n    type: MessageType!\n  }\n  type Mutation {\n    sendMessage(text: String!, type: MessageType = greeting): Message!\n  }\n  type Query {\n    serverTime: Float!\n  }\n  type Subscription {\n    messageFeed(type: MessageType): Message!\n  }\n`;\n\nexport const resolvers = {\n  Mutation: {\n    async sendMessage(ctx: any, { text, type }: SendMessageArgs) {\n      const payload: Message = { text, type };\n\n      await pubSub.publish('NEW_MESSAGE', payload);\n\n      return payload;\n    },\n  },\n  Query: {\n    serverTime: () =\u003e Date.now(),\n  },\n  Subscription: {\n    messageFeed: {\n      resolve: (rootValue: Message) =\u003e {\n        // root value is the payload from sendMessage mutation\n        return rootValue;\n      },\n      subscribe: withFilter(\n        pubSub.subscribe('NEW_MESSAGE'),\n        (rootValue: Message, args: { type: null | MessageType }) =\u003e {\n          // this can be async too :)\n          if (args.type == null) {\n            return true;\n          }\n\n          return args.type === rootValue.type;\n        },\n      ),\n    },\n  },\n};\n```\n\n### Initialize all components for Subscriptions to work with AWS API Gateway V2 Websockets:\n\nIn a file named `lambda.ts` place the following code:\n\n```ts\nimport { APIGatewayProxyEvent } from \"aws-lambda\";\nimport {\n  PubSub,\n  SubscriptionManager,\n  ApiGatewayConnectionManager,\n  ISubscriptionEvent,\n  IEventStore,\n  APIGatewayWebSocketEvent,\n} from \"graphql-lambda\";\n\nimport { SQS } from \"aws-sdk\";\n\nexport class SQSQueue implements IEventStore {\n  public events: ISubscriptionEvent[];\n  private sqs: SQS;\n\n  constructor() {\n    this.sqs = new SQS();\n  }\n\n  publish = async (event: ISubscriptionEvent | APIGatewayProxyEvent | APIGatewayWebSocketEvent): Promise\u003cvoid\u003e =\u003e {\n    var params = {\n      QueueUrl: process.env.sqsfifo,\n      MessageGroupId: \"0\",\n      MessageBody: JSON.stringify(event),\n    };\n    await this.sqs.sendMessage(params).promise();\n  };\n}\n\nexport const eventStore = new SQSQueue();\nexport const pubSub = new PubSub({ eventStore });\nexport const subscriptionManager = new SubscriptionManager({\n  subscriptionManagerStorage: new Map(), // Replace this with any persistence layer you prefer, Redis, MySQL, etc.\n});\nexport const connectionManager = new ApiGatewayConnectionManager({\n  connectionManagerStorage: new Map(), // Replace this with any persistence layer you prefer, Redis, MySQL, etc.\n});\nexport const eventProcessor = new EventProcessor()\nexport * from \"graphql-lambda\";\n```\n\nFor production environments the `new Map()` should be replaced with a proper data storage like MySQL, Redis, DynamoDB, etc.\n\n### Server Creation\n\nProvide the schema (type definitions and resolvers) to the GraphQL Server, together with the `subscriptionsManager`, `connectionManager` and `eventProcessor` defined in `lambda.ts`. \n\nPlace the following code in a file named `graphql.ts`:\n\n```js\nimport {\n  Server,\n  subscriptionManager,\n  connectionManager,\n  EventProcessor,\n  APIGatewayWebSocketEvent,\n  eventStore,\n  eventProcessor,\n} from \"./lambda\";\n\nimport { typeDefs, resolvers } from \"./graphql/schema\";\n\nconst server = new Server({\n  typeDefs,  // (schema-first approach)\n  resolvers, // (schema-first approach)\n  // schema, // (code-first approach)\n  connectionManager,\n  eventProcessor,\n  subscriptionManager,\n\n  onError: (err) =\u003e {\n    console.log(err);\n  },\n  playground: {\n    endpoint: `/${process.env.STAGE}/graphql`,\n    subscriptionEndpoint: process.env.lambdaSubscriptionEndpoint,\n  },\n});\n\nexport const handleHttp = server.createHttpHandler();\nexport const handleWebSocket = server.createWebSocketHandler(); // Required for subscriptions websockets\nexport const eventHandler = server.createEventHandler(); // Required for subscription events\n```\n\nOptionally, a `schema` parameter can be provided to the server instead of `typeDefs` and `resolvers`.\n\n### Separate subscriptions events into separate function through SQS (for memory persistence)\n\nTo keep this example simple and make it work without any kind of Database, we will make all graphql subscriptions events to be handled by a separated function, so we can just store everything in memory, and make sure only one function handles all the subscription-related events by piping all events through a AWS SQS FIFO Queue:\n\nIn a file named `handlers.ts`, place the following code:\n\n```ts\nimport { handleHttp, handleWebSocket, eventHandler } from \"./graphql\"\n\nexport async function handleGraphql(\n  event: APIGatewayEvent | APIGatewayWebSocketEvent,\n  context\n) {\n  if (\n    (event as APIGatewayWebSocketEvent).requestContext != null \u0026\u0026\n    (event as APIGatewayWebSocketEvent).requestContext.routeKey != null\n  ) {\n    await eventStore.publish(event);\n\n    let result = {\n      body: \"\",\n      headers: event.headers?.[\"Sec-WebSocket-Protocol\"]?.includes(\"graphql-ws\")\n        ? {\n            \"Sec-WebSocket-Protocol\": \"graphql-ws\",\n          }\n        : undefined,\n      statusCode: 200,\n    };\n\n    return result;\n  } else if (\n    (event as APIGatewayEvent).requestContext != null \u0026\u0026\n    (event as APIGatewayEvent).requestContext.path != null\n  ) {\n    return handleHttp(event as APIGatewayEvent, context);\n  } else {\n    throw new Error(\"Invalid event\");\n  }\n}\n\nexport async function handleGraphqlSubscriptions(event: SQSEvent, context) {\n  if ((event as SQSEvent).Records != null) {\n    for (const record of (event as SQSEvent).Records) {\n      const ev = JSON.parse(record.body);\n      if (\n        (ev as APIGatewayWebSocketEvent).requestContext != null \u0026\u0026\n        (ev as APIGatewayWebSocketEvent).requestContext.routeKey != null\n      ) {\n        await handleWebSocket(ev as APIGatewayWebSocketEvent, context);\n      } else {\n        await eventHandler(ev, context);\n      }\n    }\n  } else {\n    throw new Error(\"Invalid event\");\n  }\n}\n```\n\nFinally, this [serverless](https://serverless.com/) project requires some custom resources.\n\nIn a file named `serverless.ts` (replacing the usual serverless.yml file), place the following code:\n\n```ts\nimport type { AWS } from \"@serverless/typescript\";\n\nconst serverlessConfiguration: AWS = {\n  service: \"graphql-lambda-example\",\n  plugins: [\"serverless-webpack\"],\n  provider: {\n    apiGateway: {\n      shouldStartNameWithService: true,\n    },\n    name: \"aws\",\n    runtime: \"nodejs12.x\",\n    region: \"us-east-1\",\n    memorySize: 512,\n    iam: {\n      role: {\n        statements: [\n          {\n            Effect: \"Allow\",\n            Action: [\"lambda:InvokeFunction\"],\n            Resource: \"*\",\n          },\n          {\n            Effect: \"Allow\",\n            Action: [\"sqs:*\"],\n            Resource: \"*\",\n          },\n        ],\n      },\n    },\n    environment: {\n      STAGE: \"${opt:stage, self:provider.stage}\",\n      sqsfifo: {\n        \"Fn::Join\": [\n          \"\",\n          [\n            \"https://sqs.\",\n            { Ref: \"AWS::Region\" },\n            \".amazonaws.com/\",\n            { Ref: \"AWS::AccountId\" },\n            \"/${self:custom.eventSQSFifo}\",\n          ],\n        ],\n      },\n      lambdaSubscriptionEndpoint: {\n        \"Fn::Join\": [\n          \"\",\n          [\n            \"wss://\",\n            {\n              Ref: \"WebsocketsApi\",\n            },\n            \".execute-api.\",\n            {\n              Ref: \"AWS::Region\",\n            },\n            \".\",\n            {\n              Ref: \"AWS::URLSuffix\",\n            },\n            \"/${self:provider.stage}\",\n          ],\n        ],\n      },\n    },\n  },\n  functions: {\n    main: {\n      handler: \"src/handlers.handleGraphql\",\n      events: [\n        {\n          http: {\n            path: \"/graphql\",\n            method: \"ANY\",\n          },\n        },\n        {\n          websocket: {\n            route: \"$connect\",\n          },\n        },\n        {\n          websocket: {\n            route: \"$default\",\n          },\n        },\n        {\n          websocket: {\n            route: \"$disconnect\",\n          },\n        },\n      ],\n    },\n    queue: {\n      handler: \"src/handlers.handleGraphqlSubscriptions\",\n      events: [\n        {\n          sqs: {\n            arn: {\n              \"Fn::GetAtt\": [\"sqsfifo\", \"Arn\"],\n            },\n          },\n        },\n      ],\n    },\n  },\n  custom: {\n    stage: \"${opt:stage, self:provider.stage}\",\n    region: \"${opt:region, self:provider.region}\",\n    eventSQSFifo: \"${self:service}-sqs-fifo-${self:custom.stage}.fifo\",\n    webpack: {\n      webpackConfig: \"./webpack.config.js\",\n      includeModules: true,\n    },\n  },\n  resources: {\n    Resources: {\n      sqsfifo: {\n        Properties: {\n          QueueName: \"${self:custom.eventSQSFifo}\",\n          FifoQueue: true,\n          ContentBasedDeduplication: true,\n        },\n        Type: \"AWS::SQS::Queue\",\n      },\n    },\n  },\n};\n\nmodule.exports = serverlessConfiguration;\n```\n\n### Deploy!\n\nDeploy to AWS Lambda and start testing!\n\n```sh\nserverless deploy\n```\n\n### Example Playground Screenshots\n\nQuery:\n\n![Screenshot 2021-03-20 at 4 06 47 PM](https://user-images.githubusercontent.com/82532/111876407-87b7c700-8996-11eb-9f60-3469f4559d21.jpg)\n\nSubscription:\n\n![Screenshot 2021-03-20 at 4 06 31 PM](https://user-images.githubusercontent.com/82532/111876419-93a38900-8996-11eb-8282-74a5860030fa.jpg)\n\nMutation:\n\n![Screenshot 2021-03-20 at 4 06 19 PM](https://user-images.githubusercontent.com/82532/111876437-a3bb6880-8996-11eb-98b4-7f30bac0bce7.jpg)\n","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguerrerocarlos%2Fgraphql-lambda","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguerrerocarlos%2Fgraphql-lambda","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguerrerocarlos%2Fgraphql-lambda/lists"}