Developing a REST API using NodeJS, Express and Typescript
Published: 2022-12-09 18:19:14

Today I want to explain how to create a REST API using the Javascript programming language. This language is very popular when making web pages but thanks to a technology called NodeJS we can also use this language to write code on a server.

In this article I am not going to explain how to install NodeJS nor am I going to delve into its operation, but I am going to focus on explaining how to install the necessary libraries and write the code to start creating an API.

To create this API we are going to use the following libraries, among others:

- Typescript: to make the code easier to read
- Prism: To manage databases,
- Express: Handle HTTP calls
- Jest: To test the code
- Zod: Data validation
- Bcrypt: hash passwords

The complete code can be found at: [ismaventuras/rest-api-typescript (github.com)](https://github.com/ismaventuras/rest-api-typescript)

# Requirements

- NodeJS 18.10.0
- Docker 20.10.13

# Starting the project

First we need to start a project using a package manager, i’m going to use `npm` but you can youse any other alternative like `yarn`

```bash
npm init --y
```

Now that we have our project started we crate the folder structure of the project. All our code will be inside the `src` folder and we will configure Typescript to transpile the code to Javascript inside a folder called `dist` . We install Typescript and create the `tsconfig.json` file to configure typescript.

```bash
npm install -D typescript 
```

```json
{
  "compilerOptions": {
    "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "module": "commonjs",                                /* Specify what module code is generated. */
    "outDir": "./dist",                                   /* Specify an output folder for all emitted files. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */

    "strict": true,                                      /* Enable all strict type-checking options. */

    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  },
  "exclude": ["test"]
}
```

Once typescript is installed and configured we install `express` , the framework to handle HTTP calls.

```
npm i express
npm i -D @types/express

```

Create the `app.ts` file in the `src` folder, this file will export an `express` application and start the web server from the `src/index.ts` file

In the `src/app.ts` file we create an `express` application, use a couple of middlewares, define a GET path, and export the application.

The middlewares we use are:

- json() - To be able to use the `body` of the requests as an object.
- urlencoded() - To be able to use `body` if the information is sent from an html form.

The route returns the HTTP code 200 and a hello world message.

```
import express from "express";

const app = express()

app.use(express.json());
app.use(express.urlencoded({extended:false}));

app.get('/',(req, res, next) => {
     return res.status(200).json({message: 'hello world'});
})

export default app

```

In the `src/index.ts` file we import the application created in the `src/app.ts` file and start listening on port 3000.

```
import app from "./app";

const port = 3000;

app.listen(port, ()=> {
     console.log(`listening on <http://127.0.0.1>:${port}`)
})

```

In order to start the project, we need to edit the `package.json` file and so that when we run `npm start` it will transpilate the code to Javascript and run the `src/index.js` file in the `dist` folder.

```
{
...
"scripts":{
"start": "tsc --build --force && node dist/src/index.js"
}
...
}

```

We execute `npm start` and we would have our web server running. If we access [http://localhost:3000](http://localhost:3000/) in our browser or by consulting the API making http calls manually we will see the hello world message.

Once the project has started, the directory and file structure would be as follows:

```tsx
📦rest-api
 ┣ 📂dist
 ┣ 📂node_modules
 ┣ 📂src
	┣ 📜app.ts
	┗ 📜index.ts
 ┣ 📜package-lock.json
 ┣ 📜package.json
 ┗ 📜tsconfig.json
```

Our application right now only returns a hello world message, to give it some more functionality we are going to create an endpoint to create, read, edit and delete tasks.

We create the `routes` folder inside `src` and inside this we create the `index.ts` and `todo.routes.ts` files. The index file will import all the routes we write and export a router object to be used from the main application.

In the `todo.route.ts` file we write a route to handle tasks, as at the moment we don't have a database, the data returned by the api are test data

```
import { Router } from "express";

const router = Router()

router.route('/')
     .get((req, res, next) => {
         res.status(200).json([
             {
                 id: 1,
                 title: 'first all',
                 content: 'a task to execute'
             },
             {
                 id: 2,
                 title: 'second todo',
                 content: 'another task to execute'
             },
         ])
     })
     .post((req, res, next) => {
         res.status(201).json(req.body)
     })

