Skip to content
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
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@
npm install
```

## Environment variables

Copy `env.example` to `.env` and set values for your environment.

For DB-backed cart functionality, these variables are required:

- `DB_HOST`
- `DB_PORT`
- `DB_USER`
- `DB_PASSWORD`
- `DB_NAME`
- `DB_SSL`
- `DB_SYNC`
- `PRODUCT_SERVICE_URL`

For AWS Lambda deployment, the app exposes serverless handler in `src/lambda.ts`.



## Running the app
Expand Down
9 changes: 9 additions & 0 deletions env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
APP_PORT=4000
PRODUCT_SERVICE_URL=http://localhost:3000

DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=cart_db
DB_SSL=false
DB_SYNC=true

### These variables are needed to make script for getting access_token works
AUTH_USERNAME=yourGithubLogin ### It should be your github username
Expand Down
5 changes: 5 additions & 0 deletions infra/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules/
dist/
cdk.out/
*.js.map
*.d.ts
13 changes: 13 additions & 0 deletions infra/bin/cart-service-infra.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CartServiceInfraStack } from '../lib/cart-service-infra-stack';

const app = new cdk.App();

new CartServiceInfraStack(app, 'CartServiceInfraStack', {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
});
10 changes: 10 additions & 0 deletions infra/cdk.context.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"availability-zones:account=797715838914:region=us-east-1": [
"us-east-1a",
"us-east-1b",
"us-east-1c",
"us-east-1d",
"us-east-1e",
"us-east-1f"
]
}
3 changes: 3 additions & 0 deletions infra/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "npx ts-node --prefer-ts-exts bin/cart-service-infra.ts"
}
125 changes: 125 additions & 0 deletions infra/lib/cart-service-infra-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import * as path from 'path';
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as rds from 'aws-cdk-lib/aws-rds';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as lambdaNodejs from 'aws-cdk-lib/aws-lambda-nodejs';
import { aws_apigateway as apigateway } from 'aws-cdk-lib';

export class CartServiceInfraStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const vpc = new ec2.Vpc(this, 'CartVpc', {
maxAzs: 2,
natGateways: 1,
subnetConfiguration: [
{
name: 'Public',
subnetType: ec2.SubnetType.PUBLIC,
},
{
name: 'Private',
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
},
],
});

const lambdaSecurityGroup = new ec2.SecurityGroup(this, 'CartLambdaSg', {
vpc,
allowAllOutbound: true,
description: 'Security group for cart Lambda',
});

const dbSecurityGroup = new ec2.SecurityGroup(this, 'CartDbSg', {
vpc,
allowAllOutbound: true,
description: 'Security group for cart Postgres instance',
});

dbSecurityGroup.addIngressRule(
lambdaSecurityGroup,
ec2.Port.tcp(5432),
'Allow Lambda to access Postgres',
);

const database = new rds.DatabaseInstance(this, 'CartPostgres', {
vpc,
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
securityGroups: [dbSecurityGroup],
engine: rds.DatabaseInstanceEngine.postgres({
version: rds.PostgresEngineVersion.VER_16_3,
}),
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
allocatedStorage: 20,
maxAllocatedStorage: 100,
credentials: rds.Credentials.fromGeneratedSecret('cart_user'),
databaseName: 'cart_db',
publiclyAccessible: false,
deletionProtection: false,
removalPolicy: cdk.RemovalPolicy.DESTROY,
deleteAutomatedBackups: true,
});

const lambdaFunction = new lambdaNodejs.NodejsFunction(this, 'LambdaFunction', {
runtime: lambda.Runtime.NODEJS_20_X,
entry: path.join(__dirname, '../../src/lambda.ts'),
handler: 'handler',
timeout: cdk.Duration.seconds(30),
memorySize: 1024,
vpc,
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
securityGroups: [lambdaSecurityGroup],
environment: {
DB_HOST: database.instanceEndpoint.hostname,
DB_PORT: database.instanceEndpoint.port.toString(),
DB_NAME: 'cart_db',
DB_USER: 'cart_user',
DB_PASSWORD: database.secret!.secretValueFromJson('password').toString(),
DB_SSL: 'false',
DB_SYNC: 'true',
PRODUCT_SERVICE_URL:
this.node.tryGetContext('productServiceUrl') ||
'https://YOUR_API_ID.execute-api.ap-south-1.amazonaws.com/prod',
},
bundling: {
externalModules: [
'aws-sdk',
'@nestjs/microservices',
'@nestjs/websockets',
'class-transformer',
'class-validator',
],
},
});

database.secret?.grantRead(lambdaFunction);
database.connections.allowDefaultPortFrom(lambdaFunction);

const api = new apigateway.RestApi(this, 'NestApi', {
restApiName: 'Nest Service',
description: 'This service serves a Nest.js application.',
deployOptions: {
stageName: 'prod',
},
});

const getLambdaIntegration = new apigateway.LambdaIntegration(lambdaFunction);

api.root.addProxy({
defaultIntegration: getLambdaIntegration,
anyMethod: true,
});

new cdk.CfnOutput(this, 'ApiUrl', {
value: api.url,
description: 'Invoke URL for cart service API',
});

new cdk.CfnOutput(this, 'DbSecretArn', {
value: database.secret?.secretArn || '',
description: 'Secrets Manager ARN containing DB credentials',
});
}
}
Loading