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

feat(data): appsync apollo extensions #7930

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 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
3 changes: 3 additions & 0 deletions src/directory/directory.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,9 @@ export const directory = {
},
{
path: 'src/pages/[platform]/build-a-backend/data/manage-with-amplify-console/index.mdx'
},
{
path: 'src/pages/[platform]/build-a-backend/data/aws-appsync-apollo-extensions/index.mdx'
}
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
import { getCustomStaticPath } from '@/utils/getCustomStaticPath';

export const meta = {
title: 'AWS AppSync Apollo Extensions',
description:
'AWS AppSync Apollo Extensions',
platforms: [
'android',
'swift',
]
};

export function getStaticPaths() {
return getCustomStaticPath(meta.platforms);
}

export function getStaticProps(context) {
return {
props: {
platform: context.params.platform,
meta
}
};
}

AWS AppSync Apollo Extensions provides a seamless way to connect to your AWS AppSync while using the Apollo client. Apollo client is an open-source GraphQL client.
josefaidt marked this conversation as resolved.
Show resolved Hide resolved

<InlineFilter filters={["swift"]}>

To learn more about Apollo, see https://www.apollographql.com/docs/ios/.

</InlineFilter>


<InlineFilter filters={["android"]}>

To learn more about Apollo, see https://www.apollographql.com/docs/kotlin.

</InlineFilter>

## Features

AWS AppSync Apollo Extensions provide AWS AppSync authorizers to be used with the Apollo client to make it simple to configure runtime interceptors and apply the correct authorization payloads to your GraphQL operations.

The Amplify library provides components to facilitate configuring the authorizers with Apollo client by providing configuration values to connect to your Amplify Data backend.

<InlineFilter filters={["android"]}>

### Install the AWS AppSync Apollo library

Add `apollo-appsync` dependency to your app/build.gradle.kts file:

```kotlin title="app/build.gradle.kts"
dependencies {
// highlight-start
implementation("com.amplifyframework:apollo-appsync:1.0.0")
// highlight-end
}
```

</InlineFilter>

<InlineFilter filters={["swift"]}>
mattcreaser marked this conversation as resolved.
Show resolved Hide resolved

### Install the AWS AppSync Apollo library

Add AWS AppSync Apollo Extensions into your project using Swift Package Manager.

Enter its GitHub URL (`https://github.com/aws-amplify/aws-appsync-apollo-extensions-swift`), select **Up to Next Major Version** and click **Add Package**

* Select the following libraries:
* **AWSAppSyncApolloExtensions**

</InlineFilter>

### Connecting to AWS AppSync with Apollo client

AWS AppSync supports the following [authorization modes](https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html)

#### API_KEY

<InlineFilter filters={["swift"]}>

```swift
import AWSAppSyncApolloExtensions

let authorizer = APIKeyAuthorizer(apiKey: "[API_KEY]")
let interceptor = AppSyncInterceptor(authorizer)
```

</InlineFilter>

<InlineFilter filters={["android"]}>

```kotlin
val authorizer = ApiKeyAuthorizer("[API_KEY]")
val interceptor = AppSyncInterceptor(authorizer)
```

</InlineFilter>

#### AMAZON_COGNITO_USER_POOLS

<InlineFilter filters={["swift"]}>

If you are using Amplify Auth, you can create a method that retrieves the Cognito access token

```swift
import Amplify

func getUserPoolAccessToken() async throws -> String {
let authSession = try await Amplify.Auth.fetchAuthSession()
if let result = (authSession as? AuthCognitoTokensProvider)?.getCognitoTokens() {
switch result {
case .success(let tokens):
return tokens.accessToken
case .failure(let error):
throw error
}
}
throw AuthError.unknown("Did not receive a valid response from fetchAuthSession for get token.")
}
```

Then create the AuthTokenAuthorizer with this method.