router.route('/:id')
     .get((req, res, next) => {
         const id = req.params.id
         res.status(200).json({
             Yo hice,
             title: 'first all',
             content: 'a task to execute'
         })
     })
     .put((req, res, next) => {
         const id = req.params.id
         res.status(200).json({
             Yo hice,
             ...req.body
         })
     })
     .delete((req, res, next) => {
         const id = req.params.id
         res.status(200).json({
             Yo hice,
             ...req.body
         })
     })

export default router;

```

The `src/routes/index.ts` file imports the above file, creates a router, and assigns the `/todo` endpoint to the previously created router. We export this router and use it from `src/app.ts`

```
import { Router } from "express";
import todoRouter from './todo.route';

const router = Router()

router.use('/todo',todoRouter);

export default router

```

We edit the `src/app.ts` file, import the router and use it by assigning the `/api` path

```
...
import router from "./routes";
...

app.use('/api', router);

...

```

To test endpoints, you can use `curl` or `Postman` but I am going to use an extension to the Visual Studio Code IDE called [REST Client]([https://marketplace.visualstudio.com/items?itemName=humao](https://marketplace.visualstudio.com/items?itemName=humao). rest-client) which allows us to create a `.rest` file to make HTTP calls. The file would look like this to do the first tests with the path `/todo`

```
GET <http://localhost:3000/api/all>

###
POST <http://localhost:3000/api/todo>
Content-Type: application/json

{
     "title": "all test",
     "content":"a test task"
}

###
GET <http://localhost:3000/api/todo/1>
###
PUT <http://localhost:3000/api/todo/1>
Content-Type: application/json

{
     "title": "all test update",
     "content":"a test task update"
}

###
DELETE <http://localhost:3000/api/todo/1>

```

We run `npm start` and click on “Send request” above each http call inside the `.rest` file to check that the routes work. The following response comes after executing the first line of the `.rest` file, where we ask for the list of all tasks.

```tsx
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 128
ETag: W/"80-90sh6WsZ1XzUGCAyZh4CPQBd81s"
Date: Sat, 03 Dec 2022 11:29:16 GMT
Connection: close

[
  {
    "id": 1,
    "title": "first todo",
    "content": "a task to execute"
  },
  {
    "id": 2,
    "title": "second todo",
    "content": "another task to execute"
  }
]
```

# Creating a development database

Now that we've verified that the route to handle tasks works, now it's time to add a database. For this article I am going to use `PostgreSQL 14` as the database engine and in order to have a development environment I am going to use `docker` , thus separating what would be the database that would be used in production and the database used in development or testing.

We edit the `package.json` file to add a couple of scripts that start and stop the database.

```
"scripts": {
     "start": "tsc --build --force && node dist/src/index.js",
     "db:start": "docker run --name rest-api-db-dev -e POSTGRES_PASSWORD=postgres -d -p 5433:5432 postgres",
     "db:stop": "docker rm rest-api-db-dev -f",
}

```

The `npm run db:start` command starts a docker with name rest-api-db-dev , with password postgres and listening on port 5433. The `npm run db:stop` command will stop and kill the docker.

# Installing and using Prisma

We need to be able to use the application's database and for this we are going to use an ORM called Prisma to generate the models for the database.

We need to install `prisma` to declare the database models, convert them to SQL and create them in the database. We also installed `@prisma/client` to generate the models in TypeScript and be able to use the types within the application, as well as use the prisma client to use the database within the application.

```
npm i @prisma/client
npm i -D prisma
npx prisma init

```

The last command will create the `prisma` folder and also create a `.env` file from where it will read the URL to use the database, in our case the `.env` file should look like this:

```
DATABASE_URL="postgresql://postgres:postgres@localhost:5433/rest-api"

```

Now we go to the `prisma/schema.prisma` file and add the model to create a task

```
...

pattern all {
   id Int @id @default(autoincrement())
   titleString
   contentString
}

