Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation site #116

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitbook.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root: ./docs/
17 changes: 17 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
title: Introduction
description: Serverless GraphQL subscriptions over WebSocket using AWS API Gateway v2 and AWS Lambda.
---

# Introduction

Fully featured [Apollo Server Lambda](https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-lambda) compatible GraphQL server with GraphQL subscriptions over WebSocket support.

Build real-time serverless AWS Lambda GraphQL services with WebSocket support over AWS API Gateway v2 or extend your existing HTTP GraphQL service with real-time capabilities.

## Features

- GraphQL subscriptions support using AWS API Gateway v2
- HTTP GraphQL operations using AWS API Gateway v1 with [apollo-server-lambda](https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-lambda) compatibility

![](./awsgql.gif 'Build real-time serverless GraphQL applications')
33 changes: 33 additions & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
- [Introduction](./README.md)

## Getting Started

- [Installation](./getting-started/00-installation.md)
- [First service](./getting-started/01-first-service.md)
- [Connection and subscription management](./getting-started/02-connection-and-subscription-management.md)
- [Event store](./getting-started/03-event-store.md)
- [GraphQL Schema](./getting-started/04-graphql-schema.md)
- [PubSub](./getting-started/05-pubsub.md)
- [WebSocket/HTTP and event processing handlers](./getting-started/06-event-handlers.md)
- [Deploy](./getting-started/07-deploy.md)
- [Test](./getting-started/08-test.md)

## Advanced

- [How it works](./advanced/how-it-works.md)
- [Serverless Support](./advanced/serverless-support.md)
- [PubSub instance in GraphQL context](./advanced/pub-sub-in-graphql-context.md)

## API

- [DynamoDBConnectionManager](./api/DynamoDBConnectionManager.md)
- [DynamoDBEventProcessor](./api/DynamoDBEventProcessor.md)
- [DynamoDBEventStore](./api/DynamoDBEventStore.md)
- [DynamoDBSubscriptionManager](./api/DynamoDBSubscriptionManager.md)
- [MemoryEventProcessor](./api/MemoryEventProcessor.md)
- [MemoryEventStore](./api/MemoryEventStore.md)
- [MemorySubscriptionManager](./api/MemorySubscriptionManager.md)
- [PubSub](./api/PubSub.md)
- [RedisConnectionManager](./api/RedisConnectionManager.md)
- [RedisSubscriptionManager](./api/RedisSubscriptionManager.md)
- [WebSocketConnectionManager](./api/WebSocketConnectionManager.md)
17 changes: 17 additions & 0 deletions docs/getting-started/00-installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
title: Installation
---

# Installation

```console
yarn add aws-lambda-graphql graphql graphql-subscriptions aws-sdk
# or
npm install aws-lambda-graphql graphql graphql-subscriptions aws-sdk
```

{% hint style="info" %}
Note that `aws-sdk` is required only for local development, it's available in AWS Lambda environment by default when you deploy the app.
{% endhint %}

Now when we've our dependencies in place, we can go through our first real-time GraphQL service.
10 changes: 10 additions & 0 deletions docs/getting-started/01-first-service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
title: First service
description: How to implement your first real-time serverless GraphQL service.
---

# First service

Now we'll introduce a few concepts so you can understand how the GraphQL subscriptions over API Gateway v2 work. To do that we're going to implement a simple GraphQL service that broadcasts messages to subscribed clients.

We're going to slowly walk through concepts which are necessary for any real-time GraphQL application in next sections.
34 changes: 34 additions & 0 deletions docs/getting-started/02-connection-and-subscription-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
title: 'Connection and subscription management'
description: How to set up connection and subscription management.
---

# Connection and subscription management

Our GraphQL server needs to know what subscriptions are clients subscribed to and how to communicate with those clients. In order to do that the server uses [Connection manager](../../packages/aws-lambda-graphql/src/types/connections.ts) and [Subscription manager](../../packages/aws-lambda-graphql/src/types/subscriptions.ts).

## Connection manager

The purpose of Connection manager is to register or unregister all the connections which have connected to your service over AWS API Gateway v2.

## Subscription manager

