aws-lambda-graphql0.13.0
Use Apollo Server Lambda with GraphQL subscriptions over WebSocket (AWS API Gateway v2).
With this library you can do:
In this quick example we're going to build a simple broadcasting server that broadcasts messages received using broadcastMessage
mutation to all subscribed connections.
Skip to final implementation if you don't need a step by step guide
First we need to install dependencies:
yarn add aws-lambda-graphql graphql graphql-subscriptions aws-sdk
# or
npm install aws-lambda-graphql graphql graphql-subscriptions aws-sdk
Note that aws-sdk
is required only for local development, it's provided by the AWS Lambda by default when you deploy the app
Now we have all the dependencies installed so lets start with server implementation.
Our GraphQL server needs to know how to store connections and subscriptions because Lambdas are stateless. In order to do that we need create instances of the Connection manager and Subscription manager. We have two options of persistent storage for our connections and subscriptions.
DynamoDB:
import {
DynamoDBConnectionManager,
DynamoDBSubscriptionManager,
} from 'aws-lambda-graphql';
/*
By default subscriptions and connections use TTL of 2 hours.
This can be changed by `ttl` option in DynamoDBSubscriptionManager and DynamoDBConnectionManager.
ttl accepts a number in seconds (default is 7200 seconds) or
false to turn it off.
It's your responsibility to set up TTL on your connections and subscriptions tables.
*/
const subscriptionManager = new DynamoDBSubscriptionManager();
const connectionManager = new DynamoDBConnectionManager({
subscriptions: subscriptionManager,
});
ttl
field in Connections, Subscriptions and SubscriptionOperations tables. You can turn off the TTL by setting up ttl
option to false
in DynamoDBSubscriptionManager
and DynamoDBConnectionManager
.
Redis:
import {
RedisConnectionManager,
RedisSubscriptionManager,
} from 'aws-lambda-graphql';
import Redis from 'ioredis';
const redisClient = new Redis({
port: 6379, // Redis port
host: '127.0.0.1', // Redis host
});
const subscriptionManager = new RedisSubscriptionManager({
redisClient,
});
const connectionManager = new RedisConnectionManager({
subscriptionManager,
redisClient,
});
In order to be able to broadcast messages (publish events) we need an Event store. Because our server can received a lot of messages we need to work with events in async, meaning that the actual events are not published directly from mutation but rather they are stored in underlying data store which works as an event source for our server. Because we decided to use DynamoDB as our persistent store, we are goint to use it as our event source.
import {
DynamoDBConnectionManager,
DynamoDBEventStore,
DynamoDBSubscriptionManager,
} from 'aws-lambda-graphql';
/*
By default event stores uses TTL of 2 hours on every event.
This can be changed by `ttl` option in DynamoDBEventStore.
ttl accepts a number in seconds (default is 7200 seconds) or
false to turn it off.
It's your responsibility to set up TTL on your events table.
*/
const eventStore = new DynamoDBEventStore();
const subscriptionManager = new DynamoDBSubscriptionManager();
const connectionManager = new DynamoDBConnectionManager({
subscriptionManager,
});
That's it for now. Our eventStore
will use DynamoDB to store messages that we want to broadcast to all subscribed clients.
ttl
field in Events store table. This can be turned off by setting up the ttl
option to false
.
Our server needs a GraphQL schema. So we'll create one.
import {
DynamoDBConnectionManager,
DynamoDBEventStore,
DynamoDBSubscriptionManager,
} from 'aws-lambda-graphql';
const eventStore = new DynamoDBEventStore();
const subscriptionManager = new DynamoDBSubscriptionManager();
const connectionManager = new DynamoDBConnectionManager({
subscriptionManager,
});
const typeDefs = /* GraphQL */ `
type Mutation {
broadcastMessage(message: String!): String!
}
type Query {
"""
Dummy query so out server won't fail during instantiation
"""
dummy: String!
}
type Subscription {
messageBroadcast: String!
}
`;
From given schema we already see that we need to somehow publish and process broadcasted message. For that purpose we must create a PubSub instance that uses our DynamoDB event store as underlying storage for events.
PubSub is responsible for publishing events and subscribing to events. Anyone can broadcast message using broadcastMessage
mutation (publish) and anyone connected over WebSocket can subscribed to messageBroadcast
subscription (subscribing) to receive broadcasted messages.
PubSub
serializes event payload to JSON. If you don't want this behaviour, set serializeEventPayload
option to false
on your PubSub
instance.
import {
DynamoDBConnectionManager,
DynamoDBEventStore,
DynamoDBSubscriptionManager,
PubSub,
} from 'aws-lambda-graphql';
const eventStore = new DynamoDBEventStore();
const subscriptionManager = new DynamoDBSubscriptionManager();
const connectionManager = new DynamoDBConnectionManager({
subscriptionManager,
});
const pubSub = new PubSub({
eventStore,
// optional, if you don't want to store messages to your store as JSON
// serializeEventPayload: false,
});
const typeDefs = /* GraphQL */ `
type Mutation {
broadcastMessage(message: String!): String!
}
type Query {
"""
Dummy query so out server won't fail during instantiation
"""
dummy: String!
}
type Subscription {
messageBroadcast: String!
}
`;
const resolvers = {
Mutation: {
broadcastMessage: async (root, { message }) => {
await pubSub.publish('NEW_MESSAGE', { message });
return message;
},
},
Query: {
dummy: () => 'dummy',
},
Subscription: {
messageBroadcast: {
// rootValue is same as the event published above ({ message: string })
// but our subscription should return just a string, so we're going to use resolve
// to extract message from an event
resolve: (rootValue) => rootValue.message,
subscribe: pubSub.subscribe('NEW_MESSAGE'),
},
},
};
Our GraphQL schema is now finished. Now we can instantiate the server so we can actually process HTTP and WebSocket events received by our Lambda server and send messages to subscribed clients.
In order to send messages to subscribed clients we need the last piece and it is a Event processor. Event processor is responsible for processing events published to our Event store and sending them to all connections that are subscribed for given event.
Because we use DynamoDB as an event store, we are going to use DynamoDBEventProcessor.
import {
DynamoDBConnectionManager,
DynamoDBEventProcessor,
DynamoDBEventStore,
DynamoDBSubscriptionManager,
PubSub,
Server,
} from 'aws-lambda-graphql';
const eventStore = new DynamoDBEventStore();
const eventProcessor = new DynamoDBEventProcessor();
const subscriptionManager = new DynamoDBSubscriptionManager();
const connectionManager = new DynamoDBConnectionManager({
subscriptions: subscriptionManager,
});
const pubSub = new PubSub({ eventStore });
const typeDefs = /* GraphQL */ `
type Mutation {
broadcastMessage(message: String!): String!
}
type Query {
"""
Dummy query so out server won't fail during instantiation
"""
dummy: String!
}
type Subscription {
messageBroadcast: String!
}
`;
const resolvers = {
Mutation: {
broadcastMessage: async (root, { message }) => {
await pubSub.publish('NEW_MESSAGE', { message });
return message;
},
},
Query: {
dummy: () => 'dummy',
},
Subscription: {
messageBroadcast: {
// rootValue is same as the event published above ({ message: string })
// but our subscription should return just a string, so we're going to use resolve
// to extract message from an event
resolve: (rootValue) => rootValue.message,
subscribe: pubSub.subscribe('NEW_MESSAGE'),
},
},
};
const server = new Server({
// accepts all the apollo-server-lambda options and adds few extra options
// provided by this package
connectionManager,
eventProcessor,
resolvers,
subscriptionManager,
typeDefs,
});
export const handleWebSocket = server.createWebSocketHandler();
export const handleHTTP = server.createHttpHandler();
// this creates dynamodb event handler so we can send messages to subscribed clients
export const handleEvents = server.createEventHandler();
Now our server is finished.
You need to map:
handleWebSocket
handlerhandleHTTP
handleEvents
.In order to do that you can use Serverless framework, see serverless.yml
file.
To connect to this server you can use Apollo Client + subscriptions-transport-ws
- see example in section 2
Sometime if you have complex schema you want to pass dependencies using context so it's easier to manage.
import {
DynamoDBConnectionManager,
DynamoDBEventProcessor,
DynamoDBEventStore,
DynamoDBSubscriptionManager,
PubSub,
Server,
} from 'aws-lambda-graphql';
const eventStore = new DynamoDBEventStore();
const eventProcessor = new DynamoDBEventProcessor();
const subscriptionManager = new DynamoDBSubscriptionManager();
const connectionManager = new DynamoDBConnectionManager({
subscriptions: subscriptionManager,
});
const pubSub = new PubSub({ eventStore });
const typeDefs = /* GraphQL */ `
type Mutation {
broadcastMessage(message: String!): String!
}
type Query {
"""
Dummy query so out server won't fail during instantiation
"""
dummy: String!
}
type Subscription {
messageBroadcast: String!
}
`;
const resolvers = {
Mutation: {
broadcastMessage: async (root, { message }, ctx) => {
await ctx.pubSub.publish('NEW_MESSAGE', { message });
return message;
},
},
Query: {
dummy: () => 'dummy',
},
Subscription: {
messageBroadcast: {
resolve: (rootValue) => rootValue.message,
// subscribe works similarly as resolve in any other GraphQL type
// pubSub.subscribe() returns a resolver so we need to pass it all the args as were received
// so we can be sure that everything works correctly internally
subscribe: (rootValue, args, ctx, info) => {
return ctx.pubSub.subscribe('NEW_MESSAGE')(rootValue, args, ctx, info);
},
},
},
};
const server = new Server({
// accepts all the apollo-server-lambda options and adds few extra options
// provided by this package
context: {
pubSub,
},
connectionManager,
eventProcessor,
resolvers,
subscriptionManager,
typeDefs,
});
export const handleWebSocket = server.createWebSocketHandler();
export const handleHTTP = server.createHttpHandler();
// this creates dynamodb event handler so we can send messages to subscribed clients
export const handleEvents = server.createEventHandler();
subscriptions-transport-ws
First install dependencies
yarn add @apollo/client @apollo/link-ws subscriptions-transport-ws
# or
npm install @apollo/client @apollo/link-ws subscriptions-transport-ws
import { WebSocketLink } from '@apollo/link-ws';
import { SubscriptionClient } from 'subscriptions-transport-ws';
import { ApolloProvider, ApolloClient, InMemoryCache } from '@apollo/client';
const wsClient = new SubscriptionClient(
'ws://localhost:8000', // please provide the uri of the api gateway v2 endpoint
{ lazy: true, reconnect: true },
null,
[],
);
const link = new WebSocketLink(wsClient);
const client = new ApolloClient({
cache: new InMemoryCache(),
link,
});
To deploy your api created using this library please see serverless.yml
template of example app.
This library supports serverless-offline. But you have to do small changes in your code to actually support it, it's not automatical.
You need to set up custom endpoint for ApiGatewayManagementApi and custom endpoint for DynamoDB if you're using it. Please refer to chat-example-server
source code.
GraphQL client and server implementation for AWS Lambda.
See package
Current infrastructure is implemented using AWS Lambda + AWS API Gateway v2 + AWS DynamoDB (with DynamoDB streams). But it can be implemented using various solutions (Kinesis, etc) because it's written to be modular.
serverless.yml
file for easy deploymentyarn.lock
is commited.Unreleased
section in your PR.yarn test
# running tests in watch mode
yarn test:watch
yarn build
Running tests locally:
yarn test
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!
MIT
场景:现在需要开发一个前后端分离的应用,后端采用 RESTful API 最为方便,但是如果这个后端服务会在一天中的某些时候有高并发的情况,使用什么样的架构最为简单呢? 刚思考这个问题的时候我想到的解决方案可能有以下几种: 使用CDN内容分发网络,减少主服务器的压力 使用LVS服务器负载均衡 使用缓存 硬件层 提高带宽,使用SSD 硬盘,使用更好的服务器 代码层,优化代码(使用性能更好的语言等
Lambda 简介 AWS Lambda 是一项计算服务,可使您无需预配置或管理服务器即可运行代码。AWS Lambda 只在需要时执行您的代码并自动缩放,从每天几个请求到每秒数千个请求。您只需按消耗的计算时间付费 – 代码未运行时不产生费用。借助 AWS Lambda,您几乎可以为任何类型的应用程序或后端服务运行代码,而且无需执行任何管理。AWS Lambda 在可用性高的计算基础设施上运行您的
aws lambda The purpose of this article is to present the most relevant details and not-so-straight steps to create/use the two important services in Amazon Web Services – AWS API Gateway and AWS Lambd
简介 Lambda 是 AWS 提供的一个无服务器架构的服务,用户只需要专注于写代码而不用关心部署,通常需要关心外部事件源,可以与 API Gateway, SNS, S3, DynamoDB 等等配合使用。 本质 简单粗暴地说,一个 Lambda 就是一个 Docker 服务或者 Firecracker 服务,Firecracker 是 AWS 开源的一个类似 Docker 的服务,AWS 计划
AWS Serverless with Claudia.js 最近项目架构升级,迁移到AWS,使用了AWS Serverless 架构 Lambda,项目使用了Claudia做项目自动部署。 Claudia可以轻松地将Node.js项目部署到AWS Lambda和API Gateway。它可以自动执行所有容易出错的部署和配置任务,并将所有内容设置为JavaScript开发人员开箱即用的方式。这意味
一般性问题 问:什么是 AWS Lambda? 通过 AWS Lambda,无需配置或管理服务器即可运行代码。您只需按消耗的计算时间付费 – 代码未运行时不产生费用。借助 Lambda,您几乎可以为任何类型的应用程序或后端服务运行代码,而且全部无需管理。只需上传您的代码,Lambda 会处理运行和扩展高可用性代码所需的一切工作。您可以将您的代码设置为自动从其他 AWS 服务触发,或者直接从任何 W
1.新建lamda function,选择python,使用代码如下,之后save,然后action下拉框依次执行publish和alias import json print('---Loading function---') def lambda_handler(event, context): # print("Received event: " + json.dumps(ev
是否有可能将AWS Lambda与Apache Kafka集成在一起?我想在lambda函数中放一个消费者。当使用者收到消息时,lambda函数执行。
我很难使用基于Java的Lambda函数设置来接收来自SNS的消息。我的函数如下所示: 它编译得很好,我将jar文件上传到Lambda(通过网络控制台)没有任何问题。 但是,当我使用代表SNSEvent模型的JSON发布到它(通过SNS发布到订阅的Lambda函数)时,Lambda函数抛出以下异常: 在类com上加载方法处理程序时出错。我的公司。LambdaHandler:java类。NoClas
在dynamoDB更新中调用lambda函数时,我遇到了问题。我已经检查了AWS Lambda:ClassNotFoundException和AWS Lambda NoClassDefFoundError,但没有成功。 我写这个lambda函数调用时,有任何更新的发电机。我按照这个教程。https://docs.aws.amazon.com/lambda/latest/dg/with-dynamo
playwright-aws-lambda Support for Playwright running on AWS Lambda and Google Cloud Functions. NOTE: Currently only Chromium is supported. Install npm install playwright-core playwright-aws-lambda --s
aws-lambda-image An AWS Lambda Function to resize/reduce images automatically. When an image isput on AWS S3 bucket, this package will resize/reduce it and put to S3. Requirements Node.js ( AWS Lambda
AWS Lambda C++ Runtime C++ implementation of the lambda runtime API Design Goals Negligible cold-start overhead (single digit millisecond). Freedom of choice in compilers, build platforms and C standa