```

To create the model in the database, we first make sure that docker is started and run the command `npx prisma migrate dev --name todo` , which will generate the SQL code to create the model in the database and will create in the test database.

Once the model has been migrated to the database, we execute `npx prisma generate` to generate the models in Typescript and to be able to use them directly using prisma in the application code.

We are going to start using prisma in our application, we are going to create the `src/db` folder and inside we are going to create the `prisma.ts` file. In this file we are going to create an instance of the prisma client and export it for use in the application, we do this instead of calling the prisma client from each application component to avoid creating multiple database connections with the same url.

```
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export default prisma

```

# Creating and testing the controllers

With the database and prisma client ready to use, we are now going to create the `src/controllers` folder where we will put the code that will interact with the database and return “something” to the routes. We create the file `src/controllers/todo.controller.ts` where we will use prisma

```
import prism from "../db/prism"
import type {All} from '@prisma/client'

type UpdateTodoType = Partial<Todo>
type CreateTodoType = Pick<Todo, "content" | "title">
type SingleTodoType = Pick<Todo,"id">;

export const createTodo = async(todo: CreateTodoType) => {
     try {
         return await prisma.todo.create({
             data:all
         });
     } catch(error) {
         throw error
     }
}

export const allAll = async() => {
     try {
         return await prisma.todo.findMany();
     } catch(error) {
         throw error
     }
}
export const getTodo = async(todo: SingleTodoType) => {
     try {
         return await prisma.todo.findUniqueOrThrow({
             where:{
                 id:all.id
             }
         });
     } catch(error) {
         throw error
     }
}
export const deleteTodo = async(todo: SingleTodoType) => {
     try {
         return await prisma.todo.delete({
             where:{
                 id:all.id
             }
         });
     } catch(error) {
         throw error
     }
}

export const updateTodo = async(todoParams: SingleTodoType, todoBody: UpdateTodoType) => {
     try {
         return await prisma.todo.update({
             where:{
                 id:allParams.id
             },
             data:todoBody
         });
     } catch(error) {
         throw error
     }
}

```

In order to test them we would have to validate the data in the routes before passing it to the controller so in order to test the controllers we are going to do some separate tests using a library called `Jest`

```
npm install --save-dev jest typescript ts-jest @types/jest ts-node

```

Create the jest configuration file: `jest.config.ts`

```
import type {Config} from 'jest';

export default async(): Promise<Config> => {
   return {
     verbose: true,
     preset: 'ts-jest',
     testEnvironment: 'node',
   };
};

```

Now we need another database to run the tests, different from the production and development databases.

First we are going to install the dotenv-cli library to be able to load different files with environment variables.

```
npm i dotenv-cli

```

Let's also create the `.env.test` file to load environment variables, such as the database URL, in different scenarios. When doing tests we do not want to touch the production database since we need to create, edit or delete tasks, so we need a separate database to be able to test that everything works, so we also make sure that when moving the changes to production we will not break anything.

We add a new script to our `package.json` to execute the tests, the process will be the following: we will restart the docker, then we will migrate the prisma models and we will execute the tests to finally stop the docker.

```
...
"scripts": {
     "start": "tsc --build --force && node dist/src/index.js",
     "db:start": "docker run --name rest-api-db-dev -e POSTGRES_PASSWORD=postgres -d -p 5433:5432 postgres",
     "db:stop": "docker rm rest-api-db-dev -f",
     "test:docker:stop": "docker rm postgres-test -f",
     "test:docker:start": "npm run test:docker:stop && docker run --name postgres-test -e POSTGRES_PASSWORD=postgres -d -p 5434:5432 postgres",
"test": "npm run test:docker:start && dotenv -e .env.test npx prisma migrate deploy && dotenv -e .env.test jest -i"
  },
...

```

The `npm run test:docker:start` command starts a docker like the development one but on port 5434 to avoid conflicts, the `npm run test:docker:stop` command stops the docker. The `npm test` command starts docker, uses dotenv to load the environment variables from the env.test file, creates the database in docker, and runs the tests inside the test folder.

We create the `test` folder and inside it we create the `test/todo.controller.test.ts` file. The test imports prisma and the controllers, creates some test tasks and once the tests are finished, it deletes all the transactions from the database in case it is going to be used for other tests.

```tsx
import prisma from "../src/db/prisma";
import { createTodo, allTodos, deleteTodo, getTodo,updateTodo } from "../src/controllers/todo.controller"
import type {Todo} from '@prisma/client'

