Introducing NoSQL Azure Table Storage for NestJS 🚀
Tags: cloud, azure, javascript, tutorial
Published: 2019-09-24 21:11:14

> Originally published on the [Trilon Blog](https://trilon.io/blog/nestjs-nosql-azure-table-storage) on September 17, 2019.

In this article, we'll be looking at how to add Azure Table Storage to our NestJS applications in only a few minutes with the new `@nestjs/azure-database` library!

In case you're not familiar with [NestJS](https://nestjs.com/), it is a TypeScript Node.js framework that helps you build enterprise-grade efficient and scalable Node.js applications.

# What's Azure Table Storage?


[Azure Table Storage](http://bit.ly/2lMRQQh) is a NoSQL key-value store using massive semi-structured datasets.

Table Storage allows you to create massively-scalable apps that require a flexible data schema. You can also perform OData-based queries and use JSON to serialize data.

Use Azure Table storage to store petabytes of semi-structured data and keep costs down.

Unlike many data stores—on-premises or cloud-based:

- Table storage lets you scale up without having to manually shard your dataset.
- Availability also isn’t a concern!
- Using geo-redundant storage, stored data is replicated three times within a region—and an additional three times in another region, hundreds of miles away.

Let's dive into how we can use Table Storage for our NestJS applications!

# Getting setup


> *NOTE*: In this demonstration, we'll be showcasing a new NestJS application generated by the CLI, but if you prefer to use an existing NestJS application - feel free - and just skip ahead!

## Generate a new NestJS Application


For demo purposes, let's make sure we have the latest NestJS CLI installed - and create a New application.

```shell
$ npm i -g @nestjs/cli
$ nest new PROJECT_NAME
```

Now let's `cd` into the newly created directory and open up our IDE. At this point, we have a simple generated NestJS Application.


## Setting up an Azure Storage account

In order to use [Table Storage](http://bit.ly/2lMRQQh), we will need to create an Azure Storage account. You can follow this [step by step guide](http://bit.ly/2mvqkHq).

Once our storage account created, we need to copy the Connection String that we will use for our SDK. In the [Azure Portal](https://portal.azure.com/), go to Dashboard > Storage > your-storage-account:

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/1276n6osvvj71btuuoju.png)

Note down the "Storage account name" and "Connection string" obtained at Access keys under the Settings tab.

> TIP: The connection string should start with `DefaultEndpointsProtocol=`

# NestJS Azure Storage Installation

Next, we need to install the `@nestjs/azure-database` SDK from NPM:

```shell
$ npm i --save @nestjs/azure-database dotenv
```

> We also install the `dotenv` package that allows us to deal with environment variables.

We will then create a file called .env with the following content:


```shell
AZURE_STORAGE_CONNECTION_STRING="<the connection string we copied from previous step>"
```

*Also very important: we will make sure to add our `.env` file to the `.gitignore`! The `.env` file MUST NOT be versioned on Git.*

Once the `.env` file created and ready, we will include the following call to the `src/main.ts` file:


```typescript
if (process.env.NODE_ENV !== 'production') require('dotenv').config();
```

> TIP: This line must be added _before any other imports_ in the `src/main.ts` file!

Our setup is now ready. Let's implement the logic of our application.

# Preparing our business logic

The Azure Table Storage support in NestJS follows the Object-Relational Mapping (ORM) design pattern which is basically a "structured" way of accessing a database from our code - letting you use an API instead of writing actual SQL code.

In order to implement this design pattern we will need to create the following components, for each feature:

- `DTO` (or data transfer object)
  - This is the object that will represent our data. DTO's are primarily used to transfer data between application services, such as that between an HTTP service and a browser.
- `Entity`
  - This is basically a class mapped to the table schema.
- `Repository`
  - This is the component that is responsible for communicating with the database.

Let's first create a NestJS feature module where we will host our feature business logic. We will use the NestJS CLI to create a `Cat` feature:

```shell
$ nest generate module cat
```

> NOTE: We will come back to our generated module at the end of the process.

## DTO

The first component we need to create for our `Cat` feature is a DTO. Inside a file named `cat.dto.ts`, we create the following class:

```typescript
export class CatDTO {
  name: string;
  age: number;
}
```

## Entity

Next, we need an `Entity`. To do so, we create a file called `cat.entity.ts` and describe the model using the decorators provided by `@nestjs/azure-database`:

| Entity                             	| Represents                                     	| Required 	|
|------------------------------------	|------------------------------------------------	|----------	|
| `@EntityPartitionKey(value: string)` 	| The `PartitionKey` of the entity                 	| Yes      	|
| `@EntityRowKey(value: string)`       	| The `RowKey` of the entity                       	| Yes      	|
| `@EntityInt32(value?: string)`       	| Signed 32-bit integer values                   	|          	|
| `@EntityInt64(value?: string)`       	| Signed 64-bit integer values                   	|          	|
| `@EntityBinary(value?: string)`      	| Binary (blob) data                             	|          	|
| `@EntityBoolean(value?: string)`     	| `true` or `false` values                           	|          	|
| `@EntityString(value?: string)`      	| Character data                                 	|          	|
| `@EntityDouble(value?: string)`      	| Floating point numbers with 15 digit precision 	|          	|
| `@EntityDateTime(value?: string)`    	| Time of day                                    	|          	|


> **Note:** The API might slightly change in the stable release.

For instance, the shape of the following entity:

```typescript
import {
  EntityPartitionKey,
  EntityRowKey,
  EntityString,
  EntityIn32
} from '@nestjs/azure-database';

@EntityPartitionKey('CatID')
@EntityRowKey('CatName')
export class Cat {
  @EntityString() name: string;
  @EntityIn32() age: number;
}
```

The `Cat` entity will be automatically converted to the following schema that is expected by the Azure Table Storage:


```json
{
  "PartitionKey": { "_": "CatID", "$": "Edm.String" },
  "RowKey": { "_": "CatName", "$": "Edm.String" },
  "name": { "_": undefined, "$": "Edm.String" },
  "age": { "_": undefined, "$": "Edm.Int32" }
}
```

## Repository

After the DTO and the Entity, we need now to create a `Cat` service that will abstract all the CRUD operations related to the `Cat` entity. This service will use the Azure Table Storage `Repository`.

Let's create a service using the NestJS CLI:

```shell
$ nest generate service cat
```

Inside the created `cat.service.ts`, we import the `Repository` and provide it with the `Cat` entity definition created in the previous step:


```typescript
import { Injectable } from '@nestjs/common';
import { Repository, InjectRepository } from '@nestjs/azure-database';
import { Cat } from './cat.entity';

@Injectable()
export class CatService {
  constructor(
    @InjectRepository(Cat)
    private readonly catRepository: Repository<Cat>,
  ) {}

  // ... other code ...
```

The Azure Table Storage `Repository` interface provides a bunch of public APIs and types for managing various CRUD (`Create`, `Read`, `Update` and `Delete`) operations. Let's see how we can implement each different operation using `@nestjs/azure-database` SDK.

## The methods we will be invoking are the following:

- `create(entity: T): Promise<T>` to create a new entity.
- `findAll(tableQuery?: azure.TableQuery, currentToken?: azure.TableService.TableContinuationToken): Promise<AzureTableStorageResultList<T>>`
to find all entities that match the given query (return all entities if no query provided).
- `find(rowKey: string, entity: Partial<T>): Promise<T>` to find one entity using its RowKey.
- `update(rowKey: string, entity: Partial<T>): Promise<T>` to update an entity. This does a partial update.
- `delete(rowKey: string, entity: T): Promise<AzureTableStorageResponse>` to remove an entity using its RowKey.

### Here is an example of such implementation:

```typescript
import { Injectable } from '@nestjs/common';
import { Repository, InjectRepository } from '@nestjs/azure-database';
import { Cat } from './cat.entity';

@Injectable()
export class CatService {
  constructor(
    @InjectRepository(Cat)
    private readonly catRepository: Repository<Cat>,
  ) {}

  // find one cat entitu by its rowKey
  async find(rowKey: string, cat: Cat): Promise<Cat> {
    return this.catRepository.find(rowKey, cat);
  }

  // find all cat entities
  async findAll(): Promise<AzureTableStorageResultList<Cat>> {
    return this.catRepository.findAll();
  }

  // create a new cat entity
  async create(cat: Cat): Promise<Cat> {
    return this.catRepository.create(cat);
  }

  // update the a cat entity by its rowKey
  async update(rowKey: string, cat: Partial<Cat>): Promise<Cat> {
    return this.catRepository.update(rowKey, cat);
  }

  // delete a cat entity by its rowKey
  async delete(rowKey: string, cat: Cat): Promise<AzureTableStorageResponse> {
    return this.catRepository.delete(rowKey, cat);
  }
}
```

## Controller

The last step is to implement the NestJS controller that will process the HTTP requests. Let's create such a controller using the NestJS CLI:

```shell
$ nest generate controller cat
```

The implementation of the controller is straightforward and would probably depend on your application business needs. Here is an example of an implementation:

```typescript
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  UnprocessableEntityException,
  NotFoundException,
  Patch
} from '@nestjs/common';
import { CatDto } from './cat.dto';
import { Cat } from './cat.entity';
import { CatService } from './cat.service';

@Controller('cats')
export class CatController {
  constructor(private readonly catService: CatService) {}

  @Get()
  async getAllCats() {
    return await this.catService.findAll();
  }

  @Get(':rowKey')
  async getCat(@Param('rowKey') rowKey) {
    try {
      return await this.catService.find(rowKey, new Cat());
    } catch (error) {
      // Entity not found
      throw new NotFoundException(error);
    }
  }

  @Post()
  async createCat(
    @Body()
    catData: CatDto,
  ) {
    try {
      const cat = new Cat();
      // Disclaimer: Assign only the properties you are expecting!
      Object.assign(cat, catData);

      return await this.catService.create(cat);
    } catch (error) {
      throw new UnprocessableEntityException(error);
    }
  }

  @Put(':rowKey')
  async saveCat(@Param('rowKey') rowKey, @Body() catData: CatDto) {
    try {
      const cat = new Cat();
      // Disclaimer: Assign only the properties you are expecting!
      Object.assign(cat, catData);

      return await this.catService.update(rowKey, cat);
    } catch (error) {
      throw new UnprocessableEntityException(error);
    }
  }

  @Patch(':rowKey')
  async updateCatDetails(@Param('rowKey') rowKey, @Body() catData: Partial<CatDto>) {
    try {
      const cat = new Cat();
      // Disclaimer: Assign only the properties you are expecting!
      Object.assign(cat, catData);

      return await this.catService.update(rowKey, cat);
    } catch (error) {
      throw new UnprocessableEntityException(error);
    }
  }

  @Delete(':rowKey')
  async deleteDelete(@Param('rowKey') rowKey) {
    try {
      const response = await this.catService.delete(rowKey, new Cat());

      if (response.statusCode === 204) {
        return null;
      } else {
        throw new UnprocessableEntityException(response);
      }
    } catch (error) {
      throw new UnprocessableEntityException(error);
    }
  }
}
```

# Putting everything together

We have finished the implementation of our `Cat` feature. In this last step, we will need to import the `AzureTableStorageModule` inside our Nest feature module `cat.module.ts` that we created earlier:

```typescript
import { Module } from '@nestjs/common';
import { AzureTableStorageModule } from '@nestjs/azure-database';
import { CatController } from './cat.controller';
import { CatService } from './cat.service';
import { Cat } from './cat.entity';

@Module({
  imports: [AzureTableStorageModule.forFeature(Cat)],
  providers: [CatService],
  controllers: [CatController],
})
export class CatModule {}
```

The `AzureTableStorageModule` module takes in few optional arguments:

```typescript
AzureTableStorageModule.forFeature(Cat, {
  table: 'AnotherTableName',
  createTableIfNotExists: true,
})
```

- `table: string`: The name of the table. If not provided, the name of the Cat entity will be used as a table name
- `createTableIfNotExists: boolean`: Whether to automatically create the table if it doesn't exist or not:
  - If `true` the table will be created during the startup of the app.
  - If `false` the table will not be created. **You will have to create the table by yourself before querying it!**

# In Conclusion

We just implemented a new `Cat` feature for our application that uses `@nestjs/azure-database` official package to add support for Azure Table Storage. With NestJS's modular system, we are able to install it and set it up with our application almost like a native Nest-feature!

If you're interested in learning more about Serverless NestJS apps with Azure [read more here](https://trilon.io/blog/deploy-nestjs-azure-functions).

{% github nestjs/azure-database %}