js

How to Build High-Performance APIs with Fastify and TypeORM

Discover how Fastify and TypeORM combine speed and structure to build scalable, type-safe APIs with minimal overhead.

How to Build High-Performance APIs with Fastify and TypeORM

I was building an API that needed to handle thousands of requests per second. Every millisecond counted, but I also needed a clean, reliable way to talk to my database. That’s when I looked at Fastify and TypeORM. Fastify promised speed I could feel, and TypeORM offered the structure my team needed. Combining them felt like the obvious next step for modern backend development.

Why does this pairing work so well? Fastify is built for speed. It has one of the lowest overheads of any Node.js framework. TypeORM brings order and type safety to database operations. Together, they let you build applications that are both fast and easy to maintain. You get performance without sacrificing code quality.

Let’s start by setting up a basic project. First, you’ll need to install the core packages.

npm install fastify @fastify/typeorm typeorm reflect-metadata pg

Notice @fastify/typeorm. This is the official plugin that bridges the two worlds. It manages the database connection lifecycle for you, tying it to your Fastify server. The pg package is for PostgreSQL; swap it for mysql2 or sqlite3 if needed.

Now, let’s define a simple entity. This is where TypeORM shines. An entity is a class that maps to a database table.

// entity/user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

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

  @Column()
  name: string;

  @Column({ unique: true })
  email: string;
}

This code does more than define fields. It creates a blueprint. TypeORM uses decorators like @Entity() and @Column() to understand how to build your SQL tables. It turns a class into a powerful data model.

How do you connect this entity to a running Fastify server? The plugin makes it straightforward. You register it and pass your connection details.

// app.ts
import fastify from 'fastify';
import { fastifyTypeorm } from '@fastify/typeorm';
import { User } from './entity/user.entity';

const app = fastify({ logger: true });

// Register the TypeORM plugin
app.register(fastifyTypeorm, {
  type: 'postgres',
  host: 'localhost',
  port: 5432,
  username: 'test',
  password: 'test',
  database: 'test',
  entities: [User],
  synchronize: true, // Use only in development
});

// A simple route to create a user
app.post('/user', async (request, reply) => {
  const userRepo = app.orm.getRepository(User);
  const newUser = userRepo.create(request.body as Partial<User>);
  const result = await userRepo.save(newUser);
  return reply.send(result);
});

app.listen({ port: 3000 }, (err) => {
  if (err) {
    app.log.error(err);
    process.exit(1);
  }
});

After registering the plugin, the database connection is available on app.orm. You can get a repository for any entity and start querying immediately. The synchronize: true option automatically creates tables from your entities. It’s great for prototyping, but dangerous for production. Can you guess why?

For production, you should use migrations. They are version-controlled scripts that change your database schema safely. TypeORM has a built-in CLI to generate and run them. This is how you keep your database changes predictable and reversible.

Let’s look at a more complete route. What if you need to fetch a user and all their related posts? TypeORM’s relations make this simple.

// entity/post.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.entity';

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

  @Column()
  title: string;

  @Column('text')
  content: string;

  @ManyToOne(() => User, (user) => user.posts)
  author: User;
}

First, we add a posts property to our User entity.

// Updated user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import { Post } from './post.entity';

@Entity()
export class User {
  // ... id, name, email columns ...

  @OneToMany(() => Post, (post) => post.author)
  posts: Post[];
}

Now, you can write a route that fetches this relationship efficiently.

app.get('/user/:id/posts', async (request, reply) => {
  const userId = parseInt((request.params as any).id, 10);
  const userRepo = app.orm.getRepository(User);

  const userWithPosts = await userRepo.findOne({
    where: { id: userId },
    relations: ['posts'], // This tells TypeORM to join the posts data
  });

  if (!userWithPosts) {
    return reply.code(404).send({ message: 'User not found' });
  }

  return reply.send(userWithPosts.posts);
});

The relations option is key. It performs a JOIN operation in SQL, loading the related posts in a single query. This avoids the “N+1 query problem,” where you make one query for the user and then an additional query for each post. Performance matters, especially at scale.

But what about errors? Fastify has excellent error handling. You can combine it with TypeORM’s transactional integrity. Imagine you need to create a user and a default post in one operation. Both should succeed or fail together.