beforeAll(async () => {
    await prisma.todo.createMany({
      data: [{ title: 'Todo 1', content:'A task to execute' }, { title: 'Todo 2', content:'A task to execute' }],
    })
    console.log('✨ Seeded db with 2 todos');
});

afterAll(async () => {
    const deleteTodos = prisma.todo.deleteMany();

    await prisma.$transaction([
        deleteTodos
    ]);

    await prisma.$disconnect();
})

test('allTodos: should get all todos', async ()=> {
    const todos = await allTodos();
    expect(todos).toMatchObject<Todo[]>
})

test('createTodo: should create a todo', async () => {
    const todo = await createTodo({title:'test', content:'test'});
    expect(todo).toMatchObject<Todo>
})
test('updateTodo: should update a todo', async () => {
    const todo = await updateTodo({id:1},{title:'test', content:'test'});
    expect(todo).toMatchObject<Todo>
})
test('getTodo: should get a todo', async () => {
    const todo = await getTodo({id:1});
    expect(todo).toMatchObject<Todo>
})
test('deleteTodo: should delete a todo', async () => {
    const todo = await deleteTodo({id:1});
    expect(todo).toMatchObject<Todo>
})
```

When running npm test we will see the results of the tests and we will have verified that the controllers we have created work.

```
PASS  test/todo.controller.test.ts
  √ allTodos: should get all todos (5 ms)
  √ createTodo: should create a todo (12 ms)
  √ updateTodo: should update a todo (13 ms)
  √ getTodo: should get a todo (5 ms)
  √ deleteTodo: should delete a todo (11 ms)

Test Suites: 1 passed, 1 total
Tests:       5 passed, 5 total
Snapshots:   0 total
Time:        2.001 s
```

# Validation of information in http requests using Zod

Before starting to use the controllers, we are going to install a library to validate the data that comes to us in each request, the library is `zod`

```
npm i zod
```

We create the `schema` folder and inside it the `index.ts` and `todo.schema.ts` files

In the `index.ts` file we will create a function where we will pass the request (which will include the body and the params) and the schema against which we want to validate the request data.

```tsx
import { Request } from "express"
import { AnyZodObject, z } from "zod"

export async function zParse<T extends AnyZodObject>(schema: T, req: Request): Promise<z.infer<T>> {
    try {
        return schema.parseAsync(req)
    } catch (error) {
        throw error
    }
}
```

In the `src/schema/todo.schema.ts` file we create schemas that validate if the title or the content of the tasks are valid and also convert the id from text to number to be able to use it in the controller.

```tsx
import {z} from 'zod';

const titleSchema =  z.string({
    description: "title",
    invalid_type_error: "title must be a string",
    required_error: "title is required"
}).max(32, "max title length is 32 characters");

const contentSchema = z.string({
    description: "content",
    invalid_type_error: "content must be a string",
    required_error: "content is required"
});

const idSchema = z.object({
    id:z.preprocess(
        (id) => parseInt(id as string, 10),
        z.number({
            description: "id",
            invalid_type_error: "id must be a number",
            required_error: "id is required"
        }).positive()
    )    
})

export const createTodoSchema = z.object({
    body: z.object({
        title: titleSchema,
        content: contentSchema,
    })
})  

export const getTodoSchema = z.object({
    params: idSchema
})

export const deleteTodoSchema = getTodoSchema

export const updateTodoSchema = z.object({
    body: z.object({
        title: titleSchema.optional(),
        content: contentSchema.optional(),
    }),
    params: idSchema
})
```

Now we use the previous code in the `src/routes/todo.route.ts` file to handle the tasks to validate what we get in the request and if it is not what we expect, return an error.

```tsx
import { Router } from "express";
import { allTodos, createTodo, deleteTodo, getTodo, updateTodo } from "../controllers/todo.controller";
import { zParse } from "../schema";
import { createTodoSchema, deleteTodoSchema, getTodoSchema, updateTodoSchema } from "../schema/todo.schema";

