Get Better with TypeScript using Express
Tags: typescript, node, javascript, tutorial
Published: 2021-10-30 07:48:17

##Hi Guys Good Day!

Today, we will make a `Rest API` using these two technologies. You may think that this is another `Rest API` like all the others but in this example, we will be using a lot of __TypeScript__ advanced features that will be really helpful for this demo. But we will be more focused on using __TypeScript__ instead of the business logic implementation. I suggest using VSCode in this example because it provides a lot of features with __TypeScript__.

The API we will be making will focus on Dogs. Our endpoints will look like this.
```cmd
GET /api/v1/dogs
GET /api/v1/dogs/:id
POST /api/v1/dogs
PUT /api/v1/dogs/:id
DELETE /api/v1/dogs/:id
```

![dog typing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hr75ywkyjm8634rkj4i4.gif)

First, make a folder, you can name it whatever you want. I will name mine _express-ts-api_.
```cmd
  mkdir express-ts-api
```

After that initialize a node project.
```cmd
 npm init --y
```

We also need to install __TypeScript__.
```cmd
 npm i -D typescript
```

We also need to install type definitions for these __Express__ and __Node__.
```cmd
 npm i -D @types/express @types/node
```

And also, we will install __Express__
```cmd
 npm i express
```

Lastly, configure this project to be a __TypeScript__ project.
Using this command
```cmd
  tsc -init
```

Our _tsconfig.json_ will look like this.
```json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true
  }
}
```
Put simply, this config tells us that our code will be outputted on es5 syntax __("target": "es5")__ and also that code will be using the CommonJS module system __("module": "commonjs")__ in the directory *build* __("outDir": "./build")__ base on the contents in the *src* directory __("rootDir": "./src")__ and the typescript language service should enforce strong type checking __("strict": "true")__ and lastly we want to import modules in different __Module Systems__ like _commonjs_ follow the specifications of the ES6 module __("esModuleInterop": true)__ without this option our imports will look like this

```ts
import * as express from 'express';
// instead of this
// import express from 'express';
```

Our _package.json_ will look like this.
```json
{
  "name": "express-ts-api",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/express": "^4.17.13",
    "@types/node": "^16.11.4",
    "typescript": "^4.4.4"
  },
  "dependencies": {
    "express": "^4.17.1"
  }
}
```
I will assume that you already know nodejs, so I will not explain the contents of this file.

Ok, let's get started. Make the _src_ directory
```cmd
 mkdir src
```

Inside the _src_ folder make the _app.ts_ file.
```ts
import express from 'express'

const app = express()

interface Dog {
  name: string
  breed: 'labrador' | 'german shepherd' | 'golden retriever'
  adopted_at: Date | null
  birth_date: Date | null
}
```

