If you’re familiar with Node.js basics, start by running the code. As you explore and read the comments, you’ll gradually understand how it works.
- Click on the header it will redirect to the respective codebase, download the code and run it.
⁙⫸ This boilerplate is perfect for starting a project without any schema
, controller
, routes
, or authentication
.
- Setup: Rename the
.env.example
to.env
and replace theMONGO_URL=""
with your own MongoDB URL. - Installation: Run
npm install
oryarn install
to install the necessary dependencies. - Running the Project: Use
npm run dev
oryarn dev
to start the project.
- If any dependency is missing, install it manually.
- If any dependency needs to be updated, update it manually or use
npm outdated
oryarn outdated
to check the outdated dependencies. Then usenpm update
oryarn update
to update the dependencies. - You can change the port number in the
.env
file as per your requirements.
⁙⫸ With Authentication + User Operations
- Receive an email and password from the user.
- Hash the password for security.
- Save the email and hashed password in the database.
- When the user logs in with their email and password, verify the credentials.
- If the credentials are valid, generate a session token.
- Send the session token to the client.
- The client stores the token and sends it back in the header for subsequent requests.
- For each request, verify the session token.
- If the token is valid, process the request and send the data to the client.
- User Schema (Mongoose): Includes
email
,username
, andauthentication
(which consists ofpassword
,salt
, andsessionToken
). - User Type Defined
- Middleware: This includes two types:
- Owner: Ensures the user is the owner of the account.
- Authenticated: Ensures the user is authenticated.
- Error Handler: Handles the error.
- Utils: This includes crypto for salt and hash password and generate session token.
- Controller: This includes
auth
(withregister
andlogin
methods) andUser
( withgetAllUser
,updateUserById
, anddeleteUserById
methods).
- Auth
register
: POST request to/api/v1/auth/register
- Example body:
{ "email": "[email protected]", "password": "Subham@123#86!GitHub_DeVeLoPeR", "username": "Subham" }
login
: POST request to/api/v1/auth/login
- Example body:
{ "email": "[email protected]", "password": "Subham@123#86!GitHub_DeVeLoPeR", "username": "Subham" }
- User
getAllUser
: GET request to/api/v1/users
. This is wrapped with theauthentication
middleware, so you need to log in first to get the data.updateUserById
: PATCH request to/api/v1/users/{id}
. This is wrapped with theauthentication
andowner
middleware, so you need to log in first to update the data.- Example body:
{ "username": "XAM" }
deleteUserById
: DELETE request to/api/v1/users/{id}
. This is wrapped with theauthentication
andowner
middleware, so you need to log in first to delete the data.
○ Frontend: Next.js
○ Backend: Express.js
- Access Token: A short-lived token that is used to access protected resources on behalf of the user.
- Refresh Token: A long-lived token that is used to refresh the access token when it expires.
- JWT - JWT is a string that has three parts separated by dots. Each part is a base64url encoded string of the header, payload, and signature.
-
JWT -
HEADER
.PAYLOAD
.SIGNATURE
HEADER
- "alg": "HS256", "typ": "JWT"PAYLOAD
- sessionID: "1234567890", "email": "[email protected]", "username": "Subham"(Don't store sensitive data like password)- encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64')
SIGNATURE
- crypto.createHmac('sha256', secret).update(encodedHeader + '.' + encodedPayload).digest('base64')
-
⁙⫸ With Authentication + User Operations
- User logs in with their credentials (username, password, etc.).
- Server verifies the credentials. If they're valid, the server generates an Access Token and a Refresh Token.
- The Access Token is a short-lived token (usually about 15 minutes to 1 hour) that carries the user information necessary to access a resource.
- The Refresh Token is a long-lived token (usually about 2 weeks to 6 months) that's used to request new Access Tokens.
- The Server sends both tokens to the client.
- Client stores the tokens. The Access Token is used for making authenticated requests.
- When the Access Token expires, the client uses the Refresh Token to request a new Access Token.
- Server verifies the Refresh Token and issues a new Access Token (and possibly a new Refresh Token).
- If the Refresh Token is expired or invalid, the user will need to authenticate again to get a new pair of tokens.
You will get two folders[Stateful
, Stateless
] in model
,middleware
,routes
,utils
and controller
folder.
- Stateful: This is the cookie-based authentication with server token.
- Stateless: This is the JWT-based authentication with access token and refresh token.
purpose : To understand the difference between stateful and stateless authentication.
Client
: This includes our frontend code using Next.js 14Server
: Here you will get all the server-side code.
-
Components
-
Username
- Formik Validation
- Toast (initialValues, validate)
- Error Handling (Yup)
- Types (validation.ts)
- Redux (userSlice)- For storing the username
- Username Validation using API (Check if the username is already present in the database or not only authorized/register users from the database are permitted to proceed to the next page)
-
Password
- Formik Validation
- Toast (initialValues, validate)
- Error Handling (Yup)
- Types (validation.ts)
- V1(Async Thunk and AXIOS ) and V2 (RTK Query and js-cookie and AXIOS)—For storing the access token and refresh token (Both implementations are correct, but V2 is the best practice)
-
Recovery
- OTP to reset password
- OTP verification
-
Reset
- Formik Validation
- Toast (initialValues, validate)
- Error Handling (Yup)
- Types (validation.ts)
- Reset Password using API
- Handle the error (API)
- Handle the ResetSession (API)
-
Register (Register User)
- Convert: convert the file to a Base64 string
- Formik Validation
- Toast (initialValues, validate , User Existence)
- Error Handling (Yup)
- Types (validation.ts)
- Email Send API
- Register User using API
- Delete Profile Picture
- Get a Profile Picture
- Hook - useMutation (react-query)
- Hook - useQuery (react-query)
- S3 Bucket (AWS) (READ HERE - S3 Bucket)
-
Profile
- Convert: convert the file to a Base64 string
- Formik Validation
- Toast (initialValues, validate)
- Error Handling (Yup)
- Types (validation.ts)
- Refresh Token (Cookie) & Access Token (Redux Store)
- Use RTK Query (react-query) for put request (update user data)
- Use RTK Query (react-query) for get access token and refresh token
- Redux Persist (Redux Toolkit)—For storing the access token when the user refreshes the page it will not log out the user
- Username from token (JWT Decode) (Fetch Hook)
-
-
api - Using Axios (Custom Hooks)
- Authentication: For checking the user is authenticated or not
- Auth:
- Login: Log in the user
- Register: Register the user
- Reset Password: Reset the password
- Mail:
- Send Mail: Send mail to the user according to the user action
- OTP:
- Generate OTP: Generate OTP for the user
- Verify OTP: Verify the OTP
- User
- Get User: Get the user data
- Update User: Update the user data
- Delete Profile Picture: Delete the profile picture from the S3 bucket
- Get Profile Picture: Get the profile picture from the S3 bucket
-
REDUX - Using Redux Toolkit
- user-userSlice-For storing the username
- user-profilePicOwnerSlice-For storing the profile picture owner
-
Hook
- axios—for making the API call
- fetch—useFetch (custom hook) for fetching user data
- useMutation—useMutation (react-query) for updating the user data
- useQuery—useQuery (react-query) for fetching user data
-
Middleware - Protected Routes (User, Profile)
- User Type Defined
- User Schema (Mongoose): Includes
username
,password
,email
,firstName
,lastName
,mobile
,address
,profile
. - utils:
- bcrypt—for hashing the password
- token -
jwtAccess
(create access token),jwtRefresh
(create refresh token),tokenEncrypt
(encrypt the token),tokenDecrypt
(crupto for encrypt and decrypt the refresh token) ,tokenVerify
(verify the token) ,saveToken
(save the Refresh Token in the database)jwtSign
(sign the token) ,jwtVerify
(verify the token)... more - mail -
sendMail
(send mail to the user)(nodemailer) normal mail and template mail - gmail-smtp -
sendGMail
(send mail to the user)(nodemailer) normal mail and template mail - gmail0Auth -
sendGMail0Auth
(send mail to the user)(nodemailer) normal mail and template mail - Check all the steps in 0AuthSetps.md
- Middleware:
- Owner: Ensures the user is the owner of the account.
- Authenticated: Ensures the user is authenticated and the token is valid.
- Error Handler: Handles the error.
- Controller: This includes
auth
-register
(register a user),login
(log in a user),generateAccessTokenHandler
(Refresh Token Generate),logoutHandler
(Clear token),verifyUser
(verify if the user exists in the database before login), checkUserExistence (check if the user exists in the database)user
-getUser
(get user data without a password),updateUser
(update user data)OTP
-generateOTP
(Generate OTP),verifyOTP
(Verify the OTP)
- Routes:
-
Auth
register
: POST request to/api/v2/auth/register
- Example body:
{ "username" : "codexam_123", "password" : "Codexam@123", "email": "[email protected]", "firstName" : "Subham", "lastName": "Maity", "mobile": "1234567890", "address" : "india", "profile": "" }
login
: POST request to/api/v2/auth/login
wrap withverifyUser
controller- Example body:
{ "username" : "codexam_123", "password" : "Codexam@123" }
reset password
: PUT request to/api/v2/auth/resetPassword
wrap withverifyUser
controller- But before that you have to generate OTP and verify it then you can reset the password
api/v2/auth/generateOTP?username=codexam_123
api/v2/auth/verifyOTP?username=codexam_123&code=427638
- Then you can reset the password
- Example body:
{ "username" : "codexam_123", "password" : "Codexam@123" }
-
Auth/VerifyUser
POST request to /api/v2/auth/verifyUser
- Example body:
{ "username" : "codexam_123" }
logout
: DELETE request to/api/v2/auth/token
- Example body:
{ "refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" }
-
Auth/Token
get refressToken
: POST request to/api/v2/auth/token
- Example body:
{ "refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" }
logout
: DELETE request to/api/v2/auth/token
- Example body:
{ "refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" }
-
User
get User
: GET request to/api/v2/user/codexam_123
herecodexam_123
is the usernameupdate User
: PUT request to/api/v2/updateuser?id=658ec47dcb30d4ea193d457a
here658ec47dcb30d4ea193d457a
is the id wrap withauthenticated
andowner
middleware- Example body:
{ "username" : "codexam_123", "password" : "Codexam@123", "email": "[email protected]" }
-
Auth/OTP
get OTP
: GET request to/api/v2/auth/generateOTP?username=codexam_123
herecodexam_123
is the username you have to pass in the query parameter and wrap withauthenticated
middlewareverify OTP
: GET request tohttp://localhost:5050/api/v2/auth/verifyOTP?username=codexam_123&code=427638
herecodexam_123
is the username and427638
is the OTP you have to pass in the query parameter and wrap withauthenticated
middleware
-
Auth/createResetSession
get Session Flag
: GET request to/api/v2/auth/createResetSession
you will get flagtrue
orfalse
if the flag istrue
then you can reset the password
-
MAIL/EMAIL
send mail
: POST request to/api/v2/mail-v1/registerMail
- Example body:
{ "username" : "codexamA_123", "userEmail" : "[email protected]", "text" : "New User Registered", "subject" : "New User Registered" }
-
MAIL/GMAIL/SMTP
-
Go to https://myaccount.google.com/security
-
Enable 2-Step Verification
-
Create App Password https://myaccount.google.com/apppasswords
send mail
: POST request to/api/v2/mail-v1/registerGMail
- Example body:
{ "username" : "codexamA_123", "userEmail" : "[email protected]", "text" : "New User Registered", "subject" : "New User Registered" }
-
MAIL/GMAIL/0Auth Check all the steps in 0AuthSetps.md
send mail
: POST request to/api/v2/mail-v1/registerGMail0Auth
- Example body:
{ "username" : "codexamA_123", "userEmail" : "[email protected]", "text" : "New User Registered", "subject" : "New User Registered" }
-
S3 Bucket Check all the steps in S3 Setup and also understand how I set up the S3 bucket in the frontend and backend Here
Get Owner Profile Pic
: POST request to/api/v2/storage-v1/s3/get-owner-image
- post with user's profile data
- Example body:
{ "requestedImageName":"4515e1be62e98" }
Get Owner Profile Pic
: POST request to/api/v2/storage-v1/s3/get-owner-image
- post with user's profile data
- Example body:
{ "requestedImageName":"4515e1be62e98" }
Delete Owner Profile Pic
: POST request to/api/v2/storage-v1/s3/remove-owner-image
- post with user's profile data
- Example body:
{ "requestedImageName":"4515e1be62e98" }
Register User Profile Pic
: POST request to/api/v2/storage-v1/s3/images
- post with image data in binary format
Get User Profile Pic
: GET request to/api/v2/storage-v1/s3/images
- Give you last uploaded image key
Delete All Keys
: DELETE request to/api/v2/storage-v1/s3/clear-database-s3
- Delete all the keys from the database
-
Sure, here's a more structured and detailed version of your content:
Redis is an open-source, in-memory key-value data store. It is versatile and can be used as a database, cache, and message broker. Redis supports various data structures such as Strings, Hashes, Lists, Sets, and more. It provides high availability via Redis Sentinel and automatic partitioning across multiple Redis nodes with Redis Cluster.
- Download Docker Desktop from the official Docker website.
- Install Docker Desktop and open it.
- Open the terminal and run the following command to start the Redis server and Redis Commander:
docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest
This command will run the Redis server on port 6379 and Redis Commander on port 8001.
- Open a web browser and navigate to
localhost:8001
to view the Redis Commander. - Go back to the terminal and run
docker ps
to check if the Redis server is running. If it's not, rundocker start redis-stack
.
Now, you can use the Redis server in your project.
- Run
docker ps
in the terminal to check the running container and copy the container id. - Use
docker exec -it <container_id> bash
to open the bash terminal of the container. For example:docker exec -it 7b7e bash
. - Type
redis-cli
to open the Redis CLI terminal. - Type
ping
to check if the Redis server is running. If you getPONG
, then the server is running.
- open the 5.redis folder and run the following command
npm run string
npm run list
npm run set
npm run hash
npm run sortedset
npm run stream
npm run bitmap
npm run geo
npm run pubsub
npm run dev
- open the server/src/client.ts setup the redis client
import Redis from "ioredis";
const client = new Redis({
host: 'localhost', // replace with your host, if not localhost
port: 6379 // replace with your port, if not 6379
});
export default client;
- open the 5.redis folder and you will get the following files
- string.ts
- list.ts
- set.ts
- hash.ts
- sortedset.ts
- stream.ts
- bitmap.ts
- geo.ts
- pubsub.ts
- src/client.ts
- src/todos.ts
You can use probabilistic data structures to store elements with a probability of false positives. It is similar to a set in other programming languages. Probabilistic data structures are often used to implement other data structures like bloom filters.
READ HERE: https://redis.io/docs/data-types/probabilistic/t-digest/
A time series data structure is a collection of elements with a timestamp. It is similar to a map in other programming languages. Time series data structures are often used to implement other data structures like time series.
READ HERE: https://redis.io/docs/data-types/timeseries/
- Step 1. Initialize a Node.js project.
- Copy the template what I already created for you.
npm i
- replace the .env file with your own database credentials.
npm start
to run the project.
- Third party api: https://jsonplaceholder.typicode.com/todos
- We will try to speed up the api response time using redis.
npm i axios
to install axios.
- Open server/src folder makes a new file todos.ts
import express from "express";
import axios from "axios";
const todos = express.Router();
todos.get("/todos", async (req, res) => {
try {
const { data } = await axios.get("https://jsonplaceholder.typicode.com/todos");
res.json(data);
} catch (error:any) {
console.error("Error fetching todos:", error.message);
res.status(500).json({ error: "Internal Server Error" });
}
});
export default todos;
- Open server/src/app.ts and add the route to the express app.
app.use("/api/v1/", todos);
-
now run
npm run dev
and go to http://localhost:5050/api/v1/todos to see the response. -
I have already setup Morgan for logging for response times, however, you can open Postman and check the response times.
-
In the first fetching it takes 403ms to fetch the data from the api, and after multiple requests, it takes 120ms–351ms to fetch the data from the api.
-
Now try to implement caching using redis.
- you can find the client.ts file in the server/src folder.
import Redis from "ioredis"; const client = new Redis( { host: 'localhost', // replace with your host, if not localhost port: 6379 // replace with your port, if not 6379 } ); export default client;
- Now open server/src/todos.ts and import the redis client.
import client from "./client"; todos.get("/todos", async (req, res) => { try { const todos = await client.get("todos"); if (todos) { console.log("Fetching from redis"); return res.json(JSON.parse(todos)); } const { data } = await axios.get("https://jsonplaceholder.typicode.com/todos"); client.set("todos", JSON.stringify(data)); console.log("Fetching from api"); res.json(data); } catch (error:any) { console.error("Error fetching todos:", error.message); res.status(500).json({ error: "Internal Server Error" }); } });
- Now run
npm run dev
and go to http://localhost:5050/api/v1/todos to see the response. - In the first fetching it takes 403ms to fetch the data from the api, and after multiple requests, it takes 0ms–1ms to fetch the data from the redis.
- you can also add
client.set("todos", JSON.stringify(data), "EX", 10)
to set the expiration time of the key. - Now open you redis-cli and run
keys *
to see the keys andttl todos
to see the expiration time of the key. - If you open redis-stack container, you can see the todos with data.
- Now you can scale your application by adding multiple instances of the application and using redis to cache the data.
- you can find the client.ts file in the server/src folder.
npm i -g @nestjs/cli
then run
npm install
Note: if you get red underline in the code editor, you can add the following line in the
.eslintrc.js
file
'prettier/prettier': ['error', { endOfLine: 'auto' }],
rules: {
'prettier/prettier': ['error', { endOfLine: 'auto' }],
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
src/main.ts
is the entry point of the application, and it uses theNestFactory
to create a new instance of the application.
in simple words, it's the main file of the application, where the application starts.
src/app.module.ts
is the root module of the application, and it uses the@Module
decorator to define the module.- In NestJS, a @Module is a class that is annotated with the @Module() decorator.
- providers: The providers that will be instantiated by the Nest injector and that may be shared at least across this module.
- controllers: The set of controllers defined in this module which have to be instantiated.
- In NestJS, a @Module is a class that is annotated with the @Module() decorator.
@Module({
imports: [], // import other modules here
controllers: [AppController],
providers: [AppService],
})
export class AppModule {} //export the module class here
src/app.controller.ts
is a basic controller that uses the@Controller
decorator to define the controller.- In NestJS, a @Controller is a class that is annotated with the @Controller() decorator.
- The @Controller() decorator is used to define a basic controller.
- The @Get() decorator is used to define a basic GET request.
- The @Post() decorator is used to define a basic POST request.
- The @Put() decorator is used to define a basic PUT request.
- The @Delete() decorator is used to define a basic DELETE request.
- The @Patch() decorator is used to define a basic PATCH request.
- The @Options() decorator is used to define a basic OPTIONS request.
- The @Head() decorator is used to define a basic HEAD request. (This decorator is used to define a route that matches the HTTP HEAD request method.)
- The @All() decorator is used to define a basic ALL request. (This decorator is used to define a route that matches any HTTP request method.)
- In NestJS, a @Controller is a class that is annotated with the @Controller() decorator.
import { Controller, Get, Post, Put, Delete, Patch, Options, Head, All } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
@Post()
postHello(): string {
return this.appService.postHello();
}
@Put()
putHello(): string {
return this.appService.putHello();
}
@Delete()
deleteHello(): string {
return this.appService.deleteHello();
}
@Patch()
patchHello(): string {
return this.appService.patchHello();
}
@Options()
optionsHello(): string {
return this.appService.optionsHello();
}
@Head()
headHello(): string {
return this.appService.headHello();
}
@All()
allHello(): string {
return this.appService.allHello();
}
}
src/app.service.ts
is a basic service that uses the@Injectable
decorator to define the service.- In NestJS, a @Injectable is a class that is annotated with the @Injectable() decorator.
- The @Injectable() decorator is used to define a basic service.
- The @Inject() decorator is used to inject a service into another service.
- The @InjectRepository() decorator is used to inject a repository into a service.
- The @InjectConnection() decorator is used to inject a connection into a service.
- The @InjectModel() decorator is used to inject a model into a service.
- The @InjectQueue() decorator is used to inject a queue into a service.
- The @InjectPipes() decorator is used to inject pipes into a service.
- The @InjectFilters() decorator is used to inject filters into a service.
- The @InjectInterceptors() decorator is used to inject interceptors into a service.
- The @InjectGuards() decorator is used to inject guards into a service.
- The @InjectExceptionFilters() decorator is used to inject exception filters into a service.
- The @InjectHost() decorator is used to inject the host into a service.
- The @InjectExecutionContext() decorator is used to inject the execution context into a service.
- The @InjectExecutionContext() decorator is used to inject the execution context into a service.
- The @InjectExecutionContext() decorator is used to inject the execution context into a service.
- The @InjectExecutionContext() decorator is used to inject the execution context into a service.
- The @InjectExecutionContext() decorator is used to inject the execution context into a service.
- The @InjectExecutionContext() decorator is used to inject the execution context into a service.
- The @InjectExecutionContext() decorator is used to inject the execution context into a service.
- The @InjectExecutionContext() decorator is used to inject the execution context into a service.
- The @InjectExecutionContext() decorator is used to inject the execution context into a service.
- The @InjectExecutionContext() decorator is used to inject the execution context into a service.
- In NestJS, a @Injectable is a class that is annotated with the @Injectable() decorator.
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
postHello(): string {
return 'Hello World!';
}
putHello(): string {
return 'Hello World!';
}
deleteHello(): string {
return 'Hello World!';
}
patchHello(): string {
return 'Hello World!';
}
optionsHello(): string {
return 'Hello World!';
}
headHello(): string {
return 'Hello World!';
}
allHello(): string {
return 'Hello World!';
}
}
-
src/app.controller.spec.ts
is a basic test file that uses the@nestjs/testing
package to test the controller. -
src/app.service.spec.ts
is a basic test file that uses the@nestjs/testing
package to test the service. -
src/app.e2e-spec.ts
is a basic test file that uses the@nestjs/testing
package to test the application end-to-end.
npm run start:dev
nest -g module <module-name>
nest -g controller <controller-name>
``
```bash
nest -g service <service-name>
nest -g pipe <pipe-name>
nest -g guard <guard-name>
nest -g filter <filter-name>
nest -g interceptor <interceptor-name>
nest -g middleware <middleware-name>
nest -g exception <exception-name>
nest -g decorator <decorator-name>
nest -g gateway <gateway-name>
nest -g resolver <resolver-name>