Building a Scalable Node.js SaaS Backend: Architecture and Trade-offs
#Backend

Building a Scalable Node.js SaaS Backend: Architecture and Trade-offs

Backend Reporter
5 min read

A production-ready Node.js backend structure for SaaS applications using TypeScript, PostgreSQL, and layered architecture to separate business logic from infrastructure concerns.

When building SaaS applications, developers often find themselves recreating the same backend foundation repeatedly. Authentication, billing integration, database models, email notifications, and API documentation become recurring tasks that slow down development and introduce inconsistencies across projects.

I've been through this cycle enough times to recognize the need for a structured starting point. The goal wasn't to create another boilerplate, but to establish an architecture that separates business logic from infrastructure concerns while remaining flexible enough for different SaaS requirements.

Tech Stack Selection

The foundation uses Node.js with TypeScript for type safety and maintainability. Express serves as the HTTP framework due to its simplicity and extensive middleware ecosystem. PostgreSQL provides relational data integrity for user accounts, subscriptions, and business data.

For the ORM, Drizzle offers type-safe SQL queries and schema definitions that integrate well with TypeScript. Stripe handles billing and subscription management, while Resend manages transactional emails. Zod provides runtime validation for incoming requests, and OpenAPI/Swagger generates API documentation automatically.

This combination balances developer experience with production readiness. Each tool addresses specific concerns without overcomplicating the architecture.

Layered Architecture

Featured image

The folder structure follows a layered approach where each layer has distinct responsibilities:

API Layer - Express routes and controllers handle HTTP requests and responses. This layer focuses on request/response formatting, validation, and HTTP-specific concerns like status codes and headers.

Application Layer - Business logic modules orchestrate operations, apply policies, and coordinate between different parts of the system. This is where domain rules live.

Infrastructure Layer - Database adapters, external API clients, and service integrations handle data persistence and third-party communications. These adapters can be swapped without affecting business logic.

Shared Layer - Common utilities, types, and configurations used across the application.

This separation means you can replace PostgreSQL with MongoDB, switch from Stripe to Chargebee, or change the HTTP framework without rewriting business logic. The trade-off is slightly more initial complexity, but the long-term maintainability gains are significant.

API Documentation Strategy

OpenAPI documentation serves multiple purposes beyond just API reference. During development, Swagger UI provides an interactive way to test endpoints with real request schemas. This immediate feedback loop speeds up frontend integration and API testing.

The documentation stays synchronized with the code because it's generated from route definitions and validation schemas. No separate documentation maintenance is required, eliminating the common problem of outdated API docs.

Request Flow Pattern

A typical API request follows this path:

  1. Express route receives the HTTP request
  2. Middleware stack applies rate limiting, authentication, and request parsing
  3. Controller validates the request using Zod schemas
  4. Application module applies business rules and policies
  5. Infrastructure adapters interact with the database or external APIs
  6. Response is formatted and returned to the client

This flow keeps each concern isolated. Rate limiting logic doesn't mix with business rules. Authentication middleware doesn't know about database schemas. The controller focuses on HTTP concerns while the module handles domain logic.

Built-in SaaS Components

The backend includes several components that most SaaS applications need:

Authentication System - User registration, login, password reset, and session management with secure token handling. The system supports multi-factor authentication and account verification workflows.

Billing Integration - Stripe integration with webhook handling for subscription events, payment failures, and trial expirations. The system tracks subscription status and enforces access controls based on billing tier.

Database Setup - PostgreSQL schema with migrations for users, subscriptions, and common SaaS entities. Connection pooling and transaction management are configured for production workloads.

Transactional Email - Resend integration for welcome emails, password resets, billing notifications, and account alerts. Email templates are structured for easy customization.

API Documentation - OpenAPI specification with Swagger UI for endpoint exploration and testing. Request/response schemas are automatically generated from TypeScript types.

Error Handling - Structured error responses with consistent formatting, proper HTTP status codes, and error logging. Different error types are handled appropriately.

Security Middleware - Rate limiting to prevent abuse, CORS configuration for frontend integration, and security headers for production deployment.

Testing Infrastructure - End-to-end tests that verify the complete request flow, including database operations and external API interactions.

These components represent the foundation that most SaaS projects build anyway. Having them pre-integrated with consistent patterns saves significant development time.

Trade-offs and Considerations

Layered architectures introduce some complexity. There's a learning curve for developers unfamiliar with the pattern, and initial setup takes longer than a simple monolithic approach. However, the benefits become apparent as the application grows.

For smaller projects or prototypes, this architecture might be overkill. A simpler structure with less separation could be more appropriate when speed of development is the priority. The key is matching the architecture to the project's scale and longevity expectations.

Database choice matters significantly. PostgreSQL works well for structured SaaS data with relationships and transactions. If your application needs more flexibility or horizontal scaling, a document database like MongoDB might be better. The layered approach makes such changes possible, though not trivial.

Production Readiness

Build seamlessly, securely, and flexibly with MongoDB Atlas. Try free.

The architecture includes production considerations from the start. Connection pooling prevents database connection exhaustion under load. Rate limiting protects against abuse and DoS attacks. Structured logging enables monitoring and debugging. Error handling prevents sensitive information leakage.

Health checks and readiness probes ensure the application only serves traffic when all dependencies are available. This is crucial for containerized deployments and load balancer configurations.

Extending the Architecture

Adding new features typically involves creating new modules in the application layer and corresponding adapters in the infrastructure layer. For example, adding a notification system would involve:

  1. Creating a notification module with business logic for when and how to notify users
  2. Building adapters for different notification channels (email, SMS, in-app)
  3. Adding API endpoints for notification preferences
  4. Updating authentication and authorization policies

Existing layers remain unchanged, and the new functionality integrates through well-defined interfaces.

Community Perspective

I'm curious how other developers approach SaaS backend architecture. Some prefer simpler, monolithic structures that prioritize rapid development. Others use more sophisticated patterns like Domain-Driven Design or event-driven architectures.

The right choice depends on team size, project complexity, expected growth, and development speed requirements. A solo developer building a simple tool might prefer minimal architecture, while a team building a complex platform benefits from clear separation of concerns.

What's your experience with Node.js SaaS backends? Do you find layered architectures helpful, or do you prefer simpler approaches? The trade-offs between structure and simplicity continue to evolve as tools and best practices mature.

pic

Comments

Loading comments...