const router = Router()

router.route('/')
    .get(async (req, res, next) => {
        try {
            const todos = await allTodos();
            return res.status(200).json(todos)
        } catch (error) {
            return res.status(500).json({error:'internal server error'});
        }
    })
    .post(async (req, res, next) => {
        try {            
            const {body} = await zParse(createTodoSchema, req);
            const todo = await createTodo(body);;
            return res.status(201).json(todo);
        } catch (error) {
            return res.status(500).json({error:'internal server error'});
        }
    })

router.route('/:id')
    .get(async (req, res, next) => {        
        try {
            const {params} = await zParse(getTodoSchema, req);
            const todo = await getTodo(params);
            return res.status(200).json(todo);
        } catch (error) {
            return res.status(500).json({error:'internal server error'});
        }
    })
    .put(async (req, res, next) => {
        try {
            const {params, body} = await zParse(updateTodoSchema, req);
            const todo = await updateTodo(params, body);
            return res.status(200).json(todo);
        } catch (error) {
            return res.status(500).json({error:'internal server error'});
        }
    })
    .delete(async (req, res, next) => {
        try {
            const {params} = await zParse(deleteTodoSchema, req);
            const todo = await deleteTodo(params);
            return res.status(200).json(todo);
        } catch (error) {
            return res.status(500).json({error:'internal server error'});
        }
    })

export default router;
```

# Error handling

If we start the application with `npm start` and retest the routes using the `.rest` file, we will see that everything works but the server returns `internal server error` for any error. For that we are going to create a middleware for express , where we can handle all the errors and send a response according to the user. This middleware will be applied after the routes, so we can use the `next()` function to send the error to the next middleware, in this case the error handler.

Create the `middleware` folder inside `src` and create the `src/middleware/errorHandler.ts` file

```tsx
import { Prisma } from "@prisma/client";
import { ErrorRequestHandler } from "express";
import { ZodError } from 'zod'

const errorHandler: ErrorRequestHandler = async (err, req, res, next) => {
    if (err instanceof ZodError) {
        const errorMessage = err.errors.map(error => error.message).join(',');
        return res.status(400).json({ error: errorMessage })
    }
    if (err.name === "NotFoundError") return res.status(404).json({ error: "item not found" }) //prisma
    if (err instanceof Prisma.PrismaClientKnownRequestError) {
        if (err.code === 'P2002') return res.status(409).json({ error: 'already exists' });
        if (err.code === 'P2025') return res.status(409).json({ error: "not found" });
    }
    if (err.type === 'entity.parse.failed') return res.status(400).json({ error: 'wrong formatted JSON' });
    console.error(err);

    return res.status(500).json({ error: 'internal server error' });
}

export default errorHandler
```

Zod validation errors will be returned to the user contained with commas, so if the user leaves out both the content and the title of the task they will receive both messages.

If it's a prisma error, then it's always handled by sending either not found or doesn't exist, since there are no other possibilities other than a database connection failure.

It is also validated if the message sent is a valid JSON, for example that it does not have a trailing comma at the end of the document.

If it is an uncontrolled error, a generic error message is returned and the error is logged by the console.

We also change all `catch` inside the `/src/routes/todo.route.ts` file to `next(error)`

Finally we use the middleware in the express app in the `/src/app.ts` file.

```tsx
...
import errorHandler from "./middleware/errorHandler";
...
app.use('/api', router);

app.use(errorHandler);

export default app
```

# Authentication

It may be that we want only certain people to be able to create tasks and for that we have to implement an authentication system, for this we need to add a new model to the `prisma/schema.prisma` file and add the model for the user that will have a username and password , once added we execute `npx prisma migrate dev --name auth` to generate the Typescript types and create the model in the database. In addition to the model we will need to create the routes, controllers, validation scheme and a middleware to validate if a user is authenticated before using a protected route.

```json
...
model User {
  id Int @id @default(autoincrement())
  username String @unique
  password String
  createdAt DateTime @default(now())  
}
```

We create the file `src/schema/auth.schema.ts` that will validate the user and the user's password when logging in or registering.

```tsx
import { z } from 'zod';

