Hamburger Icon
  • Labs icon Lab
  • Core Tech
Labs

Guided: Databases and TypeORM in NestJS

This lab provides a hands-on environment to practice integrating a PostgreSQL database with an existing NestJS application. You will configure your NestJS application to use a PostgreSQL database using TypeORM, an Object-Relational Mapper. From there, you will become familiar with TypeORM and how it can be used to create table entities, perform migrations, and interact with the database via a repository pattern. Lastly, this lab will touch on API optimizations, specifically pagination, and database transactions in case of failures when saving data.

Labs

Path Info

Level
Clock icon Intermediate
Duration
Clock icon 1h 34m
Published
Clock icon Feb 27, 2025

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Table of Contents

  1. Challenge

    Introduction

    Welcome to the Guided: Databases and TypeORM in NestJS Lab

    Throughout this lab, you will become familiar with how PostgreSQL, a popular database option, is integrated with a NestJS application. You will use TypeORM, an Object-Relational Mapper (ORM), to interact with the relational database, PostgreSQL.

    This lab will have you do the following:

    • Configure a NestJS application to use TypeORM with PostgreSQL.
    • Define database tables using TypeORM's Entity decorator.
    • Manage database relationships.
    • Create and run migrations.
    • Perform interactions with the database using the supported TypeORM Repository pattern.
    • Handle database transactions.
    • Optimize database queries.

    A basic understanding of Typescript and NestJS will be useful to complete this lab, but it is not necessary. Specific knowledge required to complete this lab will be provided.

    For the purposes of this lab, any packages or libraries that serve as a dependency for the completion of this lab are already installed for you.

    You may find three Terminal windows helpful while you complete this lab. One for running the client, another for running the server, and a third one to run migrations. You will not be starting the application until you have implemented parts of the PostsService in a future step.

    --- > Throughout the next steps, you can refer to the /solution directory at any point if you need assistance. The /solution directory contains subdirectories for each of the following steps.

  2. Challenge

    Configure TypeORM with PostgreSQL in NestJS

    Overview of TypeORM

    TypeORM is an Object-Relational Mapper (ORM) for TypeScript and JavaScript that allows developers to interact with relational databases using object-oriented syntax instead of writing raw SQL queries. It is widely used with frameworks like NestJS and supports databases such as PostgreSQL and others.

    Key Features of TypeORM * Entity-Based Modeling – Defines database tables as TypeScript classes * Decorators & Metadata – Uses decorators like `@Entity()`, `@Column()`, `@PrimaryGeneratedColumn()` to define database schema * Migrations – Allows schema changes without data loss * Repository & Active Record Patterns – Supports both Repository (`getRepository()`) and Active Record (instance methods) approaches * For this lab, you will learn about the Repository pattern. * Query Builder – Provides a powerful API to construct complex queries programmatically * This lab will not review Query Builder. If you find yourself in a situation where the TypeORM repository methods do not perform the SQL you need, you can use Query Builder to override and build SQL queries using Query Builder's interface. To learn more about Query Builder you can visit the [documentation](https://typeorm.io/select-query-builder). * Another option is calling the `query` method on an entity's repository if you need to execute a specific string of SQL. * Relations Handling – Supports One-to-One, One-to-Many, Many-to-Many relationships * Transaction Support – Enables execution of transactions for critical database operations

    It is preferred to use TypeORM along with NestJS applications because TypeORM facilitates the following:

    • TypeScript support – Seamless integration with NestJS
    • Ease of use – Uses decorators for defining schemas
    • Scalability – Works well with large-scale applications
    • Extensibility – Supports advanced database features like caching and transactions

    Configure TypeORM with PostgreSQL in NestJS

    The PostgreSQL database, nestjs_db, has been created for you. The PostgreSQL user, myuser, has also been created for you with the password, mypassword. This password is not a great production password, but it works for this lab scenario. myuser has been granted the proper permissions to connect with the database, nestjs_db, and perform migrations. The user, myuser, will also be able to read and write to the database.

    The NestJS application will connect to the database, nestjs_db, using myuser. The TypeOrmModule has a method, called forRoot, that is used to configure the PostgreSQL database connection globally. It also sets up the TypeORM connection and makes it available throughout the app.

    In this step, you will use TypeOrmModule.forRoot() in the root module, AppModule, to configure the NestJS application to connect to the PostgreSQL database,nestjs_db. You will do this with under the myuser user at port 5432 and host localhost. When you configure TypeORM with PostgreSQL in the root module, it will establish the database connection when the application starts.

    The root module provided is bare bones and only contains global imports for the Posts and Comments modules. Complete the tasks below to configure the NestJS application to use TypeORM with PostgreSQL.

    Details on TypeOrmModule.forRoot()
    • TypeOrmModule.forRoot() is called inside the root module (AppModule) to initialize the database connection.
    • It allows you to configure the database type, host, port, username, password, and other settings.
    • It automatically registers TypeORM’s connection, entity manager, and repositories across the app.
    • It ensures proper database initialization at app startup.
    ## Use Environmental Variables to Configure TypeORM to Use PostgreSQL

    In the previous task, you hard coded database credentials (like username, password, host) in /server/app.module.ts. Even though this is enough to start using TypeORM to interact with the PostgreSQL database throughout the NestJS application, it is recommended to use environment variables to configure TypeORM with PostgreSQL in NestJS.

    Using environment variables for credentials is encouraged to protect sensitive information, enable environment-specific configurations, simplify deployment and configuration management with cloud providers, and improve code maintainability.

    In the next task, you will update /server/app.module.ts to use the environmental variables provided to you in /server/.env.

  3. Challenge

    Design and Implement Post Entity

    Use TypeORM Decorators to Design and Implement Database Entities

    Using TypeORM decorators, you can define database tables as TypeScript classes.

    What are NestJS Features?

    To interact with these database tables (entities), you will often have a NestJS feature with includes the following:

    • An entity that provides the table column's definitions, constraints, and relationships
    • A controller for each table that defines API endpoints to request specific actions be performed on the table's data
    • A service for each table that can be injected into the controller to perform the queries necessary to complete API endpoint responsibilities
    • A module for each table that can be used to specify the controller, providers, and imports that can be used

    The module, controller, and server for a table can be created using NestJS's CLI with the following commands:

    • nest generate module <entity name>
    • nest generate controller <entity name>
    • nest generate server <entity name>

    These commands will create a subdirectory in the src directory of a NestJS application named after the entity. In the appropriate subdirectory, you want to create a file called <entity name>.entity.ts and get started with the table definition.

    For the context of this lab, a feature's controller has been implemented for you and you will be finishing each feature's entity, service, and module.

    Relevant TypeORM Entity Decorators In this lab, you will use the TypeORM decorators below to design and implement entites.
    • @Entity(): The @Entity() decorator is used on a Typescript class to make it a database table.
    • @PrimaryGeneratedColumn(): The @PrimaryGeneratedColumn() decorator means that this field will be autogenerated for you and will represent the primary key in the entity table.
    • @Column(): The @Column() decorator means that this field will be a column in the entity table.
    • @OneToMany(): The @OneToMany() decorator is used to define a one-to-many relationship from this entity to another entity.
    • @ManyToOne(): The @ManyToOne() decorator is used to define a many-to-one relationship from this entity to another entity.
    • @JoinColumn(): The @JoinColumn() decorator specifies the the foreign key column name in the table relations.

    In this step, you will be defining and implementing the Post database entity. A Post will have the following columns:

    • id: number
    • content: string
    • title: string

    A file for the Post entity has been provided at server/src/post/post.entity.ts. In the upcoming tasks, you will be making changes to this file. Any imports or dependencies needed to complete this step have already been included for you.

  4. Challenge

    Design and Implement Comment Entity

    Design and Implement Comment Entity

    In this step, you will be defining and implementing the Comment entity. A Comment will have the following columns:

    • id: number
    • content: string

    A file for the Comment entity has been provided at /server/src/comments/comment.entity.ts. In the upcoming tasks, you will be making changes to this file. Any imports or dependencies needed to complete this step have already been included for you.

  5. Challenge

    Manage Database Relationships within Database Entities

    Manage Database Relationships within Database Entities

    If you have worked with a relational database before, you are probably familiar with the concept of relations. Relations allow you to create entities that are related and interact with them seamlessly.

    TypeORM supplies decorators that can be used on a database entity to specify the following types of relationships:

    • One-to-One
    • Many-to-One
    • One-to-Many
    • Many-to-Many

    In this NestJS forum application, a Post can be associated with many Comments, but a Comment can only be associated with one Post. You will also want to delete all comments on the post when the post is deleted. This deletion process is referred to as cascade deletion.

  6. Challenge

    Create and Run Database Migrations

    Create and Run Database Migrations

    Right now, you would see your nestjs_db database has no tables if you were to inspect the database contents using the psql command interface.

    You want to create the Post and Comment entities from the previous step as database tables with the correct table columns using migrations. To do this, you will use the typeorm-ts-node-commonjs command to create the migration SQL file and run it.

    What are Migrations?

    As you build your application, you will often need to make changes to your application's database schema. A schema represents the overall structure of your database and the tables within it. If you change a table and that change is not reflected in the database's schema, you are likely to run into errors.

    You may also find that you need a way to update a table's schema without making changes to the data already in the table. This is where migrations can help!

    What is the typeorm-ts-node-commonjs Package?

    typeorm-ts-node-commonjs is a package alias for running TypeORM CLI with TypeScript and CommonJS module resolution. It can be used to create the required SQL to perform a migration in a file located at migrations-directory/<Migration Id>-migration-name. SQL in your migrations directory will be ran whenever the migration has not been ran or is in a down state.

    Migration Up and Down States

    You may notice that migrations have an up and down state with related methods. The up state runs when the migration is applied, and the down state runs when the migration is reversed.

    Every generated or created migration should include an up and down method that contains the SQL to be applied to the database.

    You are given a TypeORM data source file in /server/src/database/data-source.ts. This file does the following:

    • Imports the Comment entity
    • Imports the Post entity
    • Imports DataSource from 'typeOrm'
    • Specifies to TypeORM which entities to watch for definition changes
    • Specifies where to store migration SQL files
    • Specifies database configuration details

    This file will be passed as the data source file using the -d flag to tell TypeORM which database configuration file to use for the migration.

  7. Challenge

    Start the Forum Application

    Start the Forum Application

    You will use the forum application, Post It, provided to test the changes you will make to the PostsService and CommentsService over the next four steps.

    This application is a React front-end built to interact with the NestJS server you will be finishing. The routes the front-end needs to operate are already implemented by the PostsController and CommentsController and are calling the PostsService and CommentsService to handle interactions with the database.

    At this time, all methods in the PostsService and CommentsService are throwing NotImplementedException exceptions. This is why you will see in the client interface toasts indicating that requests are failing.

    As you implement methods in the related services, you will be able to use the forum application client interface to determine if your NestJS server is reading and writing to the database correctly.

    Note: When a NestJS application is created from scratch via the NestJS Command Line Interface (CLI) with the command nest new <project_name>, the start:dev script starts the application in watch mode. This means it does not need to be restarted after changes are made to the server code. When changes are made to the server code in watch mode, the process can detect these changes and recompile as needed.

    --- ## Visit the Client Forum Application Interface

    Open the SimpleBrowser tab and click the refresh button, or click this link to open in another browser: {{localhost:3000}}.

    When the application has been started, you will see a table with no posts and a couple buttons to Create a Post and Generate a Random Post with Random Comments. You will see pop-ups indicating failure while loading posts. As you implement the appropriate service methods, these requests will start working.

    Feel free to go to the window where you are running the forum application client interface anytime to test changes in the upcoming steps.


  8. Challenge

    Implement the Post Service Utilizing TypeORM's Repository

    Registering an Entity in a Feature Module

    You need to register an entity in a feature's module to have access to an entity repository or entity class in a feature's service. This can be done with the TypeOrmModule.forFeature() method.

    Details on TypeOrmModule.forFeature()

    In a NestJS application using TypeORM with PostgreSQL, TypeOrmModule.forFeature() is used to register specific entities (models) within a module, making them available for dependency injection.
    TypeOrmModule.forFeature() is helpful because it does the following:

    • It allows lazy-loading of repositories within feature modules.
    • It ensures proper dependency injection of repositories into services.
    • It follows NestJS modularity principles, making your app more scalable.

    `TypeOrmModule.forFeature()` is responsible and used for the following:
    * It is used inside feature modules (like `PostModule` and `CommentModule`) and not inside the root module (`AppModule`). * It registers specific entities (like `Post` and `Comment`) so they can be injected into services via `@InjectRepository()`. * Without `forFeature()`, you cannot inject repositories into services. * It specifies entities that will be registered within a feature module via a list passed to it.

    If you want to import a feature entity into controller or service (even if you do not need to inject the entity's repository), you will need to include it in the list of feature's being registered in the feature's module.

    --- ## The Repository Pattern

    The Repository Pattern is a software design pattern that provides an abstraction layer between the application and the data persistence layer, such as a database. It encapsulates data access logic, making it easier to manage, test, and modify database interactions without directly coupling the business logic to the database.

    Key Objectives of the Repository Pattern
    1. Encapsulation of Data Access Logic:
      • It hides the complexity of data queries and CRUD operations from the rest of the application.
    2. Separation of Concerns:
      • Business logic is decoupled from data access, improving maintainability.
    3. Testability:
      • It facilitates unit testing by allowing the repository to be mocked or replaced.
    4. Abstraction:
      • It enables switching between different data sources (e.g., changing from PostgreSQL to MongoDB) with minimal changes to the business logic.
    ---

    TypeORM Repositories

    In NestJS, the repository pattern is naturally integrated when using TypeORM, which provides built-in repository functionality via the Repository class. The Repository class is often injected into an feature's service and you have to inject a Repository for each entity you will be interfacing with data wise and register that entity in the feature's module.

    Relevant TypeORM Repository Methods In this lab, you will use the TypeORM `Repository` methods below to interact with the PostgreSQL database.
    • Repository.create()
      • Optional: Accepts an object literal with appropriate properties which will be written into new object
      • Returns the new object
    • Async Repository.save()
      • Saves a given entity or array of entities
      • If the entity exists, it is updated
    • Async Repository.find()
      • Accepts FindOptions object as an optional first parameter
        • FindOptions will perform additional selects, wheres, and joins pieces to a query
        • When FindOptions are not provided, this method will return all data in a table without related data
      • Returns an array of entities
    • Async Repository.findOne()
      • Accepts FindOptions for the first parameter
      • Finds the first entity that matches given FindOptions
      • Returns an entity
    • Async Repository.delete()
      • Deletes entities by entity id, ids, or given conditions in the first parameter
      • Returns a DeleteResult object that contains an affected key
      • When the value of affected is 0, nothing is deleted
      • When the value of affected is 1, something is deleted
      • Executes a single query to perform a deletion, unlike calling remove on an entity and then save separately
      • Skips any before actions or validations you have in place, which can lead to data corruption
    • Async Repository.update()
      • Partially updates entity by a given update options or entity id
      • Skips any before actions or validations
      • Executes one UPDATE SQL query
      • Returns a UpdateResult object
    • Async Repository.remove()
      • Removes a given entity or array of entities in a single transaction
      • Accepts an entity or array of entities
      • Returns the removed entity/entities
    • Async Repository.findAndCount()
      • Finds entities that match given FindOptions
      • Counts all entities that match given conditions
      • Optional: Can ignore pagination settings (from and take options)

    You can look at the TypeORM Repository API documentation to see what other ways you can interact with an entity table using the repository pattern.

    --- ## Implement the Post Service Utilizing TypeORM's Repository for the Post Data Entity

    With the client application running, you will soon be able to verify the changes that you make to the PostsService class in /server/src/posts/posts.service.ts are working.

    Follow the tasks below to learn more about how to interact with the Post entity database table using the repository pattern.

    All dependencies you will need to complete the following tasks have been installed for you and any necessary imports have already been imported for you. ## Test Code Changes Using Client Interface

    Visit the forum application wherever you have it running. You should now be able to view all, search for, create, edit, and delete posts. You should also be able to view a specific post and its related comments by clicking on the post's title in the table. You will still be able to see changes to posts even after refreshing the client interface. This is evidence that data is being persisted to the database.

    You currently should not be able to see paginated post results or generate random posts with comments. These will be implemented over the next two steps.

  9. Challenge

    Implement the Comment Service Utilizing TypeORM's Repository

    Implement the Comment Service Utilizing TypeORM's Repository

    Complete this step for extra repository practice with entities that relate to one another and a functioning client interface that can create, update, and delete comments.

    In the client interface, comments can be seen on an associated post, created on a specific post, deleted from the comment table on a post, and updated by double clicking the comment. Use the client interface as you complete the tasks below to verify that the NestJS application is interacting with the Comment table in the PostgreSQL database. ## Test Code Changes Using Client Interface

    You should now be able to return to your client interface and create, update, and delete comments.

  10. Challenge

    Handle Database Transactions

    Database Transactions

    A database transaction is a sequence of one or more database operations that are executed as a single unit of work. Transactions ensure that either all operations in the sequence are successfully completed or none of them are applied, maintaining data integrity and consistency.

    Why Database Transactions are Helpful
    A transaction follows the ACID properties: * Atomicity – Ensures that all operations within the transaction are completed. If any operation fails, the entire transaction is rolled back. * Consistency – Ensures that the database transitions from one valid state to another, maintaining all constraints and rules. * Isolation – Ensures that concurrent transactions do not interfere with each other, preventing issues like dirty reads or lost updates. * Durability – Ensures that once a transaction is committed, its changes are permanently stored in the database, even in the event of a crash.

    Transactions support your mission to do the following as a developer:

    • Maintain data consistency: Prevent partial updates that could lead to data corruption.
    • Handle multiple operations as a unit: Ensure that either all changes are applied or none at all.
    • Prevent race conditions: Ensure proper handling of concurrent operations, avoiding conflicts.
    • Rollback on failure: If an error occurs, you can revert to the previous stable state.

    Database Transactions with TypeORM

    Database transactions are possible in TypeORM by using the application's DataSource object to create a QueryRunner object that can connect to the database and interact with the database.

    Information About QueryRunner A `QueryRunner` provides the low-level control needed to work with database transactions in the upcoming tasks. A `QueryRunner` is capable of the following:
    • Managing database queries within a transaction
    • Providing a manager can be used instead of the default entity manager or entity repository to ensure that the operation is executed within a transaction

    In this step, you will implement the createPostWithComments method on the PostsService using TypeORM database transactions. This service is found at /server/src/posts/posts.service.ts.

    In order to perform database transactions using TypeORM, you will need to inject an instance of a TypeORM DataSource into your PostsService. After that, the DataSource instance can create a QueryRunner and manage a database transaction.

    Note: In the past, you had to import the appropriate entities in your feature module. You did not need to import DataSource in your feature module because you imported it in the root module, AppModule, in step two. This means that this NestJS application's DataSource is available in all features. ## Test Code Changes Using Client Interface

    If you go to your client interface, you can now generate random posts with a random number of comments from one to five. This will create the body that will be provided to your createPostWithComments service method.

    At this time, generate some random posts in the client interface, this will help with the next step on optimization (specifically pagination).

  11. Challenge

    API Optimization via Pagination

    Optimization

    When working with a database, you will at times need to optimize things to improve performance.

    Different Types of Optimization

    These optimizations may include:

    • Database Optimizations:
      • Specializing Workload Configuration
      • Indexing data and specific columns for quicker reads (But potentially slower writes—be careful not to overdo indexing, for example, by adding an index to every column)
      • Data Design and Redesign
      • Denormalization
    • SQL Optimizations:
      • Avoid the use of wildcard characters.
      • Join data over subqueries.
    • API Optimizations:
      • Limit the depth of your API responses by picking and choosing what related objects are in a response.
      • Get related data in a single query instead of several queries in a loop (the dreaded N+1 problem).
      • Implement pagination for fetching data.
    ---

    API Optimization via Pagination

    Pagination speeds up requests by only fetching a subset of data. The subset of data is then rendered on a page, and the next subset of data is fetched when the next page is visited. Pagination often works by limiting the amount of data fetched by requesting a page size and offsetting where data starts based on the page size and current page.

    In this step, you will implement pagination when fetching posts. The paginated request is made from the Paginated Posts tab in the client interface. To check out the code changes you are going to make, go to the Paginated Posts tab. You will begin to see the Posts table paginate when you have created more than five posts.

    --- ## Test Code Changes Using Client Interface

    In the client interface, visit Paginated Posts. You will see a table that displays five posts on each page. If you look into the Developer Tools Network tab, you should see a new request for each page's data. The method you implemented in the task above correctly fetches the pages data.

  12. Challenge

    Conclusion

    Conclusion

    Congratulations on completing all of the steps of this lab! You now have the skills to integrate a PostgreSQL database with a NestJS application and interact with it using TypeORM entities, repositories, and migrations. You now have a better understanding of API optimizations and how to perform database transactions, including rolling back data in case of failures. This was no easy feat so always take the time you deserve to reward yourself. For more information on TypeORM, you can look at the TypeORM documentation.

Jaecee is an associate author at Pluralsight helping to develop Hands-On content. Jaecee's background in Software Development and Data Management and Analysis. Jaecee holds a graduate degree from the University of Utah in Computer Science. She works on new content here at Pluralsight and is constantly learning.

What's a lab?

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Provided environment for hands-on practice

We will provide the credentials and environment necessary for you to practice right within your browser.

Guided walkthrough

Follow along with the author’s guided walkthrough and build something new in your provided environment!

Did you know?

On average, you retain 75% more of your learning if you get time for practice.