Building a NestJS API — A Peek Through My Search History

Recently, I was tasked with creating our API. I chose NestJS to experiment with since its tagline:

A progressive Node.js framework for building efficient, reliable and scalable server-side applications.

really jived with me. I was excited to tackle this problem and thought with my JavaScript background — learning a new library while also learning TypeScript couldn’t be that hard, could it? Well, 500+ searches and a pretty good API foundation later, NestJS has lived up to the excitement. This article takes you through my development hurdles and successes.

A selection of my searches will be displayed in the format: search🔎 as well as quick answers to some. Additionally, on the search itself, I’ve linked to specific articles or posts that solved those queries.

API requirements

  • Typescript, TypeORM, GraphQL Support
  • Basic Splash Page that shows ‘API Operational’ with CameraKit Logo/etc
  • User scheme to support sign-in, sign-up, portal access, and user settings

Doing Research

It starts with the nestjs🔎 search. One that I had used seven more times over the course of development, most assuredly to get to the main NestJS site — a great source of documentation and best practices. I quickly realized I needed to take a step back to see what NestJS does. nestjs explanation🔎landed me this great article from Auth0. The next search nestjs vs nextjs🔎 seems a bit out of place, but to make things a bit more confusing, the main CameraKit website began transitioning to NextJS for server-side rendered React; which, of course, has nothing in common with NestJS besides very similar pronunciation.

Constructing the Scaffold

I soon gained a bit of confidence as I started branching off to the beginning steps of making an API— nestjs user authentication🔎 which led me down the path of using nestjs passport🔎, and then getting a bit more serious with the keyword-shotgun approach scaffold nestjs typescript typeorm graphql🔎 — a surprisingly helpful search which pointed me towards this GitHub Repository. I will implement some of the ideas here, just to have them not work later, though, after an update changes the interfaces. I then realized I should include Linting; tslint airbnb rules🔎, tslint config json extend🔎, and tsconfig.json🔎 helped set up TsLint with my preferred Airbnb Config. This was not any harder than installing and adding a line in tslint.json.

Adding a Database

Apollo support was next on my todo list:

🔎 
- scaffold tests typescript typeorm grapql
- apollo server express
-
nestjs grapql scaffold

Though I ran into what I thought was a problem with a package version Module has no exported member ‘graphiqlexpress’🔎. I solved this by adding @types/grpahql. Only after some confusion: Removing carrots from dependencies🔎, What are carrots in package.json🔎.

After adding TypeORM (with the help of the docs), and deciding to use PostgreSQL as the database, I was ready to start writing some code.

🔎
-
add typeorm to nestjs
- best db to use with typeorm
- what database to use with typeorm
- typeorm and mongodb
- set up postgresql server on windows

Making the Index Page

I like starting with a splash page for the API to have a quick and dirty way to confirm requests are being served. This involved getting the file system

🔎
-
nestjs index example
- import path

- favicon

Including the favicon

🔎
- favicon

- favicon include in site
- Nestjs fastify “favicon”

As well as getting the image and font right

🔎
-
Roboto text
- font shorthand
- vertically align text in div
“CameraKit API Operational” on the index page

“nest can’t resolve dependencies of the”🔎

This search, and similar phrases, came up multiple times while developing with NestJS. An error might look something like:

[ExceptionHandler] Nest can't resolve dependencies of the UserController (UserService, ?). Please make sure that the argument at index [1] is available in the current context.

and I did not know where to look at first. I thought there was something I didn’t know about nestjs interface🔎 or typescript interface🔎, but there were some helpful clues. First, I looked inside the user.controller, and realized that (UserService, ?) points towards its constructor:

You can see NestJS knows about the UserService, and even though I believed I imported the SubscriptionService

nestjs imports exports providers🔎 showed me it both needs to be exported from subscription.module

As well as imported from the user.module

Using Different Development Environments

Because I develop on a Windows computer and Mac laptop, programs and services outside of the great NPM need to be installed: install postgres mac🔎, debugged: invalid active developer path🔎 and set up: create role postgresql🔎 postgresql mac service cli🔎 default postgres password🔎 create user postgres🔎. After getting back up to speed on the Mac side I begin implementing user registration and login. nest authentication login api🔎 and nestjs authentication🔎 kept pointing me towards nestjs/passport as well as the authentication technique in the NestJS docs. I eventually went with their “best practice”, and decided to follow the JWT approach.

