How I automatically deployed Typescript to Elastic Beanstalk to speed up server-side development
Speed up your development process by auto-deploying Typescript code to EB!
For my most recent team project Booktogether — an online publishing platform in Korean for book curations and reviews — I was responsible for server-side development using Typescript and Express, as well as deployment using AWS Elastic Beanstalk.
As I soon found out, manual deployment to EB was a tedious process. I was constantly compiling Typescript code to Javascript, compressing all the files into a .zip file, and uploading the compressed file to AWS.
That’s when I thought to myself, “is there any way I can speed up deployment, so I can just focus on writing working code?” After doing some digging online, I found this walkthrough by Aaron White that helped me set up AWS CodePipeline to send my code straight from Github to EB.
Side Note: A Common Error
Although I won’t go through configuration details of CodePipeline too deeply in this article, I would like to specify a problem that I encountered while setting up this auto-deployment system. When I originally pushed the server-side code (along with a build spec file) from Github to AWS, the EB console responded with the following error:
[Instance: i-12345] Command failed on instance. Return code: 1 Output: (TRUNCATED)…/opt/elasticbeanstalk/containerfiles/ebnode.py”, line 180, in npm_install raise e subprocess.CalledProcessError (TRUNCATED)
As I later realized, this error happened because of the npm install
command that EB runs when code is deployed to the EC2 instance. Specifically, the installation of some packages trigger the node-gyp
process, which is run by the default EC2 user.
The default user does not have permission to access some of the packages in the node_modules directory that npm install
creates, which is why one needs to create a file called .npmrc
in the project root and add the following:
# Forces node-gyp process to be run as root user, not ec2 default
unsafe-perm=true
Auto-deploying Typescript code to EB
Now back to the heart of the matter. When code is deployed to a Node.js environment in Elastic Beanstalk, node app
is automatically called in order to initialize the server. However, because Node.js is a Javascript runtime environment, the Typescript code that I had written for my server would not run by default. I needed to compile my Typescript code first.
In order to accomplish this without having to manually upload my code as a .zip file, I had two options. The first was to simply compile Typescript code on my local machine using a command like npm run build
, and then upload both Javascript and Typescript to my Github repository. This option, however, had two main drawbacks:
- An extra step (executing
npm run build
) would be added to the deployment process - I would have to manage changes in both .js and .ts files in Github
Given these issues, I resorted to another option — specifically, the use of the .ebextensions
directory. Files in this directory are used for advanced custom configuration of an Elastic Beanstalk environment. .ebextensions
files are written in YAML or JSON format, with a .config file extension.
The config file I used in this case configured EB in such a way that the instance would compile all Typescript code to Javascript right after running npm install
. Referencing the following article on Typescript compilation using .ebextensions, I created a file called source_compile.config
in the .ebextensions
directory and included the following script:
# source_compile.config
container_commands:
compile:
command: "./node_modules/.bin/tsc -p tsconfig.json"
env:
PATH: /opt/elasticbeanstalk/node-install/node-v10.15.3-linuxT-x64/bin/
The command tsc -p tsconfig.json
tells EB to compile the Typescript project to Javascript given the configuration file tsconfig.json
. If there is no outdir
key specified in tsconfig.json
, the compiled .js files will be located in the same locations as the corresponding .ts files.
Additionally, I needed to ensure the version of node specified in the PATH
key was compatible with the node version used by my EB environment. If needed, one should change the node version (currently written as v10.15.3) and OS name used in the PATH
key of the source_compile.config
script above.
Having gone through all these steps, I once again merged changes into my main Github branch — and voilà! The server was up and running.
Thus, by making use of an auto-deployment pipeline and .ebextensions
, I let deployment take care of itself and was able to focus more on writing server-side code in Typescript. For a product or application that is widely in use, it would be smart to set up an a separate development server connected to this pipeline and additional CI/CD tools for rigorous testing.