Skip to main content

Learn Typeorm in 10 Minutes

·12 mins
Learn Typeorm in 10 Minutes

Using any ORM is a fantastic skill to add your resume but they are also a really handy tool which can help speed-up your development process.

In this tutorial I’m going to get you up and running with TypeORM and show you how you can create a CRUD API that you can use in your own apps and projects.

Watch the video tutorial here.

The first thing we need to do is get TypeORM installed in your project.

Install TypeORM #

TypeORM is basically just an NPM package so you’re obviously going to need to have both Node and NPM installed to use it.

If you’ve got those pre-requisites it’s simply a case of installing TypeORM into your current project.

npm install typeorm --save

You also need to install a database driver for TypeORM to use.

This is just a case of choosing the database that you’re going to be using with TypeORM and then installing the relevant NPM package.

We’re going to use SQLite for this tutorial but other options include PostGres, MySQL and MongoDB.

npm install sqlite3 --save

There’s one final step before we move on to connecting to the database and that is to configure the typescript environment by setting emitDecoratorMetadata and experimentalDecorators both to true in your tsconfig.json file.

"emitDecoratorMetadata": true,
"experimentalDecorators": true,

Now we’re setup and ready to connect to our database using TypeORM.

## Make a connection

To make a connection, let’s first create a new file to hold all the details needed to connect.

This is known in TypeORM as a datasource.

import { DataSource } from "typeorm";

export const AppDataSource = new DataSource({
    type: "sqlite",
    database: "my-blog.db",
    synchronize: true,
    logging: true,
    entities: [],
    subscribers: [],
    migrations: [],
});

You can see that we import the DataSource class from the TypeORM package and then create a new instance of this, specifying the type of database we’re using (SQLite) and any additional configuration that is needed to connect.

If we were using a different type of database like PostGres or MySQL, we would also need to provide authorization details but these aren’t needed for SQLite so we just don’t include them.

(Here’s an example of a PostGres datasource for comparison)

// DataSource.ts
import { DataSource } from "typeorm";

export const AppDataSource = new DataSource({
    type: "postgres",
    host: "localhost",
    port: 5432,
    username: "test",
    password: "test",
    database: "test",
    synchronize: true,
    logging: true,
    entities: [],
    subscribers: [],
    migrations: [],
})

To actually make a connection to the database we need to call a function on the DataSource we have created and It’s a good idea to do this in our main app code where we’ll be writing our routes.

So in our app.ts file we’ve got a basic Express app setup and we’re going to wrap our database connection in the TypeORM initialize function.

// app.ts
import express, { Request, Response } from 'express';
import cors from 'cors';
import { AppDataSource } from './DataSource';

const app = express();
app.use(express.json());
app.use(cors());

AppDataSource.initialize()
    .then(() => {
        // Successfully conencted to the database so let's start the Express app

        app.listen(3000, () => {
            console.log('App listening on port 3000');
        });

    })
     .catch(error => {
        console.log(error);
    });

So we import the DataSource we created in the first step and then call it’s initialize function which connects to the database and returns a Promise.

If successful, we can then continue to set our Express app to serve but if there’s a problem, like the username and password isn’t correct for example, we catch any errors returned from the Promise.

So now we’re connected we can define what data we’re going to be using with TypeORM by defining data models.

Defining a model #

TypeORM uses the term ‘Entity’ to refer to data models and it provides a TypeScript decorator which allows you to convert a class to one of these entities to use within your app.

We’ll create this in a separate file BlogPost.ts.

// BlogPost.ts
import { PrimaryGeneratedColumn, Column, Entity } from "typeorm"
 
@Entity()
export class BlogPost {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    title: string

    @Column()
    text: string
}

You can see that we use the Entity decorator to let TypeORM that this is one of our data models and we can define properties on the objects we’ll be using with the Column decorator.

There are few different ways in TypeORM of creating primary keys for our models but here we’ve just used the PrimaryGeneratedColumn decorator which will create an auto-incrementing number for this field for each object we create like 1, 2, 3, 4 and so on but you can use other things like guids as well.

To use the new data model you’ll need to ensure it is listed in the entities section of your app’s DataSource.