```swift
import AWSAppSyncApolloExtensions

let authorizer = AuthTokenAuthorizer(fetchLatestAuthToken: getUserPoolAccessToken)
let interceptor = AppSyncInterceptor(authorizer)
```
</InlineFilter>

<InlineFilter filters={["android"]}>

Create the AuthTokenAuthorizer with this method.

```kotlin
val authorizer = AuthTokenAuthorizer { ApolloAmplifyConnector.fetchLatestCognitoAuthToken() }
```

</InlineFilter>

You can provide your own custom `fetchLatestAuthToken` provider for **AWS_LAMBDA** and **OPENID_CONNECT** auth modes.

#### AWS_IAM

<InlineFilter filters={["swift"]}>

If you are using Amplify Auth, you can use the following method for AWS_IAM auth

```swift
import AWSCognitoAuthPlugin
import AWSAppSyncApolloExtensions

let authorizer = IAMAuthorizer(
signRequest: AWSCognitoAuthPlugin.createAppSyncSigner(
region: "[REGION]"))
```

</InlineFilter>

<InlineFilter filters={["android"]}>

If you are using Amplify Auth, you can use the following method for AWS_IAM auth

```kotlin
val authorizer = IamAuthorizer { ApolloAmplifyConnector.signAppSyncRequest(it, "us-east-1") }
```

</InlineFilter>

### Connecting Amplify Data to Apollo client

Before you begin, you will need an Amplify Data backend deploy. To get started, see [Set up Data](/[platform]/build-a-backend/data/set-up-data/).

Once you have deployed your backend and created the `amplify_outputs.json` file, you can use Amplify library to read and retrieve your configuration values with the following steps:

<InlineFilter filters={["swift"]}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need Android version of this section

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the Android section, which is

  1. the gradle for the second package, could you update to the final name?
  2. a code example , need to add this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated dependency name

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated code snippet


1. Enter its GitHub URL (`https://github.com/aws-amplify/amplify-swift`), select **Up to Next Major Version** and click **Add Package**
2. Select the following libraries:
1. **AWSPluginsCore**
3. Drag and drop the `amplify_outputs.json` file into your Xcode project.
4. Initialize the configuration with `try AWSAppSyncConfiguration(with: .amplifyOutputs)`

The resulting configuration object will have the `endpoint`, `region`, and optional `apiKey.` The following example shows reading the `amplify_outputs.json` file from the main bundle to instantiate the configuration and uses it to configure the Apollo client for **API_Key** authorization.

```swift
import Apollo
import ApolloAPI
import AWSPluginsCore
import AWSAppSyncApolloExtensions

func createApolloClient() throws -> ApolloClient {
let store = ApolloStore(cache: InMemoryNormalizedCache())

// 1. Read AWS AppSync API configuration from `amplify_outputs.json`
let configuration = try AWSAppSyncConfiguration(with: .amplifyOutputs)

// 2. Use `configuration.apiKey` with APIKeyAuthorizer
let authorizer = APIKeyAuthorizer(apiKey: configuration.apiKey ?? "")
let interceptor = AppSyncInterceptor(authorizer)
let interceptorProvider = DefaultPrependInterceptorProvider(interceptor: interceptor,
store: store)
// 3. Use `configuration.endpoint` with RequestChainNetworkTransport
let transport = RequestChainNetworkTransport(interceptorProvider: interceptorProvider,
endpointURL: configuration.endpoint)

return ApolloClient(networkTransport: transport, store: store)
}
```

</InlineFilter>

<InlineFilter filters={["android"]}>

Add `apollo-appsync-amplify` dependency to your app/build.gradle.kts file:

```kotlin title="app/build.gradle.kts"
dependencies {
// highlight-start
implementation("com.amplifyframework:apollo-appsync-amplify:1.0.0")
// highlight-end
}
```

```kotlin
// Use apiKey auth mode, reading configuration from AmplifyOutputs
val connector = ApolloAmplifyConnector(context, AmplifyOutputs(R.raw.amplify_outputs))
val apolloClient = ApolloClient.Builder()
.appSync(connector.endpoint, connector.apiKeyAuthorizer())
.build()
```

