---
title: "Full-Stack TypeScript in 2025: Our Stack at HMD Developments"
description: "The complete TypeScript stack we use at HMD Developments in 2025 - from Next.js to infrastructure, and why we chose every piece."
date: 2025-07-06T00:00:00.000Z
category: Engineering
readingTime: "4 min read"
---


Every few months, someone asks me what stack HMD Developments uses. Here is the full picture as of mid-2025, every layer, every tool, every decision.

## The Philosophy

One language, everywhere. TypeScript runs our frontends, our backends, our infrastructure-as-code, our CLI tools, and our build scripts. Not because TypeScript is perfect for every use case, but because context-switching between languages has a real cognitive cost.

When a frontend developer can read the API code, and a backend developer can review the UI logic, the entire team moves faster. I wrote about the value of TypeScript specifically in [Why TypeScript Is Worth the Investment](/blog/why-typescript-is-worth-it) - this post is about the broader ecosystem.

## Frontend: Next.js 15 + React 19

Next.js is our default for every web application. React Server Components fundamentally changed our architecture - most of our components are server-only, with client components reserved for interactive elements.

**Why Next.js over alternatives:**
- **App Router** with RSC gives us server-first rendering without sacrificing interactivity
- **Vercel deployment** is zero-configuration for Next.js (we're Vercel partners for deployment)
- **File-based routing** reduces boilerplate
- **Built-in optimization** - images, fonts, scripts - all handled by the framework

For this personal site, I wrote about the specific performance patterns in [Next.js 15 on Vercel](/blog/nextjs-15-performance-patterns).

**UI library:** We use Material UI for most client projects. It provides a full component library that handles accessibility, theming, and responsive design out of the box. For custom projects where we need full design control, we write vanilla CSS with custom properties.

## Backend: Node.js + Fastify (or Next.js API Routes)

For simpler backends (CRUD APIs, webhook handlers), Next.js API routes or route handlers are sufficient. For complex backend services, we use Fastify.

**Why Fastify over Express:**
- Significantly faster (benchmarks consistently show 2-3x throughput)
- Built-in schema validation with JSON Schema
- Plugin system is cleaner than Express middleware
- TypeScript support is first-class

```typescript
import Fastify from 'fastify';

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

app.get<{ Params: { id: string } }>('/users/:id', async (request) => {
  const { id } = request.params;
  return getUserById(id);
});
```

The generic type parameter on the route handler gives us type-safe access to params, query, body, and headers. No casting, no runtime validation needed for shape - the types handle it.

## Database Layer: Prisma + PostgreSQL

Prisma is our ORM for any project that uses a relational database. The DX is unmatched:

```typescript
const user = await prisma.user.findUnique({
  where: { id: userId },
  include: { posts: true, profile: true },
});
// user is fully typed - includes posts and profile
```

The generated types from the Prisma schema mean that every database query returns a correctly-typed result. If we change the schema, TypeScript catches every affected query at compile time.

For MongoDB projects (like Chat Guard), we use the native MongoDB driver. Mongoose adds overhead we don't need when we're already using TypeScript for type safety.

## Authentication: NextAuth.js / Auth.js

Auth.js (formerly NextAuth.js) handles authentication for our web applications. It supports OAuth providers, credentials authentication, and session management with minimal configuration.

We use JWTs for stateless sessions in API-heavy applications and database sessions for traditional web applications. The choice depends on whether we need server-side session revocation.

## Infrastructure: Terraform + Pulumi

Our infrastructure-as-code uses Pulumi for new projects (TypeScript all the way down) and Terraform for existing projects we haven't migrated.

```typescript
// Pulumi - infrastructure in TypeScript
const bucket = new aws.s3.Bucket("uploads", {
  acl: "private",
  versioning: { enabled: true },
});
```

Writing infrastructure in the same language as application code means one less context switch. Engineers who write the application can review and modify the infrastructure. No separate HCL knowledge required.

## Testing: Vitest + Playwright

- **Unit/integration tests:** Vitest (fast, native TypeScript support, compatible with Jest's API)
- **E2E tests:** Playwright (multi-browser, excellent TypeScript support, parallel execution)

```typescript
// Vitest
test('calculateReadingTime returns correct estimate', () => {
  const content = 'word '.repeat(500);
  expect(calculateReadingTime(content)).toBe('2 min read');
});
```

We aim for high coverage on business logic and critical paths. We don't enforce coverage numbers - we enforce that tests exist for anything that could silently break in production.

## CI/CD: GitHub Actions + Vercel

GitHub Actions for CI (lint, test, build). Vercel for deployment. I wrote about our CI/CD patterns in detail in [CI/CD Pipelines That Don't Make You Cry](/blog/cicd-pipelines-that-work).

## Monitoring: Sentry + Vercel Analytics

Sentry captures errors and performance issues in real-time with full stack traces and session replay. Vercel Analytics provides Web Vitals and traffic data.

## The Full Stack at a Glance

| Layer | Technology |
|-------|-----------|
| Language | TypeScript (everywhere) |
| Frontend | Next.js 15, React 19, Material UI |
| Backend | Next.js Route Handlers, Fastify |
| Database | PostgreSQL + Prisma, MongoDB + native driver |
| Auth | Auth.js |
| Infrastructure | Pulumi, Terraform, AWS, GCP |
| Deployment | Vercel, AWS EKS |
| Testing | Vitest, Playwright |
| CI/CD | GitHub Actions |
| Monitoring | Sentry, Vercel Analytics |

## What's Not TypeScript

Two exceptions worth noting:

1. **iOS development** - Swift. No viable TypeScript-to-native iOS path that we'd trust for production. React Native is an option, but for apps where native performance matters (like [Snooze If You Can](/blog/swift-ios-alarmkit-journey)), Swift is the answer.

2. **ML/AI pipelines** - Python. The ecosystem (PyTorch, Hugging Face, LangChain) is irreplaceable. We use Python for model training and inference services, with TypeScript wrapping the API layer.

## The Bottom Line

A unified stack isn't about language purity - it's about reduced cognitive overhead. One language, one set of patterns, one type system across the entire organisation. The productivity compounds over time.

---

*Choosing a stack for a new project? Optimize for the team's existing knowledge first, ideal technology second. The best stack is the one your team can ship with.*