// DataSource.ts
import { DataSource } from "typeorm";
import { BlogPost } from './BlogPost';

export const AppDataSource = new DataSource({
    type: "sqlite",
    database: "my-blog.db",
    synchronize: true,
    logging: true,
    entities: [BlogPost], // All your data models need to be listed here
    subscribers: [],
    migrations: [],
});

There are a couple of different ways to work with your data model using TypeORM but the easiest and most flexible way is to setup a repository for it in your app code.

We can do this by calling the getRepository function on the data source we have created passing in the blog post data model class.

// app.ts
import { BlogPost } from './Blogpost';

// ...

AppDataSource.initialize()
    .then(() => {
        // Successfully conencted to the database so let's start the Express app

        app.listen(3000, () => {
            console.log('App listening on port 3000');

            // Create a repository for working with our model
            const blogPostRepository = AppDataSource.getRepository(BlogPost);

        });

    })
     .catch(error => {
        console.log(error);
    });

We’ll use this blogPostRepository variable to work with the Blog Post data model in TypeORM.

Now we’ve got a basic data model setup, we can use TypeORM to create some data and store it in our SQLite database.

Create some data #

You can create new objects based on the data models you define and set the properties of those objects as you like and when you’re ready, you can store the entity in the database using the repository we just setup.

import { BlogPost } from './Blogpost';

const newBlogPost = new BlogPost();
newBlogPost.title = 'My First Post!';
newBlogPost.text = 'This is my first ever blog post with TypeORM!';
const result = await blogPostRepository.save(newBlogPost);

Of course setting all the properties like this would be a bit time consuming especially if you had lots of things to set!

You can apply complete objects to the data model rather than setting properties individually.

Here’s an example of creating a new blog post in our example Express app.

 app.post('/blogPost', async (req: Request, res: Response) => {
    const data = req.body;

    const result = await blogPostRepository.save(data);

    return res.json({
        status: 'OK',
        data: result
    })
});

Of course there’s no validation or sanitising of the data here (which is extremely dangerous!) but hopefully you can see how we can take the data as an object from our front-end app and then use the TypeORM repository to store it.

Now we’ve stored something in our database, let’s take a look at how we can retrieve items too.

Read some data #

The first thing you might want to do is get a list of all the blog posts that have been stored in the database.

For that we can use the find function.

app.get('/blogPost', async (req: Request, res: Response) => {
    const allBlogPosts = await blogPostRepository.find();

    return res.json({
        status: 'OK',
        data: allBlogPosts
    })
});

Here we’re just returning all the blog posts that have been stored in the database which might be useful for our blog app’s home page.

You might want to limit or page these results on different parts of your blog and you can do that by providing values to the skip and take options with the find function.

app.get('/blogPost', async (req: Request, res: Response) => {
    const offset = req.query.offset;
    const amount = req.query.amount;

    const allBlogPosts = await blogPostRepository.find({ skip: offset, take: amount });

    return res.json({
        status: 'OK',
        data: allBlogPosts
    })
});

If we were then to send a request to this endpoint in our Express app with offset and amount query parameters we could control the amount of results returned by the find function.

The other thing we might want to do is get a specific blog post for our app, like when a user clicks through to read an article.

We can retrieve something specific from our database with TypeORM using the findBy or findOneBy functions.

app.get('/blogPost/:id', async (req: Request, res: Response) => {
    const id = parseInt(req.params.id);
    const blogPost = await blogPostRepository.findOneBy({ id });

    return res.json({
        status: 'OK',
        data: blogPost
    })
});

In our Express route, we’re specifiying the ID of the blog post we want to find and then we pass this value to the findByOne function to get that one particular blog post.

If you’re an admin of our blog app you’ll probably want to make changes to your posts so let’s have a look at how to update data stored in our database.

Update some data #

Updating is a two stage process - you first need to find the right resource in the database, then you can modify it before saving it back to the database.

app.patch('/blogPost/:id', async (req: Request, res: Response) => {
    const changedText = req.body.text;
    const id = parseInt(req.params.id);

    const blogPost = await blogPostRepository.findOneBy({ id });
    blogPost.text = changedText;

    const result = await blogPostRepository.save(blogPost);

    return res.json({
        status: 'OK',
        data: result
    });
});

