Skip to content

Commit

Permalink
Feat(typescript/appsync-dynamodb-join): Appsync dynamodb join example (
Browse files Browse the repository at this point in the history
…#1012)

* initial commit with cdk setup

* added stability in readme

* included .js files

* excluded test from package.json

* main: optimizations for data and dynamodb tables

* small readme updates

---------

Co-authored-by: oscarwh <[email protected]>
Co-authored-by: Michael Kaiser <[email protected]>
  • Loading branch information
3 people committed Mar 30, 2024
1 parent 0e41aaa commit d511933
Show file tree
Hide file tree
Showing 17 changed files with 1,001 additions and 180 deletions.
1 change: 1 addition & 0 deletions typescript/appsync-graphql-dynamodb/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!*.js
114 changes: 81 additions & 33 deletions typescript/appsync-graphql-dynamodb/README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,100 @@
# AppSync GraphQL API Acting on DynamoDB
# AWS CDK AppSync DynamoDB Table Joining Demo

<!--BEGIN STABILITY BANNER-->
---

![Stability: Experimental](https://img.shields.io/badge/stability-Experimental-important.svg?style=for-the-badge)
![Stability: Stable](https://img.shields.io/badge/stability-Stable-success.svg?style=for-the-badge)

> **This is an experimental example. It may not build out of the box**
>
> This examples is built on Construct Libraries marked "Experimental" and may not be updated for latest breaking changes.
> **This is a stable example. It should successfully build out of the box**
>
> If build is unsuccessful, please create an [issue](https://github.com/aws-samples/aws-cdk-examples/issues/new) so that we may debug the problem
> This example is built on Construct Libraries marked "Stable" and does not have any infrastructure prerequisites to build.
---

<!--END STABILITY BANNER-->

This an example of an AppSync GraphQL API, pointing to five resolvers doing CRUD operations on a single DynamoDB table.
This project demonstrates how to use AWS CDK (Cloud Development Kit) to create an AWS AppSync API backed by DynamoDB tables. The essence of this project lies in establishing a one-to-many relationship between two tables, where one table stores information about cars, and the other stores information about defects associated with cars. This allows querying both tables together as a sort of nested query.

## Build
## Table of Contents
- [Overview](#overview)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Usage](#usage)
- [Architecture](#architecture)

To build this app, you need to be in this example's root folder. Then run the following:
## Overview

```bash
npm install -g aws-cdk
npm install
npm run build
```
This project sets up an AWS AppSync API named `carAPI` with two DynamoDB tables: `cardata-cars` and `cardata-defects`. The `cardata-cars` table stores information about cars, while the `cardata-defects` table stores information about defects associated with cars. The AppSync API provides GraphQL endpoints to query and mutate data in these tables. The essence of this project is to enable querying both tables in a nested manner, representing the one-to-many relationship between cars and defects.

The data used in this project is sourced from public data of RDW (Government agency Dienst Wegverkeer, commonly known as RDW, which handles the type-approval and registration of motorized vehicles and driving licences in the Netherlands). This data used in this project is based on data freely available for use without any restrictions.

This will install the necessary CDK, then this example's dependencies, and then build your TypeScript files and your CloudFormation template.
## Prerequisites

## Deploy
Before getting started, ensure you have the following prerequisites:
- Node.js installed (v18.x)
- AWS CDK installed (`npm install -g aws-cdk`)
- AWS CLI configured with appropriate credentials

Run `cdk deploy`. This will deploy / redeploy your Stack to your AWS Account.
## Installation

After the deployment you will see the API's URL, which represents the url you can then use.
1. Clone this repository to your local machine.
2. Navigate to the project directory.
3. Install dependencies by running `npm install`.

## Synthesize Cloudformation Template
## Usage

To see the Cloudformation template generated by the CDK, run `cdk synth`, then check the output file in the "cdk.out" directory.
To deploy the AWS infrastructure, run the following command:

## The Component Structure
```bash
cdk deploy
```

This Stack contains:
To remove the deployed infrastructure, run:

```bash
cdk destroy
```

The data in the DynamoDB tables can be populated using the utilities provided in `utils/index.js`. It will take a couple of seconds to push all the data to DynamoDB. Execute the following command to populate the tables:

```bash
npm run push-data
```

Once the CDK stack is deployed and the data is ingested into the DynamoDB tables you can query it through the AWS Appsync Console. If you can go to the query tab you can execute the following GraphQL Request:

```graphql
query GetCar {
getCar(licenseplate: "BR794ZQ3") {
expirydateapk
cylindervolume
catalogprice
defects {
defectdescription
defectstartdate
licenseplate
}
firstcolor
firstregistrationdate
licenseplate
}
}
```

- 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 three mutations to save, update and delete an item
- a __DynamoDB table__ `items` that stores the data with a Pay Per Request Billing Mode
- an __AppSync DataSource__, connecting your API to the DynamoDB table.
- an __AppSync Resolver__ for a Query `getOneItem` to get one item from the DynamoDB table.
- an __AppSync Resolver__ for a Query `getAllItems` to get all items from the DynamoDB table.
- an __AppSync Resolver__ for a Mutation `addItem` to put an item into the DynamoDB table (the id is autogenerated, you need only a name).
- an __AppSync Resolver__ for a Mutation `updateItem` to update an item in the DynamoDB table.
- an __AppSync Resolver__ for a Mutation `deleteItem` to delete one item from the DynamoDB table.
## Architecture

![alt text](appsync-architecture.png)

The AWS CDK stack defined in `cdk-appsync-demo-stack.ts` sets up the following resources:
- DynamoDB tables:
- `cardata-cars`: Stores information about cars.
- `cardata-defects`: Stores information about defects associated with cars.
- `defects-by-licenseplate` Global Secondary Index (GSI) that allows to query defects by licenseplate
- AppSync API (`carAPI`):
- GraphQL schema defined in `graphql/schema.graphql`.
- Data sources connected to DynamoDB tables.
- Resolvers to query data.
- Resolvers:
- `getCar.js`: Resolver function to fetch cars from the `cardata-cars` table.
- `getDefects.js`: Resolver function to fetch defects associated with cars from the `cardata-defects` table.

## Costs
The operational expenses associated with deploying this architecture are estimated to be approximately a couple of dollars per month (based on the eu-central-1 region). Cost optimization measures can be implemented by adjusting the write capacity of DynamoDB tables and indexes to a lower setting after the execution of the `npm run push-data` command.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions typescript/appsync-graphql-dynamodb/bin/cdk-appsync-demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CdkAppsyncDemoStack } from '../lib/cdk-appsync-demo-stack';

const env = { region: 'eu-central-1' };
const app = new cdk.App();
new CdkAppsyncDemoStack(app, 'CdkAppsyncDemoStack', { env } );
62 changes: 61 additions & 1 deletion typescript/appsync-graphql-dynamodb/cdk.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,63 @@
{
"app": "node index"
"app": "npx ts-node --prefer-ts-exts bin/cdk-appsync-demo.ts",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"**/*.d.ts",
"**/*.js",
"tsconfig.json",
"package*.json",
"yarn.lock",
"node_modules",
"test"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true
}
}
127 changes: 127 additions & 0 deletions typescript/appsync-graphql-dynamodb/data/cars.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
[
{
"licenseplate": {
"S": "87FRABCE"
},
"brand": {
"S": "BMW"
},
"tradename": {
"S": "5ER REIHE"
},
"expirydateapk": {
"N": "20231019"
},
"firstcolor": {
"S": "GREEN"
},
"cylindercount": {
"N": "8"
},
"firstregistrationdate": {
"N": "19980414"
},
"length": {
"N": "0"
},
"width": {
"N": "0"
}
},
{
"licenseplate": {
"S": "93A5ZR2U"
},
"brand": {
"S": "BMW"
},
"tradename": {
"S": "1ER REIHE"
},
"expirydateapk": {
"N": "20241020"
},
"firstcolor": {
"S": "BLACK"
},
"cylindercount": {
"N": "4"
},
"cylindervolume": {
"N": "1596"
},
"firstregistrationdate": {
"N": "20041029"
},
"length": {
"N": "423"
},
"width": {
"N": "0"
}
},
{
"licenseplate": {
"S": "104RZAFB"
},
"brand": {
"S": "BMW"
},
"tradename": {
"S": "1ER REIHE"
},
"expirydateapk": {
"N": "20241004"
},
"firstcolor": {
"S": "GREY"
},
"cylindercount": {
"N": "4"
},
"cylindervolume": {
"N": "1995"
},
"firstregistrationdate": {
"N": "20050415"
},
"catalogprice": {
"N": "32826"
},
"length": {
"N": "423"
},
"width": {
"N": "0"
}
},
{
"licenseplate": {
"S": "BR794ZQ3"
},
"brand": {
"S": "BMW"
},
"tradename": {
"S": "5ER REIHE"
},
"expirydateapk": {
"N": "20231019"
},
"firstcolor": {
"S": "GREEN"
},
"cylindercount": {
"N": "8"
},
"firstregistrationdate": {
"N": "19980414"
},
"length": {
"N": "0"
},
"width": {
"N": "0"
}
}
]
Loading

0 comments on commit d511933

Please sign in to comment.