This article was originally published on my personal blog.
Introduction
In the context of my current job, I wanted to evaluate the various existing backend frameworks based on NodeJS. Why is that? The only Node.js backend framework I had ever used so far was express, which is an awesome light-weight framework, but that doesn't have any opinion whatsover on how you should structure your app.
During my investigation, I came across NestJS several times. The most appealing thing to me was its thorough documentation and its large ecosystem. I was especially interested in the OpenAPI integration, which I knew could greatly improve the frontend development experience when coupled with a code generator.
In the end, I decided to create a small POC to see whether it would be a fit.
Specifications of the project
Functional requirements
The POC is going to be a minimal, hideous "TODO list" app (styling is not in the scope of this endeavor).
In this POC, I'll be able to:
- Add tasks,
- Remove tasks,
- List all tasks
Technical requirements
- Use Typescript everywhere.
- NestJS for the backend.
- React for the frontend.
- Tasks are saved in a Postgres DB.
- Redis is used for caching responses.
- API endpoints are documented using OpenAPI.
- API endpoints' parameters are validated in the backend.
- Frontend code related to the API endpoints is auto-generated.
- The development environment is set up in docker.
- Monorepo containing both the backend and the frontend.
Building the project
The source code for this part of the project is available here: https://github.com/arnaud-cortisse/trying-out-nestjs-part-1.
Setting up Docker dev environment
Since I took Docker and Kubernetes: The Complete Guide and Microservices with Node JS and React courses, I've been a huge fan of setting up my dev environment inside docker instances instead of setting it up directly on my machine. I love the fact that I can have everything up and running with a single command, without having to worry about dependency conflicts (is my current version of NPM compatible with that project?, etc.).
A few commands to execute
- Install the Nest CLI:
npm i -g @nestjs/cli
(you might need to prefix it withsudo
) - Create an empty folder:
mkdir todo-list-app
- Go inside the folder:
cd todo-list-app
- Init npm package:
npm init -y
- Init git, if you want to save your work:
git init
- Create frontend folder:
mkdir -p packages/react-app
- Create backend folder:
mkdir -p packages/nestjs
- Create the React app:
npx create-react-app packages/react-app --template typescript
- Create the NestJS app:
nest new packages/nestjs
- Delete the .git folder automatically created by NestJS:
rm -rf packages/nestjs/.git
- Create frontend env variables file:
touch packages/react-app/.env.dev
- Create backend env variables file:
touch packages/nestjs/.env.dev
- Create frontend Dockerfile for dev environment:
touch packages/react-app/Dockerfile.dev
- Create backend Dockerfile for dev environment:
touch packages/nestjs/Dockerfile.dev
- Create docker-compose file for dev environment:
touch docker-compose.yml
- Create frontend .dockerignore file:
touch packages/react-app/.dockerignore
- Create backend .dockerignore file:
touch packages/nestjs/.dockerignore
A few files to fill / change
packages/react-app/Dockerfile.dev
FROM node:alpine WORKDIR /app COPY package.json . RUN npm install --legacy-peer-deps COPY . . CMD ["npm", "run", "start"]
--legacy-peer-deps
is just a temporary fix for https://github.com/facebook/create-react-app/issues/9515.
packages/nestjs/Dockerfile.dev
FROM node:alpine WORKDIR /app RUN npm install -g @nestjs/cli COPY package.json . RUN npm install COPY . . CMD ["npm", "run", "start:dev"]
Nothing crazy here, but we just make sure we install the NestJS CLI globally before doing anything else.
packages/react-app/.env.dev
REACT_APP_BACKEND_SCHEMA=http REACT_APP_BACKEND_HOSTNAME=localhost REACT_APP_BACKEND_PORT=3001 CHOKIDAR_USEPOLLING=true
CHOKIDAR_USEPOLLING
is required when developing inside a docker container and using create-react-app (https://github.com/facebook/create-react-app/issues/1049#issuecomment-261731734).
The other variables are defined so that we can communicate with the NestJS API.
packages/nestjs/.env.dev
NEST_PORT=3001 PGHOST=postgres PGPORT=5432 PGUSER=postgres PGPASSWORD=example PGDATABASE=postgres REDIS_HOST=redis REDIS_PORT=6379 REDIS_PASSWORD=password REDIS_TTL=10
We define the port on which NestJS will run as well as the Postgres and Redis configs.
packages/react-app/.dockerignore
node_modules
We don't want the local node_modules
folder to be copied over the instance.
packages/nestjs/.dockerignore
node_modules
docker-compose.yml
version: "3.5" services: nestjs: build: context: ./packages/nestjs dockerfile: Dockerfile.dev env_file: - ./packages/nestjs/.env.dev ports: - 3001:3001 volumes: - ./packages/nestjs/:/app - /app/node_modules react_app: build: context: ./packages/react-app dockerfile: Dockerfile.dev env_file: - ./packages/react-app/.env.dev ports: - 3000:3000 volumes: - ./packages/react-app/:/app - /app/node_modules postgres: image: postgres:13.1 environment: POSTGRES_PASSWORD: example ports: - 5432:5432 redis: image: redis:6.2-rc1 environment: REDIS_PASSWORD: password redis_commander: image: rediscommander/redis-commander:latest restart: always environment: - REDIS_HOSTS=local:redis:6379 ports: - 8081:8081 depends_on: - redis
-
nestjs
is our backend. -
react_app
is our frontend. -
postgres
is going to be used to store the tasks. -
redis
is going to be used by NestJS to cache responses. -
redis_commander
is just a tool that allows us to examine the Redis DB quickly. -
volumes
under react_app and nestjs is key to get auto-reload whenever you modify files inside your editor. The only annoying thing with this setup is that you'll need to rebuild your docker images whenever you add a new dependency inside your node_modules (see https://github.com/BretFisher/node-docker-good-defaults for fixes).
packages/nestjs/src/main.ts
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(process.env.NEST_PORT); } bootstrap();
Modify the port the app is listening to using the process.env.NEST_POST environment variable (defined in packages/nestjs/.env.dev).
Test current setup
You should now be able to start your dev environment typing docker-compose up
in the root directory.
You can then go to the following addresses:
-
localhost:3000
--> React app. -
localhost:3001
--> NestJS app. -
localhost:8081
--> Redis Commander (which should be connected to your Redis instance).
Final words
With the current state, I've a working dev environment inside dev containers. All I have to do to get started is docker-compose up
(sometimes, I have to do a docker-compose up --build
, depending on whether or not new npm packages have been installed).
Whenever I update any .ts
files in my code editor, the apps are reloaded accordingly, making it a very pleasant dev experience for the task at hand: asserting whether or not NestJS is going to be a good fit for me by developing a POC.
Top comments (0)