realtime-todo-task-management-app-nestjs-and-angular
realtime-todo-task-management-app-nestjs-and-angular copied to clipboard
NestJS and Angular Todo Task Management App with User Authentication & Realtime Communication via Websockets
trafficstars
realtime-todo-task-management-app-nestjs-and-angular
Please Remember that this is a hobby project and done in the freetime
Requirements
- Docker
How to run the Project & additional Docker Commands
- Run all services:
docker-compose up
Additional Docker Commands:
- Rebuild all Images (also run e.g. if you have a new dependency):
docker-compose build --no-cache - Run services with rebuild:
docker-compose up -d --build api frontend postgres postgres_admin - Remove all images:
docker rmi -f $(docker images -a -q) - Remove all containers:
docker rm -vf $(docker ps -a -q) - Remove all Volumes:
docker-compose down -v - Clear all
docker system prune -a --volumes
Troubleshooting
- If your container says for backend: 'Can not find Module: "XXX"' then
cd todo-apiandnpm run build
Setup steps in detail
TODO: Health check with NestJS Terminus
TODO: Health check with Terminus for Database
Docker & Docker-Compose (Database)
- Create a "docker-compose.yml" file at the top level of the project
- Add a "postgres" database service, so that NestJS can connect to it
[Video 1] NestJS Backend API
- Install the NestJS cli globally
npm install -g @nestjs/cli - Create the NestJS backend api
nest new todo-api --skip-git - Make sure that all dependencies are installed:
cd todo-apiandnpm install - Run the project and try to connect to the api via Postman
npm run start:dev - Generate all needed modules
- Auth Module:
nest generate module auth - Todo Module:
nest generate module todo - All Modules should now be generated and also be imported into the main module
- Auth Module:
- Generate the User Resource (inside the User Module)
- Create the plain User Resource (Module, Controller, Service, Entity and DTO (Domain Transfer Object)) with
cd /src/userand thennest generate resource user - Move the generated Classes to new folders, for example the service into a "services" folder in the user module
- Create the plain User Resource (Module, Controller, Service, Entity and DTO (Domain Transfer Object)) with
- Set a global prefix to your NestJS api, so that everything is served under the base path "/api"
- Open the main.ts and add
app.setGlobalPrefix('api');accordingly
- Open the main.ts and add
- Add Typeorm and the connection to the database
- Add access to environment variables via
npm i --save @nestjs/config - Add the
ConfigModule.forRoot({isGlobal: true})to the imports of our "app.module" - Add the Typeorm Packages and the Postgres ("pg") packages via
npm install --save @nestjs/typeorm typeorm pg - Add the Typeorm Module with the configurations ``
- Add access to environment variables via
- Create a "Dockerfile" so that the api can be started via docker-compose as a service
[Video 2] Angular Frontend
- Install the Angular CLI globally
npm install -g @angular/cli - Create the Angular Frontend with
ng new angular-frontend - Make sure to install all deps:
cd angular-frontendandnpm install - Set Up our main modules
- Create Public Module (here we handle Register and Login of the users):
ng generate module public - Create Private Module (handles Stuff after login, jwt protected):
ng generate module private
- Create Public Module (here we handle Register and Login of the users):
- Create dir
/modelsand dir/services - Create a Service to get a value from the todo-api:
ng generate service testand modify the return from the backend as Json - Create a proxy.conf to proxy all requests when running
ng servefor/apiagainst
our in container todo-api underhttp://api:3000& add it to our angular.json - Add a Dockerfile & then add the service to our docker-compose file
- Fix the start script in the angular package.json to run inside a container to
"start": "ng serve --host=0.0.0.0 --port 4200 --poll 2000"
Explanation: https://stackoverflow.com/questions/46778868/ng-serve-not-working-in-docker-container The "--poll 2000" is needed to get hot reload working Other solution could be to use for ex. nginx
[Video 3] NestJS User Api (Register and Login)
To be able to later build our Realtime Todo Api, we always need to know who the user is and what roles/rights does he has. For this we provide Endpoints for Login and Register. The Login Endpoint will return a JWT, which will then be attached to every request against the backend and it should always be validated.
- Read the Docs about Authentication: https://docs.nestjs.com/security/authentication
- Install the dependencies via:
npm install --save @nestjs/passport passport passport-jwt passport-local bcrypt @nestjs/jwt - Create the Jwt Strategy under path
/auth/strategies/jwt.strategy.ts(read more in the docs at 1.) - Create the Auth Service under path
/auth/services/auth.service.ts - Create a Guard under path
/auth/guards/jwt.guard.ts - Update imports/providers/... for the AuthModule (make sure to export the authService, so we can use it in other modules)
- Make sure to add the environment variable for the JWT_SECRET to the docker-compose file
- Import the AuthModule into the UserModule
- Create the User Entity & the User Interface
- Install the package class-validator to check our dto values
npm i --save class-validator class-transformer - Make sure to enable the class validation in main.js by adding:
app.useGlobalPipes(new ValidationPipe()); - Create the dtos for login and register
- Create the login and register Endpoints and Service Functions
- Run the Api with docker and test it with Postman
[Video 4] Angular Login & Register Component & Connect with Api
- Install Angular Material:
ng add @angular/material - Remove old TestService
- Create Diretories in
angular-frontend/src/app/public:/validators,/components,/services - Create a
public-routing.module.tsfor all routes in our public module - Create a
public.private-module.interfaces.tsfor our interfaces - Create the components
LoginComponentandRegisterComponentvia the cli with the commandng generate component loginandng generate component register - Update the "public-routing.module.ts" and the "app-routing.module.ts"
- Create the Service
UserServiceto make the login and register requests against the aping generate service user - Add the login and register functions to the UserService
- Import the
MatCardModule, MatFormFieldModule, ReactiveFormsModule, MatInputModule, MatButtonModulein the PublicModule - Import the
MatSnackbarModulein the AppModule - Create the html, scss and logic for the LoginComponent
- Cretate the html, scss and logic for the RegisterComponent
[Video 5] Nest.js Websockets with Socket.io, Authentication Middleware & testing with Postman
- Read the docs: https://docs.nestjs.com/websockets/gateways
- Install necessary deps:
npm i --save @nestjs/websockets @nestjs/platform-socket.io socket.io - Create file
/todo-api/auth.middleware.ts - Implement the middleware there to authenticate the user who is making a request
- Add the UserService to the Exports from the user.module
- Here you can read more about lifecycles: https://docs.nestjs.com/faq/request-lifecycle#summary
- Add the Middleware Consumer to the
app.module.ts(on top level of our application) - Generate a Websocket Gateway via
cd /todo-api/src/todo-nest generate gateway todo - Import UserModule and AuthModule to TodoModule
- Test the gateway with postman
[Video 6] Angular - add Jwt Auth handling (with Socket.io & normal http Api)
- Read what the "@auth0/angular-jwt" package does: https://www.npmjs.com/package/@auth0/angular-jwt?activeTab=readme
- Then install it:
cd angular-frontendnpm i @auth0/angular-jwt - Update Angular (https://update.angular.io/?v=14.0-15.0) to work with recent package version:
ng update @angular/core@15 @angular/cli@15andng update @angular/material@15 - Remove all legacy imports after the angular update
- Then install it
cd angular-frontendnpm install socket.io-client - Add "JwtModule" to Imports from "@auth0/angular-jwt" in "app.module.ts"
- Make sure to save the jwt from the response of our login request (user.service.ts - login())
- Add a "getLoggedInUser()" to our user.service to get the user from the jwt
- Add a "dashboardComponent" to our private module,
cd .\angular-frontend\src\app\private\&mkdir components&cd components&ng g c dashboard - Add a "PrivateRoutingModule" to our routing module & import it in our "private.module"
- Update the "app-routing.module" and lazy load the private Module
- Update our "login.component" to navigate to the dashboard after login
- NestJS, allow cors in our gateway for our angular requests & update the Auth handling (postman will then not work anymore, because no support for auth so far - to work you have to use a raw connection and send the auth token along)
- Add a "todoService" to our private Module
cd .\angular-frontend\src\app\private\&mkdir services&cd services&ng g s todo - Call our function from the dashboard onInit and see if everything works
[Video 7] Angular - Init ToDo List with Drag and Drop
- Create a Card Component,
cd .\angular-frontend\src\app\private\components\ng g c card - Create an
private-module.interfaces.tsfile in our private module and create aTodoIteminterface - Add DragDropModule to the imports of our
privateModule - Implement the example from https://material.angular.io/cdk/drag-drop/examples
- Rename CSS Classes
- Create TestData with the TodoItem interface, eg.g. 'testData: TodoItem[] = [....]'
- Update Styles and Html
[Video 8] NestJS/Angular - handle Todos with Websocket Connection
- Create a todo interface file in the todo module
src/todo/todo.private-module.interfaces.ts - Create the Todo Interface
- Create a folder entities in our todo module
cd src/todomkdir entities - Create a file todo.entity.ts
cd entitiestouch todo.entity.ts - Create the todo.entity.ts
- Create the todo.service to handle the todos and save them in the database
- Create a interface for the connection against our service in our
todo.private-module.interfaces.tsand also add aconnected-user.entity.tsto handle the connections of a user, so that we can send push messages - Add a
connection.service.tsto implement the logic for handling and saving our connections - Save the connection to our database when a user connects against our gateway (in our gateway.handleConnection)
- Add the gateway.onHandleDisconnect
- Add in our Angular frontend a listener to the 'todos' event with socket.io
- Add a setup Service in Nestjs to add some todos on startup to our database
- Check that when we connect with our Frontend against the gateway, that we print the Todos for the 'todos' event to our console
[Video 9] Angular - create Todos /w Reactive Form & Dialog
- Create new Component
ng g c create-todoin our Angular Project & don't forget ro reference it in the imports of the private module - Add ElementRef for the
create-todo.componentto our dashboard, as well as a button to open the ref - Create FormGroup in 'create-todo.component'
- Create form in template für 'create-todo.component'
- Log out Form onSubmit()
- Rename
interfaces.tstoprivate-module.interfaces.tsand create also a const fileprivate-module.consts.tsand refactor a bit
[Video 10] NestJS & Angular - handle Events & messages for Todos
Handle Todo Create
- NestJS: Listen @SubscribeMessage('addTodoItem') Event and create a todoItem if the Event is received
- Add Listener in todo.gateway.ts
- Add save Function to our todo.service.ts
- Add findAll() function to connection.service.ts
- Distribute the createdTodoItem to all connected Clients/users
- Angular
- Add a saveTodo() method to our todo.service.ts and emit an event matching to our todo.gateway
- Call this saveTodo() when we hit save in our create-todo.component
- Add a listener to the 'addedTodo' Event so we can catch new Todos and subscribe to it in our dashboard
- Add a behaviorSubject to our todo.service, so that we can subscribe in our dashboard to our items
- Then in our dashboard component fix some stuff, for example how to filter the items
Handle Todo Update
- NestJS:
- Add Listener in todo.gateway.ts and emit the updatedTodo after update to all clients
- Add update Function in todo.service.ts
- Angular:
- Add a listener to our todo.service.ts
- Add a function to send a todoUpdate to our api
- call the todo.service.ts in our dashboard.component, so that we receive updates from our api
- call the updateTodo from our service after our drop() function in the dashboard.component