</InlineFilter>

Depending on your authorization strategy defined on your schema, you can use the corresponding Authorizer. To read more about the strategies and their corresponding auth modes, see [Available authorization strategies](/[platform]/build-a-backend/data/customize-authz/#available-authorization-strategies).


Some common ones are

* `publicAPIkey` strategy, `apiKey` authMode, **APIKeyAuthorizer**
* `guest` strategy, `identityPool` authMode, **IAMAuthorizer**
* `owner` strategy, `userPool` authMode, **AuthTokenAuthorizer**

If you define multiple authorization strategies on a single model, you will have to create separate Apollo client instances for each Authorizer that you want to use in your app.

### Downloading the AWS AppSync schema

The schema is used by Apollo’s code generation tool to generate API code that helps you execute GraphQL operations. The following steps integrate your AppSync schema with Apollo's code generation process:

<InlineFilter filters={["swift"]}>
1. Navigate to your API on the [AWS AppSync console](https://console.aws.amazon.com/appsync/home)
2. On the left side, select Schema
3. When viewing your schema, there should a “Export schema” drop down. Select this and download the `schema.json` file.
4. Add this file to your project as directed by [Apollo Code Generation documentation](https://www.apollographql.com/docs/ios/code-generation/introduction)

You can alternatively download the introspection schema using the [`fetch-schema`](https://www.apollographql.com/docs/ios/code-generation/codegen-cli#fetch-schema) command with the `amplify-ios-cli` tool.

</InlineFilter>

<InlineFilter filters={["android"]}>
1. Navigate to your API on the [AWS AppSync console](https://console.aws.amazon.com/appsync/home)
2. On the left side, select Schema
3. When viewing your schema, there should a “Export schema” drop down. Select this and download the `schema.json` file.
4. Add this file to your project as directed by [Apollo documentation](https://www.apollographql.com/docs/kotlin/advanced/plugin-recipes#specifying-the-schema-location)
</InlineFilter>

## Connecting to AWS AppSync real-time endpoint

The following example shows how you can create an Apollo client that allows performing GraphQL subscription operations with AWS AppSync.

<InlineFilter filters={["swift"]}>

```swift
import Apollo
import ApolloAPI
import ApolloWebSocket
import AWSPluginsCore
import AWSAppSyncApolloExtensions

func createApolloClient() throws -> ApolloClient {
let store = ApolloStore(cache: InMemoryNormalizedCache())
let configuration = try AWSAppSyncConfiguration(with: .amplifyOutputs)

// 1. Create your authorizer
let authorizer = /* your Authorizer */
let interceptor = AppSyncInterceptor(authorizer)

let interceptorProvider = DefaultPrependInterceptorProvider(interceptor: interceptor,
store: store)
let transport = RequestChainNetworkTransport(interceptorProvider: interceptorProvider,
endpointURL: configuration.endpoint)

// 2. Create the AWS AppSync compatible websocket client
let websocket = AppSyncWebSocketClient(endpointURL: configuration.endpoint,
authorizer: authorizer)
// 3. Add it to the WebSocketTransport
let webSocketTransport = WebSocketTransport(websocket: websocket)
// 4. Create a SplitNetworkTransport
let splitTransport = SplitNetworkTransport(
uploadingNetworkTransport: transport,
webSocketNetworkTransport: webSocketTransport
)
// 5. Pass the SplitNetworkTransport to the ApolloClient
return ApolloClient(networkTransport: splitTransport, store: store)
}
```

</InlineFilter>

<InlineFilter filters={["android"]}>

```kotlin
val endpoint = AppSyncEndpoint("<your_appsync_endpoint>")
val authorizer = /* your Authorizer */

val apolloClient = ApolloClient.Builder()
.appSync(endpoint, authorizer)
.build()
```

</InlineFilter>