I recently created a code base called Wrapper.js, which combines NextJS, Serverless Framwork and Terraform within a Monorepo.
After writing a post about the benefits of using javascript to automate NextJS, Serverless Framework and Terraform Devops processes, I wrote this post which will show you how I have put together, in a manner that you can use in your own projects!
Table of Contents
What is Wrapper.js and why you should care about it
A TLDR from my previous blog post:
Wrapper.js is a monorepo, that seamlessly and dynamically integrates the NextJS, Serverless Framework and Terraform together.
Wrapper.JS orchestrates these technologies into one neat solution, which would normally be difficult due to the complexities of each specialism (e.g front end vs back end vs cloud infrastructure).
It orchestrates all technologies through npm commands to:
1. run a local dev environment for both front and back ends
2. deploy and destroy an environment
3. generate environment specific variable files based on aws secrets for NextJS, SLS and Terraform
It reduces complexity of using these technologies to manage multiple environments within a continous deployment and integration pipeline
How Wrapper.js combines these technologies
Without going into the actual detail of the code logic, I’ll talk through the way the codebase is structured so you can understand how the technologies work together.
Environment & Orchestration Files
Here is a view of the codebase, looking only at the index directories.
Looking at the code base in its most basic form, lets start by looking at the index directory files:
index.js
bitbucket-pipelines.yml
.env
package.json
devops/
backend/
frontend/
The nodejs wrapper script, that triggers logic based on the npm command that is executed.
The bitbucket config file for continous deployment and integration pipeline
AWS credentials that allow the wrapper script to automate processes that interact with your aws account.
NPM dependencies that allow the wrapper script to work.
The Devops Infrastructure
The devops folder enables the wrapper script (the root level index.js) and is also the home of the terraform code.
One of the core concepts of Wrapper.js, is the creation and use of environment variables throughout the stack. This, as well as running a local dev environment and cloud deployment commands, is what the scripts folder enables.
Terraform is configured in this codebase to dynamically generate and manage all cloud resources (apart from lambdas) based on these environment files.
index.js
bitbucket-pipelines.yml
.env
package.json
devops
|
└─── continuous-deployment
| └─── Dockerfile
└─── scripts
| └─── next.js
| └─── serverless.js
| └─── terraform.js
| └─── utils.js
└─── terraform
└─── main.tf
└─── backend.tf
└─── variables.tf
└─── terraform.tfvars.json
└─── modules
└─── acm
| └─── ...
└─── apiGateway
| └─── ...
└─── cloudfront
| └─── ...
└─── s3
| └─── ...
└─── ...
backend/
frontend/
Contains a Dockerfile for the configuration of the ci/cd server that enables the bitbucket pipeline.
- next.js: contains all logic to automate NextJS (front end) related processes
- serverless.js: contains all logic to automate Serverless Framework (back end) related processes
- terraform.js: contains all logic to automate Terraform (cloud infrastructure) related processes
- utils.js: contains all the helper functions that automate the above script files
- main.tf: the root configuration file, that shows all cloud resources that are declared as terraform modules
- backend.tf: the configuration of how state is dynamically stored in an s3 bucket, based on environment variables set in variables.tf
- variables.tf: the list of expected variables that are expected to get passed into the terraform configuration files, these variables are set in terraform.tfvars.json
- terraform.tfvars.json: these are the environment variables which feed the terraform configuration, these are generated by the wrapper script
- modules folder: this folder contains sub directories for each of the cloud resources that are created in the terraform configuration, I won’t detail these in this tutorial – but it is these resources that are dynamically created in different environments based on the variables passed to the terraform configuration.
The Backend Services
Unlike Terraform, which is being used to create all architectural cloud resources, Serverless Framework is being used to deploy lambda functions only.
This means that within Wrapper.js, Terraform is creating a blank ApiGateway and Serverless Framework is modifying that API Gateway with end points that trigger lambda’s – further details on what is deployed in Terraform vs Serverless can be found here.
Back end code is organised into backend services, each service contains lambda functions based on the business logic that needs to be be performed.
index.js
bitbucket-pipelines.yml
.env
package.json
devops/
backend
|
└─── index.js
└─── utils.js
└─── serverless.common.yml
└─── serverless.env.json
└─── package.json
└─── services
└─── serviceOne
| └─── serverless.yml
| └─── package.json
| └─── lambdaOne
| | └─ lambdaOne.yml
| | └─ lambdaOne.js
| └─── lambdaTwo
| └─ lambdaTwo.yml
| └─ lambdaTwo.js
|
└─── serviceTwo
└─── ...
frontend/
Opening the backend folder, shows you how the backend is managed:
A script to orchestrate the local host development, cloud deployment and removal of multiple Serverless Framework services through npm commands.
A file containing helper functions that enables the index.js script.
A YAML file containing configurations that are shared across all services, these depend on variables that are retrieved from serverless.env.json.
A file that is dynamically created by the wrapper script, which contains environment specific variables that will feed into the back end services.
A file that contains all the npm dependencies to run the index.js file.
The Services directory contains each backend service as a directory – an example of a service contains:
- serverless.yml: The serverless framework configuration for that service, this will be dependent on the serverless.common.yml for some of its configurations
- package.json: A file that contains all the npm dependencies to run the service
- lambdaOne.yml and lambdaOne.js: contains the lambda configuration and the nodejs code for the lambda function, these are contained within a folder called lambdaOne. These are obviously dummy names, but the concept is you can have as many lambda’s as you’d like in a service, I’ve shown a second set of lambda’s in the code snippet above as an example.
The Frontend Application
Last but not least, the front end folder contains the front end application which is written with ReactJS and uses NextJS as a framework for structuring the codebase.
index.js
bitbucket-pipelines.yml
.env
package.json
devops/
backend/
frontend
|
└─── .next
└─── .babelrc
└─── components
| └─── ...
└─── pages
| └─── ...
└─── public
| └─── ...
└─── stores
| └─── ...
└─── next.config.js
└─── package.json
The frontend folder is essentially a standard NextJS application, made up of:
Standard NextJS directory that contains dev and build configurations.
Standard Babel file that contains all dev configurations for ES6 transpiling settings.
A folder that contains ReactJS components.
A folder that contains special ReactJS components, which render as pages if placed within this folder – this is a special NextJS feature.
A folder that contains all assets (e.g images and videos etc) for the front end.
A folder containing global state stores that are created using Zustand.
A file that is dynamically generated by the wrapper script, which contains all environment specific variables for the front end.
A file that contains all npm dependencies and commands required to run the frontend.
In conclusion
This post has detailed Wrapper.js’s approach to how it practically structures the technologies in the same codebase.
I hope this has been helpful and that someone out there is able to use this (or parts of this) to help them!!
Hope this was fun to read – enjoy automating 😀