In this code we’re getting the value of the changed blog post text from the request body and identifying the id of the blog post using the path parameter in the Express route.

We can then find the blog post resource using the findOneBy function and change it’s text property.

The repository can then be used to store the updated blog post.

Let’s take a look at the final type of interaction you might want to use in your apps, removing resources from the database.

Delete some data #

To remove items from the database, there’s a delete on the repository object we can use.

 app.delete('/blogPost/:id', async (req: Request, res: Response) => {
    const id = parseInt(req.params.id);

    const result = await blogPostRepository.delete({ id });

    return res.json({
        status: 'OK',
        data: result,
    })
});

You’ll probably want to consider sanitising and validating the resource the request is trying to delete but you can see it’s just a simple case of calling the delete function with some way of identifying the resource in the same way the find functions work.

So that’s the basic CRUD operations you would expect to do with resources stored in your database however most apps aren’t quite that simple.

## Using Relationships (multiple data models that are linked)

In all but the most simplest of apps, it’s likely you’ll have data models that refer to other data models.

For example, in our really simple blog post app, we might want to have an Author data model to link the posts that are being written to a specific person.

import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"

@Entity()
export class Author {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    name: string
}

We want to connect this Author entity to each blog post that is created so we know who wrote it.

To do this, we want to create a relationship between the two data models we now have.

In the case of the authors on the site, they can write many blogs posts whereas a blog post can only have one author.

We can setup these relationships with more TypeORM decorators.

import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"
import { BlogPost } from "./BlogPost"

@Entity()
export class Author {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    name: string

    @OneToMany(() => BlogPost, (blogPost) => blogPost.author)  // How the blog posts are related to the author
    blogPosts: BlogPost[]
}

So here we’ve added the OneToMany decorator to the blogPosts property on the Author data model.

This will add a new property to the data model and will fetch all of the blog posts this author has written when requesting an Author entity BUT only if we tell TypeORM to - i’ll explain more about this in a moment.

The BlogPost data model needs to be updated too in order to setup the other side of the relationship.

import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"
import { Author } from "./Author"

@Entity()
export class BlogPost {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    title: string

    @Column()
    text: string

    @ManyToOne(() => Author, (author) => author.blogPosts) // How the author relates to this blog post
    author: Author
}

This time we’ve used the ManyToOne decorator to show that an author can write many blog posts and these can be found on the blogPosts property of an Author entity.

Now when we create a new blog post, we’ll want to make sure we’re setting the author of who wrote the post.

app.post('/blogPost', async (req: Request, res: Response) => {
    const data = req.body;
    const authorId = data.author;
    const blogContent = data.text;

    // Find the author from their ID
    const author = await authorRepository.findOneBy({ id: authorId });

    const newBlogPost = new BlogPost();
    newBlogPost.text = blogContent;
    newBlogPost.author = author;

    const result = await blogPostRepository.save(data);

    return res.json({
        status: 'OK',
        data: result
    })
});

Updating the POST route in our Express app, we now need to find the author who wrote the post (which we’re doing in this example by passing in the authorId within the request body from the front end app).

Once we’ve found the author, we can construct a new BlogPost entity, setting the author property to the found author, and save this to the database.

If you try and fetch blog posts now, they’ll look pretty much the same.

So if we want to get the author data returned to us when we fetch our blog posts, we need to let TypeORM know that we want to include related data in the result.

app.get('/blogPost', async (req: Request, res: Response) => {
    const blogPosts = await blogPostRepository.find({ relations: { author: true } });

    return res.json({
        status: 'OK',
        data: blogPosts
    });
});

By passing in an options object to one of the find functions, we can specify which related entities we want to return in the result.

Of course, we can do this the other way round and include the blogPosts entities when fetching author data.

Check out the code #

There’s lots of other things you can do in addition to what we’ve covered including building your own queries using the built-in QueryBuilder functions to allow you to customise the way data is stored and retrieved in your database.

If you’re still not 100% sure on how you might use TypeORM in your projects, check out this next article where we’ll build a complete app using TypeORM and it will be a great project for your portfolio or just something to further your skills with using ORMs.