- Lab
- Core Tech

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.

Path Info
Table of Contents
-
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. -
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 operationsIt 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
, usingmyuser
. TheTypeOrmModule
has a method, calledforRoot
, 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 themyuser
user at port5432
and hostlocalhost
. 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
andComments
modules. Complete the tasks below to configure the NestJS application to use TypeORM with PostgreSQL.## Use Environmental Variables to Configure TypeORM to Use PostgreSQLDetails 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.
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
. -
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. APost
will have the following columns:id
:number
content
:string
title
:string
A file for the
Post
entity has been provided atserver/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. -
Challenge
Design and Implement Comment Entity
Design and Implement Comment Entity
In this step, you will be defining and implementing the
Comment
entity. AComment
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. -
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 manyComments
, but aComment
can only be associated with onePost
. 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. -
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 thepsql
command interface.You want to create the
Post
andComment
entities from the previous step as database tables with the correct table columns using migrations. To do this, you will use thetypeorm-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 atmigrations-directory/<Migration Id>-migration-name
. SQL in your migrations directory will be ran whenever the migration has not been ran or is in adown
state.Migration Up and Down States
You may notice that migrations have an
up
anddown
state with related methods. Theup
state runs when the migration is applied, and thedown
state runs when the migration is reversed.Every generated or created migration should include an
up
anddown
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. - Imports the
-
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
andCommentsService
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
andCommentsController
and are calling thePostsService
andCommentsService
to handle interactions with the database.At this time, all methods in the
PostsService
andCommentsService
are throwingNotImplementedException
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>
, thestart: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.
-
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
- Encapsulation of Data Access Logic:
- It hides the complexity of data queries and CRUD operations from the rest of the application.
- Separation of Concerns:
- Business logic is decoupled from data access, improving maintainability.
- Testability:
- It facilitates unit testing by allowing the repository to be mocked or replaced.
- 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. TheRepository
class is often injected into an feature's service and you have to inject aRepository
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 parameterFindOptions
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
- Accepts
Async Repository.findOne()
- Accepts
FindOptions
for the first parameter - Finds the first entity that matches given
FindOptions
- Returns an entity
- Accepts
Async Repository.delete()
- Deletes entities by entity id, ids, or given conditions in the first parameter
- Returns a
DeleteResult
object that contains anaffected
key - When the value of
affected
is0
, nothing is deleted - When the value of
affected
is1
, something is deleted - Executes a single query to perform a deletion, unlike calling
remove
on an entity and thensave
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)
- Finds entities that match given
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.
-
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 InterfaceYou should now be able to return to your client interface and create, update, and delete comments.
-
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 aQueryRunner
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 thePostsService
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 yourPostsService
. After that, theDataSource
instance can create aQueryRunner
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'sDataSource
is available in all features. ## Test Code Changes Using Client InterfaceIf 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).
-
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.
- Database Optimizations:
-
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.
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.