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

Add an example for AppSync with API Key, and CRUD Resolvers with DynamoDB #44

Merged
merged 12 commits into from
May 31, 2019
Merged
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ $ cdk destroy
|---------|-------------|
| [api-cors-lambda-crud-dynamodb](https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/api-cors-lambda-crud-dynamodb/) | Creating a single API with CORS, and five Lambdas doing CRUD operations over a single DynamoDB |
| [application-load-balancer](https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/application-load-balancer/) | Using an AutoScalingGroup with an Application Load Balancer |
| [appsync-graphql-dynamodb](https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/appsync-graphql-dynamodb/) | Creating a single GraphQL API with an API Key, and four Resolvers doing CRUD operations over a single DynamoDB |
| [classic-load-balancer](https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/classic-load-balancer/) | Using an AutoScalingGroup with a Classic Load Balancer |
| [custom-resource](https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/custom-resource/) | Shows adding a Custom Resource to your CDK app |
| [elasticbeanstalk](https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/elasticbeanstalk/) | Elastic Beanstalk example using L1 with a Blue/Green pipeline (community contributed) |
Expand Down
17 changes: 17 additions & 0 deletions typescript/appsync-graphql-dynamodb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# AppSync GraphQL API with four resolvers CRUD with DynamoDB

This an example of an AppSync GraphQL API, pointing to four resolvers doing CRUD operations with a single DynamoDB table.

## The Component Structure

This Stack contains:

- a GraphQL API with an API Key (Use with caution, each key is only valid for 7 days.)
- a GraphQL Schema with Queries to get one and all items and two mutations to save and delete an item
- a DynamoDB table `items` that stores the data with a Pay Per Request Billing Mode
- an IAM Role that allows AppSync to invoke your DynamoDB table.
- a DataSource, connecting your API to the DynamoDB table with the previously specified role.
- a Resolver for a Query `getOne` to get one item from the DynamoDB table.
- a Resolver for a Query `all` to get all items from the DynamoDB table.
- a Resolver for a Mutation `save` to put an item into the DynamoDB table (the id is autogenerated, you need only name).
- a Resolver for a Mutation `delete` to delete one item from the DynamoDB table.
3 changes: 3 additions & 0 deletions typescript/appsync-graphql-dynamodb/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "node index"
}
145 changes: 145 additions & 0 deletions typescript/appsync-graphql-dynamodb/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import cdk = require('@aws-cdk/cdk');
import { CfnGraphQLApi, CfnApiKey, CfnGraphQLSchema, CfnDataSource, CfnResolver } from '@aws-cdk/aws-appsync';
import { Table, AttributeType, StreamViewType, BillingMode } from '@aws-cdk/aws-dynamodb';
import { Role, ServicePrincipal } from '@aws-cdk/aws-iam';


