Building CRUD Operations with TypeORM and Nest.js

When it comes to building modern full-stack applications with support for CRUD (create, read, update, delete) operations, there are a lot of options to choose from. In this tutorial, we're going to focus on using TypeORM and Nest.js – two powerful and popular tools that provide a lot of features to help you build efficient and scalable applications.

What is TypeORM?

TypeORM is an Object-Relational Mapping (ORM) library that provides a way to map your database schema onto your TypeScript code. This allows you to easily interact with your database within your TypeScript files, without having to write SQL queries directly. TypeORM supports a wide range of databases including PostgreSQL, MySQL, SQLite, and more.

What is Nest.js?

Nest.js is a progressive JavaScript framework for building efficient, scalable, and modular server-side applications. It's built on top of Node.js and provides a lot of features to help you build powerful and maintainable applications. Nest.js is designed to be flexible and unopinionated, allowing you to choose the libraries and tools that work best for your specific use case.

Setting up Your Development Environment

Before we dive into building our CRUD operations, we need to make sure we have everything set up correctly. Here's what you'll need:

  • Node.js
  • TypeScript
  • Nest.js
  • TypeORM

If you don't have any of these installed, you can follow the installation guides on their respective websites.

Creating a Database Schema

Now that we have our development environment set up, we're ready to create our database schema. For the purposes of this tutorial, we'll be using PostgreSQL, but you can use any database supported by TypeORM.

First, we need to create our database. Open up your terminal and run the following command:

createdb <database name>

Next, let's create our table. We'll be creating a simple table called "tasks" with three columns: "id", "title", and "description". In your terminal, run the following command:

psql <database name>
CREATE TABLE tasks (
  id SERIAL PRIMARY KEY,
  title VARCHAR(255) NOT NULL,
  description TEXT
);

Now that we have our database and table set up, we're ready to start building our CRUD operations.

Creating a Nest.js Project

Let's start by creating a new Nest.js project. Open up your terminal and run the following command:

nest new <project name>

This will create a new Nest.js project with all the necessary files and folders.

Connecting to the Database

Next, we need to connect to our database using TypeORM. First, let's install the necessary packages:

npm install typeorm pg

The "typeorm" package is the main TypeORM library, and "pg" is the PostgreSQL driver for TypeORM.

Next, we need to create a TypeORM configuration file. Create a new file called "ormconfig.json" in the root of your project and add the following:

{
  "type": "postgres",
  "host": "localhost",
  "port": 5432,
  "username": "<your username>",
  "password": "<your password>",
  "database": "<your database name>",
  "entities": ["src/**/**.entity{.ts,.js}"],
  "synchronize": true
}

Make sure to replace the "username", "password", and "database" fields with your actual database credentials.

The "entities" field specifies where TypeORM should look for the entity files – the files that define the structure of your database. We'll be creating this file next.

Creating the Task Entity

Now that we have our database connection set up, we're ready to create the "Task" entity. In Nest.js, entities are classes that represent a database table. Create a new file called "task.entity.ts" in the "src" folder and add the following:

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class Task {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column({ nullable: true })
  description?: string;
}

This defines our "Task" entity, with three properties: "id", "title", and "description". The "@PrimaryGeneratedColumn()" decorator specifies that the "id" column should automatically generate a value on each new row, and the "@Column()" decorator specifies the remaining columns.

We've also added a "nullable" option to the "description" column, which means that it can be null (or undefined) in the database. This will come in handy later when we update tasks.

Creating the Task Service

Now that we have our entity created, we're ready to start building the "Task" service. In Nest.js, services are classes that handle the business logic of your application. Create a new file called "task.service.ts" in the "src" folder and add the following:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Task } from './task.entity';

@Injectable()
export class TaskService {
  constructor(
    @InjectRepository(Task)
    private taskRepository: Repository<Task>
  ) {}

  async findAll(): Promise<Task[]> {
    return await this.taskRepository.find();
  }

  async findOne(id: number): Promise<Task> {
    return await this.taskRepository.findOne(id);
  }

  async create(task: Task): Promise<Task> {
    return await this.taskRepository.save(task);
  }

  async update(id: number, task: Task): Promise<void> {
    await this.taskRepository.update(id, task);
  }

  async delete(id: number): Promise<void> {
    await this.taskRepository.delete(id);
  }
}

This defines our "TaskService" with all the CRUD operations we need. We're injecting the TypeORM "Repository" into our service using the "@InjectRepository()" decorator, and then we're using this repository to perform the necessary operations.

The "findAll()" method returns all the tasks from the database, "findOne()" returns a single task by ID, "create()" creates a new task, "update()" updates an existing task, and "delete()" deletes a task by ID.

Creating the Task Controller

Finally, we need to create a controller that will handle the incoming HTTP requests and call the appropriate service methods. In Nest.js, controllers are classes that handle incoming HTTP requests and return responses.

Create a new file called "task.controller.ts" in the "src" folder and add the following:

import { Controller, Get, Post, Put, Delete, Param, Body } from '@nestjs/common';
import { TaskService } from './task.service';
import { Task } from './task.entity';

@Controller('tasks')
export class TaskController {
  constructor(private taskService: TaskService) {}

  @Get()
  async findAll(): Promise<Task[]> {
    return this.taskService.findAll();
  }

  @Get(':id')
  async findOne(@Param('id') id: number): Promise<Task> {
    return this.taskService.findOne(id);
  }

  @Post()
  async create(@Body() task: Task): Promise<Task> {
    return this.taskService.create(task);
  }

  @Put(':id')
  async update(@Param('id') id: number, @Body() task: Task): Promise<void> {
    await this.taskService.update(id, task);
  }

  @Delete(':id')
  async delete(@Param('id') id: number): Promise<void> {
    await this.taskService.delete(id);
  }
}

This defines our "TaskController" with all the necessary HTTP routes. We're using the "@Get()" decorator to define a route that returns all tasks, "@Get(':id')" to define a route that returns a single task by ID, "@Post()" to define a route that creates a new task, "@Put(':id')" to define a route that updates an existing task, and "@Delete(':id')" to define a route that deletes a task by ID.

Testing Your Application

Now that we have our entities, services, and controllers created, we're ready to test our application. Start your server by running the following command:

npm run start:dev

This will start your server in development mode, with auto-reloading on code changes.

Then, open up your favorite API testing tool (like Postman) and test out the different HTTP routes:

  • GET http://localhost:3000/tasks – returns all tasks
  • GET http://localhost:3000/tasks/:id – returns a single task by ID
  • POST http://localhost:3000/tasks – creates a new task
  • PUT http://localhost:3000/tasks/:id – updates an existing task by ID
  • DELETE http://localhost:3000/tasks/:id – deletes a task by ID

Make sure to replace "localhost:3000" with your actual server URL.

Deploying to the Cloud

Now that we have our application working locally, we can deploy it to the cloud using a service like Heroku. Here are the general steps:

  1. Create a new Heroku app
  2. Connect your Heroku app to your Git repository
  3. Add a PostgreSQL add-on to your Heroku app
  4. Push your code to Heroku
  5. Run database migrations on Heroku

For a more detailed guide on how to do this, check out the official documentation:

https://devcenter.heroku.com/articles/getting-started-with-nodejs

Conclusion

In this tutorial, we learned how to efficiently build a modern full-stack application with CRUD operations using TypeORM and Nest.js. We covered everything from setting up your development environment to deploying your application to the cloud.

If you're interested in learning more about TypeORM and Nest.js, there are a lot of resources available online, including their respective documentation and GitHub repositories.