generated from amazon-archives/__template_MIT-0
-
Notifications
You must be signed in to change notification settings - Fork 1k
New serverless pattern - appsync-private-api-sam #2367
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
Closed
Tessot
wants to merge
2
commits into
aws-samples:main
from
Tessot:tessot-featurev2-appsync-private-api-sam
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| # AWS AppSync Private API | ||
|
|
||
| This pattern shows how you can deploy an AWS AppSync Private API which can only be invoked by resources within your private network. AWS AppSync Private APIs helps customers to restrict access to GraphQL APIs to API consumers within a private network, such as Amazon Virtual Private Cloud (VPC) or hybrid environment. To deploy the provided SAM template, provide the VPC ID and Subnet ID as parameters which will be where the AppSync Interface VPC endpoint will be deployed. It is recommended to provide 2 or more subnet IDs for high availability. This implementation will support all GraphQL `queries`, `mutations` and `subscriptions` defined in the AppSync API GraphQL schema. To demonstrate this pattern, the template will deploy a simple Restaurant API with Amazon DynamoDB as a data source. | ||
|
|
||
| Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/apigateway-appsync-dynamodb-sam | ||
|
|
||
| Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. | ||
|
|
||
| ## Requirements | ||
|
|
||
| - [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. | ||
| - [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured | ||
| - [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) | ||
| - [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed | ||
| - [Create a VPC and Subnets](https://docs.aws.amazon.com/vpc/latest/userguide/create-vpc.html) to deploy the AppSync Interface VPC Endpoint or to test out the pattern, you can use the default VPC in each region. Use commands below to identify the default VPC ID and Subnet IDs | ||
|
|
||
| ``` | ||
| aws ec2 describe-vpcs --filters Name=isDefault,Values=true --query "Vpcs[0].VpcId" --output text | ||
| ``` | ||
|
|
||
| ``` | ||
| aws ec2 describe-subnets --filters "Name=vpc-id,Values=vpc-xxxxxxxxxxx" --query "Subnets[*].SubnetId" --output json | ||
| ``` | ||
|
|
||
| ## Deployment Instructions | ||
|
|
||
| 1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: | ||
| ``` | ||
| git clone https://github.com/aws-samples/serverless-patterns | ||
| ``` | ||
| 2. Change directory to the pattern directory: | ||
| ``` | ||
| cd appsync-private-api-same | ||
| ``` | ||
| 3. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: | ||
| ``` | ||
| sam deploy --guided | ||
| ``` | ||
| 4. During the prompts: | ||
|
|
||
| - Enter a stack name | ||
| - Enter the desired AWS Region | ||
| - Enter the VpcID where to deploy the Private AppSync API | ||
| - Enter a comma-separated list of SubnetIds in the VPC to deploy theA AppSync API Interface Endpoint | ||
| - Allow SAM CLI to create IAM roles with the required permissions. | ||
|
|
||
| Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. | ||
|
|
||
| 5. Note the outputs from the SAM deployment process. Two of the outputs `AppSync GraphQL API URL` and `AppSync VPC Endpoint DNS` will be used to test this pattern. | ||
|
|
||
| ## How it works | ||
|
|
||
| This patterns creates and AppSync Interface VPC Endpoint and a sample AppSync Private API backed with a DynamoDB data source. Requests to AppSync Private APIs will go through AWS’s private network without going over the internet. GraphQL requests from your application are routed via the interface VPC endpoint to AppSync Private API. Interface VPC endpoint is powered by [AWS PrivateLink](https://aws.amazon.com/privatelink/), a highly available, scalable technology that enables you to privately connect your VPC to AWS services like AWS AppSync as if the services were in your VPC. | ||
|
|
||
| API Key is used as the authorization mode for the AppSync API. However it is not recommended to use API Key for production application, please refer to other authorization modes supported by AppSync in the [documentation](https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html) | ||
|
|
||
| ## Testing | ||
|
|
||
| You can test this pattern using any command prompt that supports the `curl` command. Refer to the outputs `AppSyncApiUrl`, `AppSyncApiKey` and `AppSyncVPCEndpointDNS` from deploying the SAM application which will be used for testing. | ||
|
|
||
| 1. Create a resource (for example EC2 instance) within your private network to invoke the AppSync API | ||
| 2. Open your command prompt where you can run a `curl` commands | ||
| 3. To add a new restaurant entry to the Restaurant API, run the `curl` command below by pasting it in your command prompt. Remember to replace the values for `{AppSyncGraphQLAPIURL}`, `{AppSyncApiKey}` and `{AppSyncVPCEndpointDNS}` which are part of the output generated after deploying the SAM template. | ||
|
|
||
| Note: You can either use the `AppSync GraphQL API URL` or `AppSync VPC Interface Endpoint DNS` to invoke the API as show below. You can refer to the blog [Architecture Patterns for AWS AppSync Private APIs](<https://aws.amazon.com/blogs/mobile/architecture-patterns-for-aws-appsync-private-apis/#:~:text=invoking%20graphql%20operations%20(queries%2C%20mutations%20and%20subscriptions)%20on%20appsync%20private%20apis>) for further guidance | ||
|
|
||
| -- Using AppSync GraphQL API URL (enabled by Private DNS settings = Yes) | ||
|
|
||
| ```curl {AppSyncGraphQLAPIURL} \ | ||
| -H "Content-Type:application/graphql" \ | ||
| -H "x-api-key:da2-{AppSyncApiKey}" \ | ||
| -d '{"query": "query MyQuery {listRestaurants {items {name state restaurantId zip cuisine }}}","variables":"{}"}' | ||
| ``` | ||
|
|
||
| -- Using AppSync VPC Interface Endpoint DNS (you will need to pass the`AppSyncGraphQLAPIURL` in the host header, remember to remove prefix `www.`) | ||
|
|
||
| ```curl https://{AppSyncVPCEndpointDNS}/graphql \ | ||
| -H "Host:{AppSyncGraphQLAPIURL}" \ | ||
| -H "Content-Type:application/graphql" \ | ||
| -H "x-api-key:da2-{AppSyncApiKey}" \ | ||
| -d '{"query": "query MyQuery {listRestaurants {items {name state restaurantId zip cuisine }}}","variables":"{}"}' | ||
| ``` | ||
|
|
||
| 4. Refer to the blog [Architecture Patterns for AWS AppSync Private APIs](<https://aws.amazon.com/blogs/mobile/architecture-patterns-for-aws-appsync-private-apis/#:~:text=invoking%20graphql%20operations%20(queries%2C%20mutations%20and%20subscriptions)%20on%20appsync%20private%20apis>) for further examples on how to test out GraphQL subscriptions. | ||
|
|
||
| ## Cleanup | ||
|
|
||
| 1. Delete the stack | ||
| ```bash | ||
| sam delete | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
|
|
||
| SPDX-License-Identifier: MIT-0 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| { | ||
| "title": "AWS AppSync Private API ", | ||
| "description": "Create an AWS AppSync Private API with a sample API to demonstrate how you can invoke Private API from resources in your private network", | ||
| "language": "YAML", | ||
| "level": "200", | ||
| "framework": "SAM", | ||
| "introBox": { | ||
| "headline": "How it works", | ||
| "text": [ | ||
| "This pattern shows how you can deploy an AWS AppSync Private API which can only be invoked by resources within your private network.", | ||
| "The SAM application will create AppSync Interface VPC Endpoint and a sample AppSync Private API backed with a DynamoDB data source.", | ||
| "Requests to AppSync Private APIs will go through AWS’s private network without going over the internet.", | ||
| "GraphQL requests from your application are routed via the interface VPC endpoint to AppSync Private API." | ||
| ] | ||
| }, | ||
| "gitHub": { | ||
| "template": { | ||
| "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/appsync-private-api-sam", | ||
| "templateURL": "serverless-patterns/appsync-private-api-sam", | ||
| "projectFolder": "appsync-private-api-sam", | ||
| "templateFile": "template.yaml" | ||
| } | ||
| }, | ||
| "resources": { | ||
| "bullets": [ | ||
| { | ||
| "text": "AppSync Private API documentation:", | ||
| "link": "https://docs.aws.amazon.com/appsync/latest/devguide/using-private-apis.html" | ||
| } | ||
| ] | ||
| }, | ||
| "deploy": { | ||
| "text": [ | ||
| "sam deploy --guided" | ||
| ] | ||
| }, | ||
| "testing": { | ||
| "text": [ | ||
| "See the GitHub repo for detailed testing instructions." | ||
| ] | ||
| }, | ||
| "cleanup": { | ||
| "text": [ | ||
| "Delete the stack: <code>sam delete</code>." | ||
| ] | ||
| }, | ||
| "authors": [ | ||
| { | ||
| "name": "Ozioma Uzoegwu", | ||
| "image": "./Ouzoegwu.jpeg", | ||
| "bio": "I am a Principal Solutions Architect working at AWS", | ||
| "linkedin": "ouzoegwu", | ||
| "twitter": "iam_tessot" | ||
| } | ||
| ] | ||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| input AddRestaurantInput { | ||
| name: String! | ||
| state: String | ||
| zip: String | ||
| cuisine: CuisineType! | ||
| } | ||
|
|
||
| enum CuisineType { | ||
| Multi | ||
| Indian | ||
| Chinese | ||
| Italian | ||
| Thai | ||
| American | ||
| Continental | ||
| } | ||
|
|
||
| input DeleteRestaurantInput { | ||
| restaurantId: ID! | ||
| } | ||
|
|
||
| type Restaurant { | ||
| restaurantId: ID! | ||
| name: String! | ||
| state: String | ||
| zip: String | ||
| cuisine: CuisineType | ||
| } | ||
|
|
||
| type RestaurantConnection { | ||
| items: [Restaurant] | ||
| nextToken: String | ||
| } | ||
|
|
||
| input UpdateRestaurantInput { | ||
| restaurantId: ID! | ||
| name: String | ||
| state: String | ||
| zip: String | ||
| cuisine: CuisineType | ||
| } | ||
|
|
||
| type Mutation { | ||
| addRestaurant(input: AddRestaurantInput!): Restaurant | ||
| updateRestaurant(input: UpdateRestaurantInput!): Restaurant | ||
| deleteRestaurant(input: DeleteRestaurantInput!): Restaurant | ||
| } | ||
|
|
||
| type Query { | ||
| listRestaurants(limit: Int, nextToken: String): RestaurantConnection | ||
| getRestaurant(restaurantId: ID!): Restaurant | ||
| } | ||
|
|
||
| schema { | ||
| query: Query | ||
| mutation: Mutation | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import * as ddb from '@aws-appsync/utils/dynamodb'; | ||
|
|
||
| export function request(ctx) { | ||
| const key = { restaurantId: util.autoId() }; | ||
| const item = ctx.args.input; | ||
| const condition = { restaurantId: { attributeExists: false } }; | ||
| return ddb.put({ key, item, condition }); | ||
| } | ||
|
|
||
| export const response = (ctx) => ctx.result; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| import * as ddb from '@aws-appsync/utils/dynamodb'; | ||
|
|
||
| export const request = (ctx) => ddb.remove({ key: { restaurantId: ctx.args.input.restaurantId } }); | ||
| export const response = (ctx) => ctx.result; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import * as ddb from '@aws-appsync/utils/dynamodb'; | ||
|
|
||
| export const request = (ctx) => ddb.get({ key: { restaurantId: ctx.args.restaurantId } }); | ||
|
|
||
| export const response = (ctx) => ctx.result; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import * as ddb from '@aws-appsync/utils/dynamodb'; | ||
|
|
||
| export function request(ctx) { | ||
| const { limit = 10, nextToken } = ctx.args; | ||
| return ddb.scan({ limit, nextToken }); | ||
| } | ||
|
|
||
| export function response(ctx) { | ||
| const { items, nextToken } = ctx.result; | ||
| return { items: items ?? [], nextToken }; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { util } from '@aws-appsync/utils'; | ||
| import * as ddb from '@aws-appsync/utils/dynamodb'; | ||
|
|
||
| export function request(ctx) { | ||
| const { restaurantId, ...values } = ctx.args.input; | ||
| const condition = { restaurantId: { attributeExists: true } }; | ||
| return ddb.update({ key: { restaurantId }, update: values, condition }); | ||
| } | ||
|
|
||
| export function response(ctx) { | ||
| const { error, result } = ctx; | ||
| if (error) { | ||
| return util.error(error.message, error.type); | ||
| } | ||
| return result; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you provide a clarifying example on how the SubnetIds Parameter need to be passed in? Is it comma separate or something esle?