export class AppSyncCdkStack extends cdk.Stack {

constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const tableName = 'items'

const itemsGraphQLApi = new CfnGraphQLApi(this, 'ItemsApi', {
name: 'items-api',
authenticationType: 'API_KEY'
});

new CfnApiKey(this, 'ItemsApiKey', {
apiId: itemsGraphQLApi.graphQlApiApiId
});

const apiSchema = new CfnGraphQLSchema(this, 'ItemsSchema', {
apiId: itemsGraphQLApi.graphQlApiApiId,
definition: `type ${tableName} {
${tableName}Id: ID!
name: String
}
type Paginated${tableName} {
items: [${tableName}!]!
nextToken: String
}
type Query {
all(limit: Int, nextToken: String): Paginated${tableName}!
getOne(${tableName}Id: ID!): ${tableName}
}
type Mutation {
save(name: String!): ${tableName}
delete(${tableName}Id: ID!): ${tableName}
}
type Schema {
query: Query
mutation: Mutation
}`
});

const itemsTable = new Table(this, 'ItemsTable', {
tableName: tableName,
partitionKey: {
name: `${tableName}Id`,
type: AttributeType.String
},
billingMode: BillingMode.PayPerRequest,
streamSpecification: StreamViewType.NewImage
});

const itemsTableRole = new Role(this, 'ItemsDynamoDBRole', {
assumedBy: new ServicePrincipal('appsync.amazonaws.com')
});

itemsTableRole.attachManagedPolicy('arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess');

const dataSource = new CfnDataSource(this, 'ItemsDataSource', {
apiId: itemsGraphQLApi.graphQlApiApiId,
name: 'ItemsDynamoDataSource',
type: 'AMAZON_DYNAMODB',
dynamoDbConfig: {
tableName: itemsTable.tableName,
awsRegion: this.region
},
serviceRoleArn: itemsTableRole.roleArn
});

const getOneResolver = new CfnResolver(this, 'GetOneQueryResolver', {
apiId: itemsGraphQLApi.graphQlApiApiId,
typeName: 'Query',
fieldName: 'getOne',
dataSourceName: dataSource.dataSourceName,
requestMappingTemplate: `{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"${tableName}Id": $util.dynamodb.toDynamoDBJson($ctx.args.${tableName}Id)
}
}`,
responseMappingTemplate: `$util.toJson($ctx.result)`
});
getOneResolver.addDependsOn(apiSchema);

const getAllResolver = new CfnResolver(this, 'GetAllQueryResolver', {
apiId: itemsGraphQLApi.graphQlApiApiId,
typeName: 'Query',
fieldName: 'all',
dataSourceName: dataSource.dataSourceName,
requestMappingTemplate: `{
"version": "2017-02-28",
"operation": "Scan",
"limit": $util.defaultIfNull($ctx.args.limit, 20),
"nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.nextToken, null))
}`,
responseMappingTemplate: `$util.toJson($ctx.result)`
});
getAllResolver.addDependsOn(apiSchema);

const saveResolver = new CfnResolver(this, 'SaveMutationResolver', {
apiId: itemsGraphQLApi.graphQlApiApiId,
typeName: 'Mutation',
fieldName: 'save',
dataSourceName: dataSource.dataSourceName,
requestMappingTemplate: `{
"version": "2017-02-28",
"operation": "PutItem",
"key": {
"${tableName}Id": { "S": "$util.autoId()" }
},
"attributeValues": {
"name": $util.dynamodb.toDynamoDBJson($ctx.args.name)
}
}`,
responseMappingTemplate: `$util.toJson($ctx.result)`
});
saveResolver.addDependsOn(apiSchema);

const deleteResolver = new CfnResolver(this, 'DeleteMutationResolver', {
apiId: itemsGraphQLApi.graphQlApiApiId,
typeName: 'Mutation',
fieldName: 'delete',
dataSourceName: dataSource.dataSourceName,
requestMappingTemplate: `{
"version": "2017-02-28",
"operation": "DeleteItem",
"key": {
"${tableName}Id": $util.dynamodb.toDynamoDBJson($ctx.args.${tableName}Id)
}
}`,
responseMappingTemplate: `$util.toJson($ctx.result)`
});
deleteResolver.addDependsOn(apiSchema);

}
}

const app = new cdk.App();
new AppSyncCdkStack(app, 'AppSyncGraphQLDynamoDBExample');
app.run();
26 changes: 26 additions & 0 deletions typescript/appsync-graphql-dynamodb/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "appsync-graphql-dynamodb",
"version": "0.23.0",
"description": "Running a GraphQL API with four resolvers to do CRUD operations on DynamoDB",
"private": true,
"scripts": {
"build": "tsc",
"watch": "tsc -w",
"cdk": "cdk"
},
"author": {
"name": "Aleksandar Simovic <[email protected]>",
"url": "https://serverless.pub"
},
"license": "MIT",
"devDependencies": {
"@types/node": "^8.10.38",
"typescript": "^3.2.4"
},
"dependencies": {
"@aws-cdk/aws-appsync": "*",
"@aws-cdk/aws-dynamodb": "*",
"@aws-cdk/aws-iam": "*",
"@aws-cdk/cdk": "*"
}
}
21 changes: 21 additions & 0 deletions typescript/appsync-graphql-dynamodb/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target":"ES2018",
"module": "commonjs",
"lib": ["es2016", "es2017.object", "es2017.string"],
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": false,
"inlineSourceMap": true,
"inlineSources": true,
"experimentalDecorators": true,
"strictPropertyInitialization":false
}
}

1 change: 0 additions & 1 deletion typescript/ecs/cluster/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,3 @@ const app = new cdk.App();
new ECSCluster(app, 'MyFirstEcsCluster');

app.run();

2 changes: 1 addition & 1 deletion typescript/ecs/ecs-service-with-task-networking/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ new ecs.Ec2Service(stack, 'awsvpc-ecs-demo-service', {
securityGroup,
});

app.run();
app.run();