🔎
- jwt passport persistent sessions
- express session
- nestjs passport session
- passport session
- passport jwt session
-
debugging nestjs
- auto attach vs option

Out of those searches, I realized I did not want to use express session as well as learning how to debug from here. For Visual Studio Code users turn on Auto Attach:

Visual Studio Code settings — search for “auto attach” and flip it on

and create nodemon.jsonwhich runs with nodemon.

Adding custom Configuration and Security

A few searches later,

🔎
- what passport strategy to use for logins local vs jwt
-
what’s the point of jwt
- saving jwt in local storage
- nestjs middleware cookie session login authentication
- passport extend strategy
- nestjs guard passport
-
use guards nestjs

I had the API hooked up to the web client in what I thought was a working state. I realized later that the nice comments in the example GitHub repository would need to be followed and implemented. After some worrying: authentication with jwt secure🔎 jwt brute force🔎 generate good jwt secret🔎 I needed a place to store my newly generated secret . I turned to dotenv🔎 to handle configuration files, and of course had to search how to name something starting with a dot in finder🔎. I started off with using purely environment variables and accessing them within the code from process.env.ENVIRONMENT_VARIABLE, but soon after nestjs configuration documentation🔎, I found my way back to the NestJS documentation which suggests using joi npm🔎 for validation. I followed the advanced configuration and made a global module nestjs global modules🔎. To round out my “security audit” I read and implemented nestjs force ssl🔎 for production

After quite a few searches

🔎
- express-sslify nestjs
- ssl “nestjs”
- nestjs redirect https
- res.redirect is not a function express-sslify

- 301 moved permanently from disk cache
- redirected you too many times https express

began to add CSRF protection csurf🔎 until I decided it was unnecessary at this time.

Deploying to Heroku

The next step was deployment to Heroku. Of course it did not work on the first try. I fixed bcrypt failing heroku🔎 by installing a new version, same with ts-node not found🔎, and an error that stumped me for a while — web process failed to bind to $PORT within 60 seconds of launch wait longer🔎. I originally thought I might need more time, but I actually needed to dynamically set the port according to the environment variable that Heroku assigns. When that still did not fix the problem app.listen string or number🔎 nestjs heroku port🔎 nestjs heroku deploy🔎 showed me the host was required to be set to 0.0.0.0.

I was then back to errors that I had seen before! This time concerning the environment variables. I was confused how the variables worked on Heroku, and realized after a bit of searching:

🔎
-
heroku dotenv
- push file to heroku
- put dotenv file in Heroku
-
“Dotenv” “heroku” site:stackoverflow.com
- dotenv parse current process env variables
- “Get all” environment variables dotenv

dotenv does not work with Heroku because the .envfile should never be committed. This is for good reason since your secrets would not be secret. Therefore, I solved the problem by either using the file (if found) or checking for environment variables that are already set and then validating them.

🔎
- get keys from object into different object specific
- get variables out of process.env
- assign specific object properties to new object
-
reduce mdn

With that done, more errors came up that looked like a typescript compilation problem, but with weird errors like cannot find global type ‘Boolean’.

🔎
-
‘AsyncIterator’. Do you need to change your target library?
- cannot find global type ‘Boolean’.
- cannot find name ‘Blob’
-
invalid module name in augmentation. Module ‘graphql/type/definition’
- unexpected token { import
-
tsc not converting imports

Almost everyone pointed towards fixes through tsconfig.json, yet suggestions to change the lib to X would remove Y error, but add Z error. I ended up with a tsconfig.jsonthat worked, but unfortunately I could not tell you why.

Still, TypeORM was throwing errors, and it was at this time I realized the TypeORM entities

🔎
-
no repository for “contact” was found
- typeorm entities
-
looks like this entity is not registered in current “default” connection

were correctly found during development in the /src directory, but since production moves the files to the/dist directory, the entities were not correctly found. I changed where TypeORM looks for the entity files by changing src/**/*.entity.tsto dist/**/*.entity.js.

Conclusion

To great relief, the server successfully deployed and started serving requests. I have since gone on to add more functionality as well as run into more problems, but that’s development! Thanks for reading, and if you’re interested: a full list of my searches!

CameraKit helps implement reliable Android camera into your mobile app easily. Open-source, performant, and optimized for tackling the complex Camera APIs on Android.