app.post('/user-with-post', async (request, reply) => {
  const { name, email, postTitle } = request.body as any;
  const manager = app.orm.manager;

  await manager.transaction(async (transactionalEntityManager) => {
    const userRepo = transactionalEntityManager.getRepository(User);
    const postRepo = transactionalEntityManager.getRepository(Post);

    const newUser = userRepo.create({ name, email });
    const savedUser = await userRepo.save(newUser);

    const newPost = postRepo.create({
      title: postTitle,
      content: 'Welcome post!',
      author: savedUser,
    });
    await postRepo.save(newPost);
  });

  return reply.code(201).send({ message: 'User and post created' });
});

The manager.transaction method wraps everything in a database transaction. If any part fails, all changes are rolled back. This keeps your data consistent. It’s a crucial tool for reliable applications.

You might wonder about complex queries. TypeORM’s QueryBuilder gives you fine-grained control. Need to find active users who posted last week?

const activePosters = await app.orm
  .getRepository(User)
  .createQueryBuilder('user')
  .innerJoinAndSelect('user.posts', 'post')
  .where('post.createdAt > :date', { date: '2023-10-20' })
  .getMany();

This generates efficient SQL while keeping your code readable. You work with objects and properties, not string-based queries.

The journey from a simple idea to a full-featured API is smoother with these tools. Fastify handles the web layer with incredible speed. TypeORM manages your data with structure and safety. The plugin connects them cleanly, managing connections and providing access where you need it.

I’ve found this setup perfect for microservices and APIs where response time is critical. The development experience is also a joy. TypeScript catches errors early, and the code is self-documenting through entities and types.

Give this integration a try in your next project. Start with a single entity and a POST route. Feel the speed of Fastify and the clarity of TypeORM. Experiment with relations and transactions. I think you’ll be impressed with what you can build.

If you found this walkthrough helpful, please share it with a fellow developer. Have you tried combining Fastify with another ORM? What was your experience? Let me know in the comments below.


As a best-selling author, I invite you to explore my books on Amazon. Don’t forget to follow me on Medium and show your support. Thank you! Your support means the world!


101 Books

101 Books is an AI-driven publishing company co-founded by author Aarav Joshi. By leveraging advanced AI technology, we keep our publishing costs incredibly low—some books are priced as low as $4—making quality knowledge accessible to everyone.

Check out our book Golang Clean Code available on Amazon.

Stay tuned for updates and exciting news. When shopping for books, search for Aarav Joshi to find more of our titles. Use the provided link to enjoy special discounts!


📘 Checkout my latest ebook for free on my channel!
Be sure to like, share, comment, and subscribe to the channel!


Our Creations

Be sure to check out our creations:

Investor Central | Investor Central Spanish | Investor Central German | Smart Living | Epochs & Echoes | Puzzling Mysteries | Hindutva | Elite Dev | JS Schools


We are on Medium

Tech Koala Insights | Epochs & Echoes World | Investor Central Medium | Puzzling Mysteries Medium | Science & Epochs Medium | Modern Hindutva

Keywords: fastify, typeorm, nodejs, api development, typescript



Similar Posts
Blog Image
Complete Guide to Integrating Next.js with Prisma ORM for Type-Safe Full-Stack Development

Learn how to integrate Next.js with Prisma ORM for type-safe, scalable full-stack apps. Build modern web applications with seamless database operations.

Blog Image
How to Build Fully Typed Web Apps with Remix and Drizzle ORM

Discover how Remix and Drizzle ORM create a type-safe full-stack workflow from database to UI. Build faster with fewer bugs.

Blog Image
Build Event-Driven Systems with EventStoreDB, Node.js & Event Sourcing: Complete Guide

Learn to build robust distributed event-driven systems using EventStore, Node.js & Event Sourcing. Master CQRS, aggregates, projections & sagas with hands-on examples.

Blog Image
Complete Guide to Integrating Next.js with Prisma ORM for Type-Safe Full-Stack Development

Build full-stack TypeScript apps with Next.js and Prisma ORM. Learn seamless integration, type-safe database operations, and API routes for scalable web development.

Blog Image
Build Type-Safe REST APIs with Fastify, Zod, and Prisma: Complete TypeScript Guide

Learn to build production-ready REST APIs with Fastify, Zod & Prisma. Complete TypeScript guide with validation, testing & advanced features.

Blog Image
Build Type-Safe Event-Driven Architecture with TypeScript Node.js and Redis Streams

Learn to build type-safe event-driven architecture with TypeScript, Node.js & Redis Streams. Includes event sourcing, error handling & monitoring best practices.