const authUsernameSchema = z.string({
    description: "Username",
    invalid_type_error: "username must be a string",
    required_error: "username is required"
})
const authPasswordSchema = z.string({
    description: "Password",
    invalid_type_error: "password must be a string",
    required_error: "password is required",
}).min(8, 'password must be greater than 8 characters')

const authBody = z.object({
    username: authUsernameSchema,
    password: authPasswordSchema,
});

export const authSchema = z.object({
    body: authBody,
});
```

Before creating the routes we must create the controllers, since we are going to deal with passwords we have to add some extra steps since we cannot store plain text passwords in the database. In order to save the passwords, what we will do is generate a hash of the password and save it in the database and for this we will use `bcrypt` .

We install bcrypt

```
npm and bcrypt
npm i -D @types/bcrypt

```

We create the `src/utils` folder and within it we create the `bcryptUtils.ts` file which will have two functions: one to hash a password and another to check that a password in plain text is equivalent to the hash we have in the database of data.

```
import bcrypt from 'bcrypt'

export const hashPassword = async (password: string) => {
     const saltRounds = 13;
     const salt = await bcrypt.genSalt(saltRounds)
     return await bcrypt.hash(password, salt)
}

export const comparePassword = async(password: string, hashedPassword:string) => {
     return await bcrypt.compare(password,hashedPassword)
}

```

Users will be able to register and login, as we are making an API the login must be using a token or similar, we could use basic authentication using the username and password in the request header but in this case what we are going to do is return a token using `jsonwebtoken`.

We install `jsonwebtoken`

```bash
npm i jsonwebtoken
npm i -D @types/jsonwebtoken
```

The function will sign a token using the login user's secret and assign an expiration time. the secret should be read from the environment variables so we edit the `.env` file to add the `SECRET` variable and some text that should not be shown to anyone. The function will return a valid JWT token.

```
import jwt from 'jsonwebtoken';

export const generateAccessToken = (username: string) => {
     return jwt.sign({username}, process.env.SECRET, {expiresIn: "1800s"});
}

```

If we try to transpile the `jwtUtils.ts` file to Javascript we will get an error stating that `process.env.SECRET` must be a `string` and right now it is `string | null`. To fix this we need to declare the types of our `.env` file and for that we create the `env.d.ts` file. Now we will see how the properties inside `process.env` are autocompleted.

```
export { };

declare global {
   namespace NodeJS {
     interface ProcessEnv {
       NODE_ENV: 'development' | 'production';
       DATABASE_URL: string;
       SECRET: string;
     }
   }
}

```

Now we have to create the controllers that will go in the routes, so we create the file `src/controllers/auth.controller.ts` , where we will create the functions to register a user and to check if it exists. Before saving the password in the database we hashe it using bcrypt and when logging in we check that the password they enter is the same.

```tsx
import prisma from "../db/prisma";
import { AuthBody } from "../schema/auth.schema";
import { comparePassword, hashPassword } from "../utils/bcrypt-utils";
import { generateAccessToken } from "../utils/jwt-utils";

export const createUser = async (userInput: AuthBody) => {
    try {
        const hashedPassword = await hashPassword(userInput.password)
        const user = await prisma.user.create({
            data:{
                ...userInput,
                password:hashedPassword
            }
        })
        return user
    } catch (error) {
        throw error
    }
}
export const loginUser = async (userInput: AuthBody) => {
    try {
        const {username, password} = userInput;
        const user = await prisma.user.findUniqueOrThrow({
            where:{
                username
            }
        })        
        let isValid = await comparePassword(user.password, password);
        if(!isValid) throw Error("INVALID CREDENTIALS");
        return generateAccessToken(username);
    } catch (error) {
        throw error
    }
}
```

Having the controllers now we are missing the routes so we create the file `src/routes/auth.route.ts` and import the routes in the file `src/routes/index.ts`

```tsx
import { Router } from "express";
import { createUser, loginUser } from "../controllers/auth.controller";
import { zParse } from "../schema";
import { authSchema } from "../schema/auth.schema";

