The majority of NestJS tutorials available online tend to stop at basic CRUD operations or simple TODO list applications. Let's move beyond these beginner examples and build something real and practical that you can actually use in production.
What We're Building
We're building a Personal Finance API from scratch. This series will guide you through creating a production-ready REST API to manage income, expenses,and accounts types—everything you need to track your money with confidence.
Each post is a 3–4 minute read focusing on one specific aspect of the application. By the end, you'll have a fully functional API that you can extend or integrate with any frontend.
Architecture Overview
Our API will follow a modular, scalable architecture:
NestJS as our backend framework (TypeScript, dependency injection, decorators)
Supabase for PostgreSQL database hosting and authentication
Drizzle ORM for type-safe database queries and migrations
REST architecture with clear resource modeling
The application will be organized into feature modules:
auth: User authentication and authorizationaccounts: account types: cash, debit, and credittransactions: Income and expense trackingcategories: Transaction categorization
We'll use semantic versioning throughout this series. For example, by the end of this post we'll have v0.1.0. Minor changes like adding a constraint will bump to v0.1.1, and so on until we reach v1.0.0—a production-ready API.
This approach helps you break down large problems into manageable steps, making planning and execution much easier.
Setting Up the Project
Let's bootstrap our NestJS application:
# Install the Nest CLI globally
npm i -g @nestjs/cli
# Create a new project
nest new finance-api
# Choose your preferred package manager (npm, yarn, or pnpm)
The CLI will generate a clean project structure with:
TypeScript configuration
Basic module, controller, and service
Testing setup with Jest
ESLint and Prettier configurations
Project Structure
After setup, we'll organize our code following NestJS best practices:
src/
├── auth/ # Authentication module
├── accounts/ # Accounts module
├── transactions/ # Transactions module
├── categories/ # Categories module
├── common/ # Shared utilities, decorators, guards
│ ├── decorators/
│ ├── guards/
│ ├── interceptors/
│ └── filters/
├── config/ # Configuration module
├── database/ # Database connection and Drizzle setup
│ ├── migrations/
│ └── schema/
│ └── seeds/
├── app.module.ts
└── main.ts
Technical Decisions
Why Supabase?
Supabase provides:
Managed PostgreSQL database with automatic backups
Built-in authentication (email/password, OAuth, magic links)
Real-time subscriptions (optional for future features)
Auto-generated REST API (though we'll build our own)
Free tier perfect for development
Why Drizzle ORM?
Drizzle offers:
Full TypeScript type safety without decorators
Lightweight and performant (faster than TypeORM)
SQL-like syntax that's easy to learn
Migration system that's version-control friendly
Perfect integration with NestJS's modular architecture
Alternative considered: Prisma (great, but Drizzle gives us more control over queries and has better performance for complex relations).
We'll discuss each of these tools in detail in their own posts. For now, this is a high-level overview of the project we're building. At the end, you can use it as a guide and choose your preferred option.
Final thoughts
Here's what you now have:
✅ A clean NestJS project ready for development
✅ Understanding of the overall architecture
✅ A clear folder structure to keep code organized
✅ Knowledge of why we chose Supabase and Drizzle
I know this post is simple, but we're taking small steps to build gradually rather than rushing ahead. I hope this series serves as a guide for your future projects and gives you a framework for accomplishing those big goals.
Review the code so far at the link below:
🔗 Code: [GitHub repository]
💡 Next post: We'll set up Supabase
Top comments (0)