Structuring TypeScript project in Express with phone number authentication using Twilio

Verdotte Aututu
6 min readAug 16, 2021

For so many years, JavaScript has been used as a Front-end programming language, but in the modern era, JavaScript usage has drastically grown from being a user interface language to a language that can be used for full-stack application development with the help of Node.js.

Though, building an application in JavaScript at scale remains a challenge due to multiple reasons such as lack of a strong type system and so on. Because of those various reasons, a lot of developers and companies around the world are turning to TypeScript to supplement their JavaScript projects.

However, structuring a TypeScript project can be sometimes embarrassing, especially for developers that are only familiar with functional programming. In this tutorial, we will learn how to structure a TypeScript project in Express and we shall also implement phone number authentication using Twilio as an illustration.

Prerequisites

  • Basic knowledge of Node.js and NPM
  • Basic understanding of Express framework
  • TypeScript fundamentals
  • Basic knowledge of MongoDB

To follow along with this tutorial, sign up for a Twilio account and create a service. Click here for more details on how to go about it. Given this article focuses more on project structure than set up, reading the following article can help to better understand certain topics that won’t be explained in depth here.

Project setup

From your desired directory where you want to keep the project, run this command to initialize the project:

npm init -y

Now with the project initialized, now we can install the required dependencies.

Dependency installation

npm install express typescript cross-env jsonwebtoken morgan twilio cors body-parser mongoose @types/cors @types/express @types/mongoose @types/morgannpm install @types/jsonwebtoken @typescript-eslint/eslint-plugin @typescript-eslint/parser dotenv eslint nodemon prettier ts-node --save-dev

Project configuration

In this section, we are going to set up various project commands, configure the TypeScript setting, and linting for TypeScript.

Add the following commands under the scripts section of package.json and it will look as follow:

Let us create a tsconfig.json file under the root directory and the following codes:

Now create a .eslintrc file in the project root and use the following starter configuration:

Add another file under the project directory named .prettierrc containing the following configurations:

Project structure

Express server creation

With almost everything configured, create an src folder in the project root under which you add the app.ts file and paste in the following code snippet:

The code above creates an App class that receives an array of routes and encapsulates four methods, one for initializing all the middlewares, another one for implementing the routing system that will handle the array of routes passed in the App constructor, the next method connects the app to the MongoDB database and the last method creates Node Server that listens on the port 3000.

Still, under the src folder, create an index.ts file and add this implementation inside:

Create Model

Let us create a models folder under src in which we create a user.ts file that will contain user model implementation.

With the help of Model<IUser>TypeScript is now aware of all the fields we defined in the interface and knows that it can expect them to be available in the User model.

Twilio service implementation

Head over your Twilio account dashboard and copy the following credentials after creating a service:

TWILIO_ACCOUNT_SID=<your account sid>

TWILIO_AUTH_TOKEN=<your auth token>

TWILIO_SERVICE_ID=<your service>

Once done, create a .env file under the project directory and it should look like this:

Create an interface folder under src in which you add the twilio.interface.ts file, the file will contain the Twilio response definition.

Let’s create another folder under src called plugins, and add twilio folder inside it. And under the twilio folder creates the following files:

As you see, the TwilioService class contains two methods, the sendVerificationCode method that takes the phone number as a parameter and uses SMS as a channel which is specified in the .env file. This method is responsible for sending the SMS containing the authentication code to the user. The next method is in charge of verifying the user authentication code in order to conclude the signup process.

Middleware implementation

Given we have to be throwing an exception on every call we make to the database, we need to implement a middleware that will be wrapping different methods and functions with a try/catch so that we won’t have to write it multiple times.

We can add another middleware for checking if the user's phone number has never been used already in the system before he/she is allowed to continue with the signup process.

Create helper function

As far as this project is concerned, we are only going to implement a single helper function that will serve for generating tokens using the jwt package.

Controller implementation

We can now implement the controller by creating controllers folder under src where we shall add authController.ts file and paste in the following snippet:

In the above code snippet, we have created the AuthController class where we leveraged the concept of dependency injection by injecting the TwilioService class. By doing so the AuthController is decoupled from the TwilioService which offers the freedom of changing the implementation at anytime and also makes unit testing easier since we can mock TwilioService class. You can read this article by Jamie Corkhill for more details about dependency injection.

We did also implement couple of methods within this class, the signup method which basically destructures the parameters received from the body of the request and create the user before calling the sendVerificationCode method from the TwilioService asynchronously.

The verifyUser method expects the user phone number and authentication code that is sent during the signup process and uses phone number for checking if the user is already created or verified, if the user has not yet been verified, the verifyCode method is called from TwilioService for the verification. If the verification is not valid and its status is different to approved we return wrong verification code otherwise we update user document by setting theverified field totrueand generate a token.

The last method is login, it checks if the user exists and if user’s account is verified, in case the user exists but not yet verified, the sendVerificationCode is called to send a new verification code otherwise a login token is generated by calling generateToken function.

Add an index.ts file under controller folder and paste the below snippet:

Route implementation

Before jumping to the route implementation, create route.inteface.ts file under the interfaces folder where the blueprint of all route classes will be defined.

Now we can create the routes folder and add authRoute.ts file that will contain all the authentication endpoints.

As you can see we have implemented the IRoute interface and, we have taken again advantage of dependency injection by injecting AuthController but this time around making it explicitly readonly. You will also notice all the Authcontroller methods and checkPhoneNumber middleware are wrapped inside the asyncHandler middleware for handling expection.

Finally, we create an index.ts file under routes folder, in which we will prepare the routes array that got passed to the App class constructor.

Start server

Run the following command to start the server in development mode:

npm run dev

And you should see this output :

Time to test different endpoints

Signup

SMS

Verify user

Login

Conclusion

In this tutorial, you learned about structuring TypeScript project in Express framework by implementing phone number authentication using Twilio as illustration. Note that this structure is just a blueprint that can give you ideas on organizing TypeScript code base with Express. In other words it can be modified, adjusted or scaled to fit a particular project requirement.

The full codes of this article can be found by clicking here.

--

--

Verdotte Aututu

Software Engineer | Backend | Blockchain | DAPP | Web3 | NodeJS | AWS