Mahabub Alam Arafat

Software Engineer - Blog

← Back to Portfolio

How I Made Our API 3-4x Faster with NestJS

How I Made Our API 3-4x Faster with NestJS

TL;DR: I rewrote ShareTrip's legacy booking API with NestJS and TypeScript. Result? 3-4x faster response times (from 20-30 seconds down to 8-9 seconds) and way less headaches. Here's how I did it and what I learned.

The Problem: Our API Was Slow (And Getting Slower)

Picture this: You're booking a flight, you hit "Search," and... you wait. And wait. Our legacy booking API was taking 20-30 seconds to respond on average. We were generating so many flight data options that the older API simply couldn't handle the search load efficiently.

The old system was a mess of:

  • JavaScript spaghetti code with no type safety
  • Database queries that would make your DBA cry
  • Zero caching (because "we'll add that later")
  • Error handling that basically said "¯\_(ツ)_/¯"

> The Wake-Up Call: With response times averaging 20-30 seconds, users were abandoning bookings left and right. We were losing real money because people couldn't wait that long to see flight options.

Why I Chose NestJS (Spoiler: It Wasn't Just Hype)

Everyone was talking about NestJS, but I needed more than hype. Here's what sold me:

TypeScript Out of the Box

No more Cannot read property 'id' of undefined at 2 AM. TypeScript caught so many bugs before they hit production that I actually started sleeping better.

Structure That Makes Sense

The old codebase? Finding anything took 20 minutes. NestJS forced us into a clean structure:

src/
├── booking/          # Everything booking-related
│   ├── booking.controller.ts
│   ├── booking.service.ts
│   └── booking.module.ts
├── payment/          # Payment stuff here
└── shared/           # Common utilities

The Migration: What Actually Happened

Week 1-2: Setting Up the Foundation

I started with the booking module. Basic CRUD operations, proper validation, and error handling that doesn't just throw generic 500 errors.

// Before: Pray this works
app.post('/booking', (req, res) => {
  // Hope req.body has what we need
  const booking = createBooking(req.body);
  res.json(booking);
});

// After: Know this works
@Post()
async createBooking(@Body() createBookingDto: CreateBookingDto) {
  return this.bookingService.create(createBookingDto);
}

Week 3-4: The Database Nightmare

Our old queries were... creative. I found a query that was doing 12 separate database calls to get data that could be fetched in one.

Week 5-6: Adding the Magic (Caching)

This is where things got interesting. I added Redis caching for frequently accessed data like airports, routes, cities, and discounts:

// Cache frequently accessed reference data
async getAirports() {
  const cacheKey = 'airports-list';
  const cached = await this.redis.get(cacheKey);
  if (cached) return JSON.parse(cached);
  
  const airports = await this.airportService.getAll();
  await this.redis.setex(cacheKey, 3600, JSON.stringify(airports)); // 1 hour cache
  return airports;
}

// Cache routes, cities, discounts similarly
async getPopularFlights() {
  const cacheKey = 'popular-flights';
  const cached = await this.redis.get(cacheKey);
  if (cached) return JSON.parse(cached);
  
  const flights = await this.flightService.getPopular();
  await this.redis.setex(cacheKey, 600, JSON.stringify(flights)); // 10 minutes
  return flights;
}

The Results (The Good Stuff)

🚀 Performance Improvements:

  • Average response time: 20-30s → 8-9s (3-4x faster!)
  • Handled massive flight data generation that broke the old API
  • Database queries reduced by 60% through smart caching
  • Cached frequent queries (airports, routes, cities, discounts) for instant access
  • Zero downtime during migration

But Wait, There's More

  • Bug reports dropped by 40% - TypeScript caught issues before users did
  • New feature development got 2x faster - Clean architecture pays off
  • Team happiness increased - No more debugging mysterious crashes at 3 AM

What I Wish I Knew Before Starting

Start Small, Think Big

I wanted to rewrite everything at once. Bad idea. Start with one module, get it right, then expand. Your future self will thank you.

Caching Is Your Friend (But Don't Go Crazy)

Cache the right things. User sessions? Yes. The current time? Probably not. I learned this the hard way when our "current promotions" were showing last week's deals.

The Unexpected Bonus

The best part wasn't the performance improvement. It was coming to work and not being afraid of the codebase. When new requirements came in, instead of thinking "Oh no, how do I hack this in?" I thought "Cool, where does this fit in our architecture?"

Should You Do This Too?

If you're dealing with:

  • Slow APIs that are getting slower
  • Code that nobody wants to touch
  • Bugs that keep coming back
  • Database queries that make you question your life choices

Then yes, consider NestJS. But remember:

  • Start small and prove the concept
  • Get your team on board (TypeScript has a learning curve)
  • Plan for migration time - it's not a weekend project
  • Measure everything so you can prove the improvements

What's Next?

Now that our booking API is solid, I'm working on the payment system. Same approach: NestJS, TypeScript, proper caching, and actual error handling. The goal? Cut payment processing time in half.

I'll write about that journey too - including the mistakes I'm definitely going to make along the way.


Questions about this migration? Want to know more about specific parts? Hit me up on LinkedIn - I love talking about this stuff.

About the Author

Mahabub Alam Arafat is a Software Engineer at ShareTrip with 2+ years of production experience. He specializes in backend development, API optimization, and turning legacy systems into modern, maintainable code.

Get in touch | LinkedIn | GitHub