Each client that subscribes to some GraphQL subscription is managed by Suscription manager. Subscription manager stores the information about connection and GraphQL document used to subscribe to the operation by the given connection.

## Put together

We'll use [`DynamoDBConnectionManager`](../api/DynamoDBConnectionManager.md) and [`DynamoDBSubscriptionManager`](../api/DynamoDBSubscriptionManager.md) to manage connections and subscriptions.

```js
import {
DynamoDBConnectionManager,
DynamoDBSubscriptionManager,
} from 'aws-lambda-graphql';

const subscriptionManager = new DynamoDBSubscriptionManager();
const connectionManager = new DynamoDBConnectionManager({
subscriptionManager,
});
```

In next section we're going to create an event store so we can store published messages and process them later.
30 changes: 30 additions & 0 deletions docs/getting-started/03-event-store.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: Event store
description: How to set up an event store so we can process events published by connected clients.
---

# Event store

Each client can publish messages that can be processed and sent to subscribed clients.

[Event store](../../packages/aws-lambda-graphql/src/types/events.md) is responsible for storing those events to storage that can be used as an event source for our event processing handler.

## Put together

We'll use [`DynamoDBEventStore`](../api/DynamoDBEventStore.md) that stores the events in DynamoDB table which will be used as an event source for our event processing lambda by leveraging DynamoDB streams.

```js
import {
DynamoDBConnectionManager,
DynamoDBEventStore,
DynamoDBSubscriptionManager,
} from 'aws-lambda-graphql';

const eventStore = new DynamoDBEventStore();
const subscriptionManager = new DynamoDBSubscriptionManager();
const connectionManager = new DynamoDBConnectionManager({
subscriptionManager,
});
```

That's it for now. Our `eventStore` will be used to publish messages to all subscribed clients. In next section we'll create a simple GraphQL schema for our service.
43 changes: 43 additions & 0 deletions docs/getting-started/04-graphql-schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
title: GraphQL schema
description: Define GraphQL schema for your first service.
---

# GraphQL schema

Our service needs a GraphQL schema, so we'll create one. Each client can subscribe to broadcast by `messageBroadcast` GraphQL subscription and is able to broadcast any message to all clients event itself by `broadcastMessage` GraphQL mutation.

## Put together

```js
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!
}
`;
```

In next section we'll create a PubSub instance and give the schema a logic so client can subscribe to messages or broadcast them.
80 changes: 80 additions & 0 deletions docs/getting-started/05-pubsub.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
title: PubSub
description: How to create PubSub instance in order to send and subscribe to messages.
---

# PubSub

Any GraphQL service that wants to offer GraphQL subscriptions needs to use [PubSub](../api/PubSub.md) in order to publish and subscribe to messages (events). PubSub uses [Event store](./03-event-store.md) to store published messages. They are later picked up from event source by [Event processor](./06-event-handlers.md).

## Put together

Let's import PubSub and connect it to [Event store](./03-event-store.md) we created before. Then we use the `pubSub` instance in our GraphQL schema resolvers.

```js
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'),
},
},
};
```

{% hint style="info" %}
⚠️ Be careful! By default `PubSub` serializes event payload to JSON. If you don't want this behaviour, set `serializeEventPayload` to `false` on your `PubSub` instance.
{% endhint %}

Now our schema is ready, so let's create a GraphQL server and expose it's handlers to AWS Lambda in the next section.
94 changes: 94 additions & 0 deletions docs/getting-started/06-event-handlers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
title: Event handlers
description: How to expose AWS Lambda handlers for our real-time GraphQL service.
---

# Event handlers

AWS Lambda needs a handler, function that processes an event received by our lambda function. Event handlers are exposed from [Server](../api/Server.md) by calling the create methods for HTTP, WebSocket or event processing handlers.

## Put together

We have functioning GraphQL schema with resolvers for `broadcastMessage` GraphQL mutation and `messageBroadcast` GraphQL subscription. We need to create a server that glues together all the pieces we created along our journey and exposes handlers:

- `handleWebSocket` for `AWS API Gateway v2` events
- `handleHTTP` for `AWS API Gateway v1` events
- `handeEvents` for `DynamoDB Streams` events

```js
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({
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();
```

Our server is finished 🎉. In next section we'll deploy our service.
Loading