const router = Router();

router
    .route('/register')
    .post(async (req, res, next) => {
        try {
            const { body } = await zParse(authSchema, req);
            const user = await createUser(body);
            res.status(201).json(user);
        } catch (error) {
            next(error);
        }
    });

router
    .route('/login')
    .post(async (req, res, next) => {
        try {
            const { body } = await zParse(authSchema, req);
            const token = await loginUser(body);
            res.status(200).json({ token });
        } catch (error) {
            next(error)
        }
    })

export default router
```

```tsx
...
import authRouter from './auth.route';
...
router.use('/auth',authRouter);
...
export default router
```

To test that it works, we are going to add some lines to the .rest file to check that everything works

```tsx
...

POST http://localhost:3000/api/auth/register
Content-Type: application/json

{
    "username":"ismael",
    "password":"SecretPassword"
}

###

POST http://localhost:3000/api/auth/login
Content-Type: application/json

{
    "username":"ismael",
    "password":"SecretPassword"
}
```

The last thing we have left is to create a middleware that is executed when the request arrives but before using the route, to validate if the user is authenticated or not, for that we are going to create the file `src/middleware/authenticateJWT.ts`

```
import { NextFunction, Request, Response } from "express";
import jwt from "jsonwebtoken";

export const authenticateJWT = async (req: Request, res:Response, next:NextFunction) => {
     const authHeader = req.headers['authorization']
     const token = authHeader && authHeader.split(' ')[1]

     if(!token) return res.sendStatus(401);
     try {
         const decoded = jwt.verify(token, process.env.SECRET, {})
         next()
     } catch(error) {
         res.status(403).json({error:"invalid token"});
     }

}

```

Now, in order to use it, we go, for example, to the `src/routes/index.ts` file and make it so that only authenticated users can use the routes for tasks.

```
import { Router } from "express";
import todoRouter from './todo.route';
import authRouter from './auth.route';
import { authenticateJWT } from "../middleware/authenticateJWT";

const router = Router()

router.use('/todo',authenticateJWT,todoRouter);
router.use('/auth',authRouter);

export default router

```

Now, if we try to use any of the routes to handle the tasks, it will return the error code 401 and an Unathorized message. In order to use the route you have to register, login and use the token that is generated when you login. The way to use it is by adding the `authorization` header with the `BEARER <token>` content.

# Extra

## Handle 404 errors

If we don't handle any of the errors, express sends a message on its own, to avoid this we create a new middleware that we will use between the routes and the error handling middleware.

We create the `src/middleware/notFound.ts` file where we return a 404 error.

```
import { NextFunction, Request, Response } from "express";

const notFound = async (req:Request, res:Response, next:NextFunction) => res.status(404).json({error: 'ENDPOINT NOT FOUND'});

export default notFound

```

And we use the middleware in the `src/app.ts` file

```
import express from "express";
import errorHandler from "./middleware/errorHandler";
import notFound from "./middleware/notFound";
import router from "./routes";

const app = express()

app.use(express.json());
app.use(express.urlencoded({extended:false}));

app.use('/api', router);

app.use(notFound);
app.use(errorHandler);

export default app

```

The `ENDPOINT NOT FOUND` message and 404 error code will now be returned when an endpoint is not found.

## Logging

In a development environment it is fine to use `console.log` to print information to the console but in production it is better to rely on functions that are not synchronous and block the process while writing. For this we are going to use `winston`, a library made to measure for this and that allows us to save the logs in a file.

we install winston

```
npm i winston

```

We create the file `src/utils/logger.ts` which will create a `logger` which will add the messages that we send from the error level to the `error.log` file.

```
import winston from 'winston';

export const logger = winston.createLogger({
     format: winston.format.simple(),
     transportation:[
         new winston.transports.File({filename:'error.log', level:'error'}),
     ]
})

//
// If we're not in production then log to the `console` with the format:
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
//
if (process.env.NODE_ENV !== 'production') {
     logger.add(new winston.transports.Console({
         format: winston.format.simple(),
     }));
}

