DEV Community

Cover image for TypeORM viewEntity
Chris Bongers
Chris Bongers

Posted on • Originally published at daily-dev-tips.com

TypeORM viewEntity

We had a brief first-look at TypeORM, an excellent ORM system for managing your database.

I wrote about this first as I was transitioning to Prisma, this is not finished, but I found some fantastic elements of TypeORM I wanted to showcase in the meantime.

The TypeORM viewEntity

And for this article, that is something called a viewEntity.

In broad terms, TypeORM has entities that reflect database tables or connections.
But there is another type of entity we can leverage. The viewEntity can be used to map to a database view.

If you have never heard of database views, this is a view of a particular result of a database.

Let's take our initial TypeORM project as a starting point.

Note: You can find it here on GitHub.

We have something called a photo table in this project, which has all our photos.

These photos can be either published or not. We could also add a simple view entity that can only show the published photos.

That way, we always perform that specific query, instead of repeating ourselves with something like andWhere(photo.isPublished).

Creating the view entity

We write the view entity the same as a normal entity, but you can give them an exact name.

Let's use publishedPhoto for our one.

We have two ways of defining the actual view entity query, which can be either a raw SQL query like this:

@ViewEntity({
    expression: `
        SELECT "*"
        FROM "Photo"
        WHERE "isPublished" = true`
})
Enter fullscreen mode Exit fullscreen mode

Or via the query builder, which would look like this.

@ViewEntity({
    expression: (connection: Connection) =>
        connection
            .createQueryBuilder()
            .select('*')
            .from(Photo, 'photo')
            .where('photo.isPublished = true')
})
Enter fullscreen mode Exit fullscreen mode

I like the query builder a bit more, as it's cleaner in my eyes.

This is a very simple query, but you can enhance the query and even include multiple joins, a small example could be:

@ViewEntity({
    expression: (connection: Connection) =>
        connection
            .createQueryBuilder()
            .select('photo.*', 'user.firstName')
            .from(Photo, 'photo')
            .leftJoin(User, 'user', 'photo.userId = user.id')
            .where('post.isPublished = true'),
})
Enter fullscreen mode Exit fullscreen mode

Once the view entity is done, we still need to set up the returned columns.
These are called viewColumn, and we can add them in our class export like this:

export class PublishedPhoto {
  @ViewColumn()
  id: string;

  @ViewColumn()
  name: string;

  @ViewColumn()
  views: number;
}
Enter fullscreen mode Exit fullscreen mode

You can, of course, add whichever columns you'd like to show here.

Loading the viewEntity

To load the view entity, we have two options. The first is to create a new migration and manually add all the queries to replicate this view entity.

This would be the preferred way of keeping everything in sync.
However, since we haven't used migrations yet, let's use the sync command.

(But it can cause some odd syncs and is a bit more unpredictable for a production application)

npm run typeorm -- schema:sync
Enter fullscreen mode Exit fullscreen mode

Once it's done, you should see the view added to your database.

View entity visible

You can even see it shows only one result, while my database has two pictures.
One is published, and the other one is not.

Query the view entity

Let's first create an endpoint to retrieve all photos to see the difference.

Open up your index.ts file and add the route of the following photo.

fastify.get('/photos', async (request, reply) => {
  const photos = await connection.manager.find(Photo);
  return { photos };
});
Enter fullscreen mode Exit fullscreen mode

If we now run our application (npm run start) and visit the page, we should see all our photos.

TypeORM returning basic entity

Now we can swap out the Photo entity with our newly created view entity, which will use instead.

fastify.get('/photos', async (request, reply) => {
  const photos = await connection.manager.find(PublishedPhoto);
  return { photos };
});
Enter fullscreen mode Exit fullscreen mode

ViewEntity in action

As you can see, view entities can be super easy for specific combinations that you'd always have to do.
Think about published or deleted or even hidden items.

You can also find this in the GitHub repo.

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Top comments (0)