A progressive Node.js framework for building efficient and scalable server-side applications.
Azure Database (Table Storage, Cosmos DB and more) module for Nest framework (node.js)
Learn how to get started with Azure table storage for NestJS
For Table Storage
- Create a Storage account and resource (read more)
- For Table Storage, In the Azure Portal, go to Dashboard > Storage > your-storage-account.
- Note down the "Storage account name" and "Connection string" obtained at Access keys under Settings tab.
For Cosmos DB
- Create a Cosmos DB account and resource (read more)
- For Cosmos DB, In the Azure Portal, go to Dashboard > Azure Cosmos DB > your-cosmos-db-account.
- Note down the "URI" and "Primary Key" obtained at Keys under Settings tab.
$ npm i --save @nestjs/azure-database
- Create or update your existing
.env
file with the following content:
AZURE_STORAGE_CONNECTION_STRING=
-
IMPORTANT: Make sure to add your
.env
file to your .gitignore! The.env
file MUST NOT be versioned on Git. -
Make sure to include the following call to your main file:
if (process.env.NODE_ENV !== 'production') require('dotenv').config();
This line must be added before any other imports!
- Create a new feature module, eg. with the nest CLI:
$ nest generate module contact
- Create a Data Transfer Object (DTO) inside a file named
contact.dto.ts
:
export class ContactDTO {
name: string;
message: string;
}
- Create a file called
contact.entity.ts
and describe the entity model using the provided decorators:
-
@EntityPartitionKey(value: string)
: Represents thePartitionKey
of the entity (required). -
@EntityRowKey(value: string)
: Represents theRowKey
of the entity (required). -
@EntityInt32(value?: string)
: For signed 32-bit integer values. -
@EntityInt64(value?: string)
: For signed 64-bit integer values. -
@EntityBinary(value?: string)
: For binary (blob) data. -
@EntityBoolean(value?: string)
: Fortrue
orfalse
values. -
@EntityString(value?: string)
: For character data. -
@EntityDouble(value?: string)
: For floating point numbers with 15 digit precision. -
@EntityDateTime(value?: string)
: For time of day.
For instance, the shape of the following entity:
import { EntityPartitionKey, EntityRowKey, EntityString } from '@nestjs/azure-database';
@EntityPartitionKey('ContactID')
@EntityRowKey('ContactName')
export class Contact {
@EntityString() name: string;
@EntityString() message: string;
}
Will be automatically converted to:
{
"PartitionKey": { "_": "ContactID", "$": "Edm.String" },
"RowKey": { "_": "ContactName", "$": "Edm.String" },
"name": { "_": undefined, "$": "Edm.String" },
"message": { "_": undefined, "$": "Edm.String" }
}
Note: The provided entity type annotations represent the Entity Data Model types.
- Import the
AzureTableStorageModule
inside your Nest feature modulecontact.module.ts
:
import { Module } from '@nestjs/common';
import { AzureTableStorageModule } from '@nestjs/azure-database';
import { ContactController } from './contact.controller';
import { ContactService } from './contact.service';
import { Contact } from './contact.entity';
@Module({
imports: [AzureTableStorageModule.forFeature(Contact)],
providers: [ContactService],
controllers: [ContactController],
})
export class ContactModule {}
You can optionally pass in the following arguments:
AzureTableStorageModule.forFeature(Contact, {
table: 'AnotherTableName',
createTableIfNotExists: true,
});
table: string
: The name of the table. If not provided, the name of theContact
entity will be used as a table namecreateTableIfNotExists: boolean
: Whether to automatically create the table if it doesn't exists 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!
- If
- Create a service that will abstract the CRUD operations:
$ nest generate service contact
- Use the
@InjectRepository(Contact)
to get an instance of the AzureRepository
for the entity definition created earlier:
import { Injectable } from '@nestjs/common';
import { Repository, InjectRepository } from '@nestjs/azure-database';
import { Contact } from './contact.entity';
@Injectable()
export class ContactService {
constructor(
@InjectRepository(Contact)
private readonly contactRepository: Repository<Contact>,
) {}
}
The AzureTableStorageRepository
provides a couple of public APIs and Interfaces for managing various CRUD operations:
create(entity: T, rowKeyValue?: string): Promise<T>
: creates a new entity.
@Post()
async create(contact: Contact, rowKeyValue: string): Promise<Contact> {
//if rowKeyValue is null, rowKeyValue will generate a UUID
return this.contactRepository.create(contact, rowKeyValue)
}
find(rowKey: string, entity: Partial<T>): Promise<T>
: finds one entity using its RowKey
.
@Get(':rowKey')
async getContact(@Param('rowKey') rowKey) {
try {
return await this.contactRepository.find(rowKey, new Contact());
} catch (error) {
// Entity not found
throw new UnprocessableEntityException(error);
}
}
findAll(tableQuery?: azure.TableQuery, currentToken?: azure.TableService.TableContinuationToken): Promise<AzureTableStorageResultList<T>>
: finds all entities that match the given query (return all entities if no query provided).
@Get()
async getAllContacts() {
return await this.contactRepository.findAll();
}
update(rowKey: string, entity: Partial<T>): Promise<T>
: Updates an entity. It does a partial update.
@Put(':rowKey')
async saveContact(@Param('rowKey') rowKey, @Body() contactData: ContactDTO) {
try {
const contactEntity = new Contact();
// Disclaimer: Assign only the properties you are expecting!
Object.assign(contactEntity, contactData);
return await this.contactRepository.update(rowKey, contactEntity);
} catch (error) {
throw new UnprocessableEntityException(error);
}
}
@Patch(':rowKey')
async updateContactDetails(@Param('rowKey') rowKey, @Body() contactData: Partial<ContactDTO>) {
try {
const contactEntity = new Contact();
// Disclaimer: Assign only the properties you are expecting!
Object.assign(contactEntity, contactData);
return await this.contactRepository.update(rowKey, contactEntity);
} catch (error) {
throw new UnprocessableEntityException(error);
}
}
delete(rowKey: string, entity: T): Promise<AzureTableStorageResponse>
: Removes an entity from the database.
@Delete(':rowKey')
async deleteDelete(@Param('rowKey') rowKey) {
try {
const response = await this.contactRepository.delete(rowKey, new Contact());
if (response.statusCode === 204) {
return null;
} else {
throw new UnprocessableEntityException(response);
}
} catch (error) {
throw new UnprocessableEntityException(error);
}
}
- Create or update your existing
.env
file with the following content:
AZURE_COSMOS_DB_NAME=
AZURE_COSMOS_DB_ENDPOINT=
AZURE_COSMOS_DB_KEY=
-
IMPORTANT: Make sure to add your
.env
file to your .gitignore! The.env
file MUST NOT be versioned on Git. -
Make sure to include the following call to your main file:
if (process.env.NODE_ENV !== 'production') require('dotenv').config();
This line must be added before any other imports!
Note: Check out the CosmosDB example project included in the sample folder
- Create a new feature module, eg. with the nest CLI:
$ nest generate module event
- Create a Data Transfer Object (DTO) inside a file named
event.dto.ts
:
export class EventDTO {
name: string;
type: string;
date: Date;
location: Point;
}
- Create a file called
event.entity.ts
and describe the entity model using the provided decorators:
-
@CosmosPartitionKey(value: string)
: Represents thePartitionKey
of the entity (required). -
@CosmosDateTime(value?: string)
: For DateTime values.
For instance, the shape of the following entity:
import { CosmosPartitionKey, CosmosDateTime, Point } from '@nestjs/azure-database';
@CosmosPartitionKey('type')
export class Event {
id?: string;
type: string;
@CosmosDateTime() createdAt: Date;
location: Point;
}
Will be automatically converted to:
{
"type": "Meetup",
"createdAt": "2019-11-15T17:05:25.427Z",
"position": {
"type": "Point",
"coordinates": [2.3522, 48.8566]
}
}
- Import the
AzureCosmosDbModule
inside your Nest feature moduleevent.module.ts
:
import { Module } from '@nestjs/common';
import { AzureCosmosDbModule } from '@nestjs/azure-database';
import { EventController } from './event.controller';
import { EventService } from './event.service';
import { Event } from './event.entity';
@Module({
imports: [
AzureCosmosDbModule.forRoot({
dbName: process.env.AZURE_COSMOS_DB_NAME,
endpoint: process.env.AZURE_COSMOS_DB_ENDPOINT,
key: process.env.AZURE_COSMOS_DB_KEY,
}),
AzureCosmosDbModule.forFeature([{ dto: Event }]),
],
providers: [EventService],
controllers: [EventController],
})
export class EventModule {}
- Create a service that will abstract the CRUD operations:
$ nest generate service event
- Use the
@InjectModel(Event)
to get an instance of the Azure Cosmos DB Container for the entity definition created earlier:
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/azure-database';
import { Event } from './event.entity';
@Injectable()
export class EventService {
constructor(
@InjectModel(Event)
private readonly eventContainer,
) {}
}
The Azure Cosmos DB Container
provides a couple of public APIs and Interfaces for managing various CRUD operations:
create(entity: T): Promise<T>
: creates a new entity.
@Post()
async create(event: Event): Promise<Event> {
return this.eventContainer.items.create(event)
}
query<T>(query: string | SqlQuerySpec, options?: FeedOptions): QueryIterator<T>
: run a SQL Query to find a document.
@Get(':id')
async getContact(@Param('id') id) {
try {
const querySpec = {
query: "SELECT * FROM root r WHERE r.id=@id",
parameters: [
{
name: "@id",
value: id
}
]
};
const { resources } = await this.eventContainer.items.query<Event>(querySpec).fetchAll()
return resources
} catch (error) {
// Entity not found
throw new UnprocessableEntityException(error);
}
}
read<T>(options?: RequestOptions): Promise<ItemResponse<T>>
: Get a document.
replace<T>(body: T, options?: RequestOptions): Promise<ItemResponse<T>>
: Updates a document.
@Put(':id')
async saveEvent(@Param('id') id, @Body() eventData: EventDTO) {
try {
const { resource: item } = await this.eventContainer.item<Event>(id, 'type').read()
// Disclaimer: Assign only the properties you are expecting!
Object.assign(item, eventData);
const { resource: replaced } = await this.eventContainer
.item(id, 'type')
.replace<Event>(item)
return replaced
} catch (error) {
throw new UnprocessableEntityException(error);
}
}
delete<T>(options?: RequestOptions): Promise<ItemResponse<T>>
: Removes an entity from the database.
@Delete(':id')
async deleteEvent(@Param('id') id) {
try {
const { resource: deleted } = await this.eventContainer
.item(id, 'type')
.delete<Event>()
return deleted;
} catch (error) {
throw new UnprocessableEntityException(error);
}
}
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please read more here.
- Author - Wassim Chegham
- Website - https://wassim.dev
- Twitter - @manekinekko
Nest is MIT licensed.