```

Edit the `src/index.ts` file so that it logs the startup message

```
import app from "./app";
import { logger } from "./utils/logger";

const port = 3000;

app.listen(port, ()=> {
     logger.info(`listening on <http://127.0.0.1>:${port}`)
})

```

And finally we edit the `src/middleware/errorHandler.ts` file so that it logs any errors we may have to the `error.log` file.

```tsx
import { Prisma } from "@prisma/client";
import { ErrorRequestHandler } from "express";
import { ZodError } from 'zod'
import { logger } from "../utils/logger";

const errorHandler: ErrorRequestHandler = async (err, req, res, next) => {
    if (err instanceof ZodError) {
        const errorMessage = err.errors.map(error => error.message).join(',');
        return res.status(400).json({ error: errorMessage })
    }
    if (err.name === "NotFoundError") return res.status(404).json({ error: "item not found" }) //prisma
    if (err instanceof Prisma.PrismaClientKnownRequestError) {
        if (err.code === 'P2002') return res.status(409).json({ error: 'already exists' });
        if (err.code === 'P2025') return res.status(409).json({ error: "not found" });
    }
    if (err.type === 'entity.parse.failed') return res.status(400).json({ error: 'wrong formatted JSON' });
        
    logger.error(err);
    return res.status(500).json({ error: 'internal server error' });
}

export default errorHandler
```

# Conclusion

In this article we have created a REST API using Typescript and NodeJS along with many other libraries. We have created a development database and a test database using docker and we have written some tests for HTTP calls and to test code using Jest. We have validated that the information that comes to us via HTTP request is correct and we have created middleware for error handling. We've learned how to use prisma to build models that work on different database engines and have created controllers that use prisma's models. We have added authentication using JWT so that some routes are private. Finally we have added some improvements such as logging or handling 404 errors.

The final directory tree would be as follows

```tsx
📦rest-api-typescript
 ┣ 📂dist
 ┣ 📂node_modules
 ┣ 📂prisma
 ┃ ┣ 📂migrations
 ┃ ┃ ┣ 📂20221205183545_todo
 ┃ ┃ ┃ ┗ 📜migration.sql
 ┃ ┃ ┣ 📂20221208145352_auth
 ┃ ┃ ┃ ┗ 📜migration.sql
 ┃ ┃ ┗ 📜migration_lock.toml
 ┃ ┗ 📜schema.prisma
 ┣ 📂src
 ┃ ┣ 📂controllers
 ┃ ┃ ┣ 📜auth.controller.ts
 ┃ ┃ ┗ 📜todo.controller.ts
 ┃ ┣ 📂db
 ┃ ┃ ┗ 📜prisma.ts
 ┃ ┣ 📂middleware
 ┃ ┃ ┣ 📜authenticateJWT.ts
 ┃ ┃ ┣ 📜errorHandler.ts
 ┃ ┃ ┗ 📜notFound.ts
 ┃ ┣ 📂routes
 ┃ ┃ ┣ 📜auth.route.ts
 ┃ ┃ ┣ 📜index.ts
 ┃ ┃ ┗ 📜todo.route.ts
 ┃ ┣ 📂schema
 ┃ ┃ ┣ 📜auth.schema.ts
 ┃ ┃ ┣ 📜index.ts
 ┃ ┃ ┗ 📜todo.schema.ts
 ┃ ┣ 📂utils
 ┃ ┃ ┣ 📜bcryptUtils.ts
 ┃ ┃ ┣ 📜jwtUtils.ts
 ┃ ┃ ┗ 📜logger.ts
 ┃ ┣ 📜app.ts
 ┃ ┗ 📜index.ts
 ┣ 📂test
 ┃ ┗ 📜todo.controller.test.ts
 ┣ 📜.env
 ┣ 📜.env.test
 ┣ 📜.gitignore
 ┣ 📜.rest
 ┣ 📜env.d.ts
 ┣ 📜jest.config.ts
 ┣ 📜package-lock.json
 ┣ 📜package.json
 ┗ 📜tsconfig.json
```