Btw, every HTTP method that we use in the _app_ object has five generic types that we can provide our custom types. The arrangement of the types are __Params__, __ResBody__, __ReqBody__, __ReqQuery__ and __Locals__. More in _Generics_ [here](https://www.digitalocean.com/community/tutorials/typescript-generics-in-typescript). Basically, __Generics__ helps us reuse code but in our case, we can reusable types.
```ts
app.get<Params,ResBody,ReqBody,ReqQuery,Locals>('/api/v1/dogs',
(req,res) => { 

})
```
We will only be using the first four generic types in this example. By default the __Params__ generic type have a value of a type of an empty object. __ResBody__ and __ReqBody__ has a type of _any_, and lastly the __ReqQuery__ has a type of _ParsedQs_.

We will be providing our own types instead of the default types provided by express.

### GET /api/v1/dogs
```ts
app.get<
{},
{ data: Dog[], message: string },
{},
{ page: number, limit: number, breed: 'labrador' | 'german shepherd' | 'golden retriever' }>
('/api/v1/dogs', (req,res) => { 
  // your implementation
})
```
In this endpoint,  we will be getting a list of dogs so we will not pass a type in the __Params__ generic because we are not getting a specific dog. We are getting a list of dogs in this endpoint, so we will leave it as an empty object. Second, the __ResBody__ is the type that we will send in _res.send_ method, in this case, we will send an object that has two properties _data_ whose type is an array of Dogs that we provided earlier and _message_ whose type is a _string_ an additional information for the response. Third, the __ReqBody__ is an empty object type because we won't be receiving any data in this endpoint. And lastly, in the __ReqQuery__ we will be passing an object type that accepts the pagination properties _page_ and _limit_ and also can the breed property so we can use it to filter dogs base on a specific breed.

### GET /api/v1/dogs/:id
```ts
app.get<
{ id: number },
{ data: Dog | null, message: string },
{}>
('/api/v1/dogs/:id', (req,res) => { 
  // your implementation
})
```
In this endpoint, we will be getting a specific dog so we will pass an object type in the __Params__ whose property is an _id_ which has the type of _number_ because we will be getting a specific dog. We are getting a list of dogs in this endpoint, so we will leave it as an empty object. Second, the __ResBody__ in this case, we will send an object that has two properties _data_ whose type are a __union type__ of the _Dog_ type and _null_ this tells us that if the dog exists it will return the shape of the _Dog_ and if does not exist it will return _null_ and the property _message_ whose type is a _string_. Third, the __ReqBody__ is also an empty object type because we won't be receiving any data in this endpoint. And lastly, we will be passing an empty object type for the __ReqQuery__ because this endpoint does not need it.


### POST /api/v1/dogs
```ts
app.post<
{},
{ data: Dog & { id: number }, message: string },
Dog,
{}>
('/api/v1/dogs', (req,res) => { 
  // your implementation
})
```
In this endpoint, we will be creating a new dog so we will pass an empty object type in the __Params__. Second, the __ResBody__, in this case, we will send an object that has two properties _data_ whose type is a __union type__ of the _Dog_ type and an object type which has a property of _id_ which is type _number_ because the DB will generate this id instead of the client and the property _message_ whose type is a _string_. Third, the __ReqBody__ has a type of _Dog_ because we will be receiving data from the client which has the shape of _Dog_. And lastly, we will be passing an empty object type for the __ReqQuery__ because this endpoint does not need it.

### PUT /api/v1/dogs/:id
```ts
app.put<
{ id: number },
{ data: Dog & { id: number }, message: string },
Partial<Dog>,
{}>
('/api/v1/dogs', (req,res) => { 
  // your implementation
})
```
In this endpoint, we will be update an existing dog so we will pass an object type in the __Params__ whose property is an _id_ which has the type of _number_. Second, the __ResBody__, in this case, we will send an object that has two properties _data_ whose type is a __union type__ of the _Dog_ type and an object type which has a property of _id_ which is type _number_ because we will return the updated value of the resource and also the property _message_ whose type is a _string_. Third, the __ReqBody__ has a type of _Dog_ because we will be receiving data from the client which has the shape of _Dog_ but every property should be optional because this is an update so we are using a Utility Type __Partial__ that makes every property in the _Dog_ interface optional. And lastly, we will be passing an empty object type for the __ReqQuery__ because this endpoint does not need it.

### DELETE /api/v1/dogs/:id
```ts
app.delete<
{ id: number },
{ data: Dog & { id: number }, message: string },
{},
{}>
('/api/v1/dogs', (req,res) => { 
  // your implementation
})
```
In this endpoint, we will be deleting a dog so we will pass an object type in the __Params__ whose property is an _id_ which has the type of _number_. Second, the __ResBody__, in this case, we will send an object that has two properties _data_ whose type is a __union type__ of the _Dog_ type and an object type which has a property of _id_ which is type _number_ because we will return the deleted dog resource and also the property _message_ whose type is a _string_. Third, the __ReqBody__ is an empty object type because we won't be receiving any data in this endpoint.  And lastly, we will be passing an empty object type for the __ReqQuery__ because this endpoint does not need it.

I think we're done.

![meme done](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flrrsvfjx6wrjx3dkhwc.jpg)

I think we're not done yet. We've been passing our own custom types directly and some of those types were repeating in some of our methods and this makes our code not clean. Let's change that.

```ts
interface BaseParams<IDType = number> {
  id: IDType
}

interface DogDetails {
  name: string
  breed: DogBreed
  adopted_at: Date | null
  birth_date: Date | null
}

interface APIResponse<Data> {
  data: Data
  message: string
}

interface Pagination {
  page: number
  limit: number
  breed: DogBreed
}

interface Empty {

}

type DogBreed = 'labrador' | 'german shepherd' | 'golden retriever'

type Dog = BaseParams & DogDetails
```
Ok, I will explain all these new types that you see. First, the interface __BaseParams__ is the type we will provide to the __Params__ position, the __BaseParams__ has _Generic type_ __IDType__  which has a default value of type _number_ you can also provide a different type for the _id_ by passing another type here <code> BaseParams\<string> </code>. The interface __DogDetails__ is the type we will use for the __ReqBody__ position. The interface __APIResponse__ is the type that we will use for the __ResBody__ position, this type also has a generic just like the type __BaseParams__, the generic __ResultType__ type will be the type of the _data_ property. The interface __Pagination__ is the type we will use for the position __ReqQuery__, this type has a property _breed_ the references another custom type that we will be talking about soon. The interface _Empty_ is a helper interface type that we will use for empty objects. The _DogBreed_ type alias is also a helper type that referenced in the __Pagination__ interface and also the __DogDetails__ interface. And, lastly, the __Dog__ type alias is the combination of two interfaces __BaseParams__ and __DogDetails__ we achieved this by using the _&_ intersection type.  

If we apply all these new types in our code, our code should look like this.
```ts
import express from 'express'

const app = express()

interface BaseParams<IDType = number> {
  id: IDType
}

interface DogDetails {
  name: string
  breed: DogBreed
  adopted_at: Date | null
  birth_date: Date | null
}

interface APIResponse<Data> {
  data: Data
  message: string
}

interface Pagination {
  page: number
  limit: number
  breed: DogBreed
}

interface Empty {

}

type DogBreed = 'labrador' | 'german shepherd' | 'golden retriever'

type Dog = BaseParams & DogDetails

app.get<Empty, APIResponse<Dog[]>, Empty, Pagination>('/api/v1/dogs', (req, res) => {
  // your implementation
})

app.get<BaseParams, APIResponse<Dog | null>, Empty, Empty>('/api/v1/dogs/:id', (req, res) => {
  // your implementation
})

app.post<Empty, APIResponse<Dog>, DogDetails, Empty>('/api/v1/dogs', (req, res) => {
  // your implementation
})

app.put<BaseParams, APIResponse<Dog>, Partial<DogDetails>, Empty>('/api/v1/dogs', (req, res) => {
  // your implementation
})

app.delete<BaseParams, APIResponse<Dog>, Empty, Empty>('/api/v1/dogs', (req, res) => {
  // your implementation
})
```
This new code is more readable and more maintainable than the old one because of the new types that we made.
I think we're really done here.

![labrador-done](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rt835h39lb2e5t6vvv7l.gif)


## Thank you guys for reading this post.
## Have a Nice Day :smiley:!.