Authentication Service in TypeScript and Clean Architecture
Table of Contents
- Prerequisites
- Project Setup
- Clean Architecture Setup
- Server Setup
- Entites
- Repository
- Use Cases
- Controllers and Routes
This guide will walk you through the process of creating an authentication service in Node.js using TypeScript and clean architecture. The authentication service will allow users to sign up and log in to your application using email and password.
Prerequisites
Before you begin, make sure you have the following:
- Node.js (v14 or higher)
- npm or yarn
- A text editor of your choice (e.g. VS Code)
Project Setup
To set up the project, follow these steps:
Create a new directory for your project.
Open the directory in your terminal or command prompt.
Initialize a new Node.js project using
npm init
oryarn init
.Install the required dependencies:
express
for building the serverjsonwebtoken
for generating and verifying JSON Web Tokensbcrypt
for hashing and verifying passwordsdotenv
for managing environment variablesknex.js
for database managementpg
or any other driver for the desired database engine@types/express
,@types/jsonwebtoken
,@types/bcrypt
,@types/dotenv
and@types/pg
for TypeScript support
You can install these dependencies using the following command:
Create a
src
directory for your source code.Inside the
src
directory, create aserver.ts
file for your server code.
Clean Architecture Setup
We will be using the Clean Architecture pattern to structure our code. The Clean Architecture is a software architecture that separates the code into layers with clear boundaries and dependencies pointing inwards. In this architecture, the core business logic is at the center of the architecture, surrounded by layers of less important details. The layers are:
- Entities: Domain models and business rules.
- Use cases: Application-specific business rules.
- Controllers: Handle requests and responses.
- Repositories: Database and external interfaces.
- Services: Manage external services like AWS S3, Twilio, etc.
The following diagram illustrates the Clean Architecture layers:
+----------------+
| Controllers |
+----------------+
| Use Cases |
+----------------+
| Entities |
+----------------+
| Repositories |
+----------------+
| Services |
+----------------+
To set up the Clean Architecture, create the following directories inside the src
directory:
domain
: Contains theUser
entity and any business rules related to authentication.usecases
: Contains the use cases for authentication, such asSignup
andLogin
.interface
: Contains the Express.js server code and its associated controllers.
Server Setup
Inside the server.ts
file, you will need to create a new Express.js server, set up middleware for parsing JSON and handling CORS, and define routes for authentication. Here is an example of what your server.ts
file might look like:
;
;
;
Entites
Entities are at the heart of the domain layer. They are the core objects that represent the business concepts of the application. Entities contain data and methods that are specific to the business domain and are independent of any particular application logic or technical implementation.
This interface defines the properties of a User entity, including its unique identifier, first and last name, email address, password, and timestamp for when it was created and last updated. The properties can be read or modified through methods defined in the domain layer.
To define an entity in TypeScript, you can use an interface or a class. Here’s an example of an interface for a User entity:
Repository
The Repository layer is responsible for managing the persistence of entities. In the context of our authentication service, the repository will handle operations related to storing and retrieving user data from the database.
To create the UserRepository, follow these steps:
Inside the
src
directory, create a new directory calledrepositories
.Inside the
repositories
directory, create a file calledUserRepository.ts
.In the
UserRepository.ts
file, import the necessary dependencies:; ;
Define a class called
UserRepository
and export it:Add methods to the
UserRepository
class for handling database operations. For example, you can add methods for creating a new user, finding a user by email, or updating a user’s password.Here’s an example of a method for creating a new user:
async user: User : Promise<User>
Note that the
getRepository
function is provided by TypeORM, which is a popular Object-Relational Mapping (ORM) library for TypeScript and JavaScript.Save the file and move on to the next step.
Use Cases
The Use Case layer contains application-specific business rules and orchestrates the flow of data between the entities and the repositories. In the context of our authentication service, the use cases will handle the logic for signing up and logging in users.
To create the Signup use case, follow these steps:
Inside the
src/usecases
directory, create a file calledSignup.ts
.In the
Signup.ts
file, import the necessary dependencies:; ; ;
Define a class called
Signup
and export it:Add a
execute
method to theSignup
class that takes in the necessary input data for signing up a user, such as email and password:async email: string, password: string : Promise<User>
In this example, the
hashPassword
function is a utility function that hashes the user’s password before storing it in the database. You can implement this function using a library likebcrypt
.Save the file and move on to the next step.
Controllers and Routes
The Controllers layer handles incoming requests, validates input data, and interacts with the use cases to process the request. In the context of our authentication service, the controllers will handle the HTTP endpoints for signing up and logging in users.
To create the AuthController and define the authentication routes, follow these steps:
Inside the
src/interface
directory, create a file calledAuthController.ts
.In the
AuthController.ts
file, import the necessary dependencies:; ; ;
Define a class called
AuthController
and export it:Add methods to the
AuthController
class for handling the signup and login endpoints. For example, you can add a method for handling the POST request to/signup
:async req: Request, res: Response : Promise<void>= req.body; try catch error }
Note that in this example, the
signupHandler
method calls theexecute
method of theSignup
use case to create a new user. If the signup is successful, it responds with the created user; otherwise, it returns a 500 error.Define the routes for authentication in the
registerRoutes
method of theApp
class in theserver.ts
file. For example, you can add the following route for the signup endpoint:'/signup', authController;
Here,
authController.signupHandler
is bound to the route handler to maintain the correct context when handling the request.Save all the files and start the server using
npm start
oryarn start
. You should now be able to send HTTP requests to the authentication endpoints.
Congratulations! You have successfully set up an authentication service in Node.js using TypeScript and clean architecture. You have learned how to structure your code using the Clean Architecture pattern, set up the server, define entities and repositories, create use cases, and handle HTTP requests with controllers and routes. From here, you can further enhance your authentication service by adding features like login, password reset, and email verification.
Remember to always follow security best practices when implementing authentication, such as using strong password hashing algorithms, protecting against brute force attacks, and handling session management securely.
Github repo link