Purple Apps consist of three main parts:
- Frontend
- Next.js
- API
- GraphQL - AWS AppSync
- RestAPI - Amazon API Gateway
- Backend
- Resources - serverless service containing CloudFormation resources like DynamoDB tables, S3 buckets, SQS queues etc.
- ...and arbirarry number of serverless services handling background processes
Because there are so many NPM scripts across Purple Stack, we are using .env
file to store all the common envionrment variables in a single place.
So in order to run all the parts of the Purple Stack locally, you need to create a .env file in the root folder of your project.
AWS_SDK_LOAD_CONFIG=true
AWS_PROFILE=purple-technology
AWS_REGION=eu-central-1
There are some settings which need to be changed in order to make this boilerplate project work:
- π = Optional
- βοΈ = Required
serverless.settings.yml
- βοΈ Replace
REGION
incommon.region
with desired AWS region where you want your application to be deployed - βοΈ Create an S3 bucket for the serverless deployments and then fill the bucket's name in
common.deploymentBucket
- βοΈ Replace
purple-stack.com
infrontend.domain
with desired domain where you would like your application to be avalible. This domain needs to have a hosted zone in the same AWS account's Route53. - βοΈ Create a wilcard certificate in
N. Virginia
region for thefrontend.domain
and fill the certificate ARN tofrontend.certificate
- π Modify the
common.projectName
to better identify your application - π Modify the
common.dnsRandomString
to better secure your feature deployments - π Modify the
vpc
in case you need to have your functions inside a VPC (if you need reach to an RDS databases, for example) - π If you would like to enable monitoring, put a list of stages you would like to monitor in
monitoring.stages
and if you do so, βοΈ fill themonitoring.topic
with the desired topic ARN
.circleci/config.yml
- π Maybe you don't need to use CircleCI
context
, so in that case you can remove all it's occurences. - π Maybe you don't need to have NPM authentication to install private packages, so in that case you can remove all occurances of
npm_auth
orcommands_npm_auth
and parts related to it. - π Maybe you need different branch filtering, so in that case feel free to modify it according to your needs.
- π Maybe you can deploy the application with the same AWS account whose AWS credentials you've provided to the CI envronment, so in that case feel free to remove all occurances of
assume_aws_role
orcommands_assume_aws_role
and any parts related to it.
If you need to deploy the application with a different AWS account than the AWS account whose credentials you have provided to the CI environment:
.circleci/aws_assume_role.sh
- βοΈ Replace
ROLE_ARN_HERE
with the correct ARN of the role which will be assumed by the CI to create the deployment - π Modify the role session name with a name which better identifies your application
@package/config
This package serves for storing and providing settings to your whole application - Frontend, API and Backend. That means that only non-secret values can be stored there.
For example:
- Minimum length of password β
- Database credentials β
@package/secrets
This package serves for providing secret values to your backend part of the applications - API and Backend.
Read more here.
@package/logger
This package serves for simplifying structured logging within Lambda functions.
Structured logging logs JSON with metadata instead of logging just a string.
This helps developers search in the logs for a specific log and gives them the proper context of what happened.
Read more here.
@package/graphql-types
This package contains TypeScript types which were generated from the GraphQL schema from API.
It's powered by @graphql-codegen
You can re-generate the types by running
$ npm run graphql-codegen
Don't forget to re-generate the types when you make a change to the GraphQL schema.
In case you forget, test
CI workflow will fail on step Graphql CodeGen check
.
@package/sls-macros
"Serverless Macros" is a term born in Purple Technology.
Serverless Macros are JavaScript functions which are executed inside the serverless.yml
file and their purpose is to conditionally generate the template. Simply said it serves as sort of programmatic extension to YAML templates.
For example: apply Point-In-Time recovery only for production DynamoDB tables or apply coorect DeletionPolicy for all production resources.
DeletionPolicy: ${file(macros.js):getStorageResourcePolicy}
Becuse of the fact that all Purple App serverless services are interconnected via Fn::ImportValue we need to keep correct deployment order in order to fulfill the dependencies.
- First we need to deploy
resources
which are lerna packages@be-prioritized/*
. - then we can deploy rest of the backend which are lerna packages
@be/*
- then API which is lerna package
api
- and then frontend which is lerna package
frontend
This boilerplate repository complies with three main architectural pillars of Purple Apps:
- Serverless
- Infrastructure as Code
- CI/CD
Serverless architecture has been playing a big role at Purple Technology since 2017 because we love the ease of developing and provisioning serverless applications that allow us to focus purely on the business logic.
All of our newly built apps are Powered by Serverless β‘οΈ
We chose to go with a combination of two market leaders - AWS and Serverless Framework.
We know that term "Serverless" is not just about Lambda functions, it is also about complementary services like DynamoDB, SNS, SQS, Cognito, S3 etc. which can be defined also in this stack in the form of infrastructure-as-code.
It has been proven by many examples in the past that managing the infrastructure purely by humans is a bad idea - human errors, miscommunication, manual changes taking too long, etc.
That's why we decided to adopt a native IaC tool for both AWS and Serverless Framework called "AWS CloudFormation."
The whole monorepo is perfectly interconnected via CloudFormation's references and imports.
With serverles monorepo full of dependencies came into play an issue of complex deployments.
Also it used to happen that two developers deploying same code endedup deploying "different" applications.
That is why we decided to adopt CI/CD - CircleCI - and to have deterministic deployments and to have test and compilation checks as GitHub PR checks.