Skip to main content
← Back to projects Nadinha

Nadinha

React Node.js TypeScript PostgreSQL

Overview

Nadinha was built to solve a very specific problem in the Brazilian payroll loan niche. My wife worked at a small credit operation that handled dozens of clients simultaneously, each with different benefits, partner banks and linked public entities. All of this was tracked across spreadsheets, loose notes and memory, which led to duplicated work, lost history and inconsistent customer service.

Instead of trying to force-fit a generic CRM, I designed a custom system aligned with this domain. The app lets staff register clients, link benefits, partner banks and entities dynamically, and keep a complete client profile with a rich interaction history. The UI is focused on desktop usage in a small office, aimed at non-technical users who need something straightforward and reliable.

I worked as a solo full-stack engineer: I modeled the PostgreSQL database, implemented a TypeScript/Node.js/Express REST API with TypeORM, and built the front-end in React 17 with Redux + Redux-Saga and Styled-Components. On the infra side, I wired Docker, Redis, background queues, AWS S3/SES and Sentry to get a production-ready setup on a lean environment.

In production, the system managed a portfolio of roughly 100 clients. It centralized client data, benefits, banks and entities into a single source of truth and provided an auditable history per client, while allowing the business to adapt the configuration (banks, benefits, entities) without developer intervention.

Key Differentiator

The key differentiator is not “being a CRM”, but being a CRM shaped around the actual workflows of a small payroll-loan business, with engineering decisions optimized for stability under imperfect conditions (shared computers, unreliable connectivity, small team).

The data model was designed so that partner banks, benefits and entities are all manageable from the admin panel. Operators can evolve the catalog of financial products and institutions as the business changes, without schema changes or developer support. On the front-end, I invested in a robust authentication and state management flow (Redux-Saga, token refresh, encrypted persistence) to keep sessions stable even on shared office machines.

On the back-end, Redis is used both as a cache layer for relatively static reference data and as the backing store for Bull queues. This keeps read-heavy endpoints fast and offloads slow operations (like e-mails) to background workers, which is critical when the app is deployed on a modest server but still needs to feel responsive in daily use.

Architecture

  • Frontend (React 17 + TypeScript): Desktop-focused SPA using Redux for global state, Redux-Saga for side-effect orchestration, Styled-Components for styling and Axios for API communication.
  • Authentication & Session Layer: JWT-based authentication with access and refresh tokens, Axios interceptor for automatic token renewal, redux-persist + encrypted storage (localforage) and an AuthContext to periodically validate tokens.
  • Backend API (Node.js + Express): TypeScript REST API organized by business modules (users, clients, banks, benefits, entities), with clear separation between controllers, services and repositories.
  • Service Layer: Business services decoupled from persistence, wired via tsyringe dependency injection, handling rules like linking benefits and entities to clients and building their profile and history.
  • Repository Layer (TypeORM): One repository per domain aggregate, backed by PostgreSQL, with migrations to track schema evolution and keep environments in sync.
  • Database (PostgreSQL): Stores users, clients, partner banks, benefits, entities, notes/history, contact data and relations between those entities.
  • Cache & Queues (Redis + Bull): Redis for caching frequently accessed reference data with a configurable TTL, and as the backing store for Bull queues used for background jobs.
  • File Storage (AWS S3): Offloads documents and attachments related to clients (contracts, proofs) from the DB to S3, storing only metadata and links in PostgreSQL.
  • Email Delivery (AWS SES): Handles transactional notifications and follow-ups through queued jobs, keeping HTTP requests fast.
  • Observability (Sentry): Centralized backend error tracking in production to catch real-world issues and improve reliability incrementally.

Technical Highlights

  • Implemented a complete JWT auth flow with refresh tokens and an Axios response interceptor that transparently renews expired access tokens on 401 and retries the original request.
  • Configured Redux + Redux-Saga with encrypted redux-persist storage (localforage + encrypt transform) to safely persist session data on shared office machines.
  • Structured the backend into domain modules (users, clients, banks, benefits, entities) with a clean separation between controllers, services and TypeORM repositories, wired through tsyringe dependency injection.
  • Used Redis as a cache layer for read-heavy, low-churn reference data (banks, entities) with a 6-hour TTL, reducing PostgreSQL load and improving average response times.
  • Integrated Bull queues backed by Redis for asynchronous tasks such as email sending and recurring routines, preventing slow external I/O from impacting HTTP latency.
  • Built a custom integration between SunEditor (rich text editor) and Unform, exposing it as a form field that syncs HTML content via refs without unnecessary React re-renders.
  • Containerized the main services (API, PostgreSQL, Redis) with Docker, standardizing local development and simplifying deployment to a simple VPS environment using environment-based configuration.

Gallery