Skip to content

An Open Finance study with Pluggy

Wallet

An end-to-end personal project built to study Open Finance APIs: a bank account connected through Pluggy brings in transactions and investment positions without typing, and the backend takes on the work of reconciling the statement, classifying and summarizing.

Role
Backend and frontend
Period
2025
Status
Open source
Wallet screen consuming the API's real contract.

What the system does

  • Bank account connection over Open Finance, with consent handled inside the aggregator's widget.
  • Expenses and income from the connected account, alongside manual entries, in the same listing.
  • Statement reconciliation: credit card purchases, bill payments and transfers between the holder's own accounts handled before anything counts as spending.
  • Investment positions by type and subtype, with the issuing institution and the twelve-month rate.
  • A monthly summary and spreadsheet export, by month, by category and by year.

Architecture

  • A NestJS API with TypeORM over PostgreSQL, with a global prefix and Swagger documentation.
  • Seven modules in the backend, with the bank connection split into three controllers, connect token, bank item and transactions, plus the investment submodule.
  • Bank consent inside the aggregator's widget: the server exchanges client credentials for an API key and returns the connect token, and bank credentials are typed into the widget, never into the application.
  • Authentication by a Firebase token verified on the server, in a route guard. The same guard provisions the user on the first authenticated request, with no signup route.
  • A uniform response contract: an interceptor wraps every successful response and a global filter handles exceptions. On the way in, a ValidationPipe with a whitelist that refuses unknown fields.
  • Spending comes in through two paths and goes out through one: the listing merges connected-account transactions and manual entries into the same response, each tagged with its origin, and the monthly summary adds both.
  • Investments as their own domain: a position entity with twenty-eight subtypes and six types in enums, the institution as a separate entity, and an assembler that translates the aggregator's vocabulary at the response boundary.
  • The schema versioned across five migrations, with no automatic synchronization.
  • Structured logging with Pino, a health-check route and three spreadsheet exports with exceljs.
  • The web app in Next.js with Recharts charts, consuming the API directly with the Firebase token in the header.

Implementation details

  • Card reconciliation subtracts the bill payment and ten excluded categories, so a purchase is not counted twice.
  • Synchronization is on demand: there is no schedule and no webhook, the bank item is refreshed when the client asks.
  • Investment deduplication uses the asset's ISIN as its key.

Technologies

TypeScript
The language of both repositories.
NestJS
The API framework, with a guard, an interceptor and a global filter.
TypeORM
Entities and the five versioned migrations.
PostgreSQL
The database for expenses, income, positions and institutions.
Pluggy
The Open Finance aggregator: connect token, transactions and investments.
Firebase
Identity, with the token verified on the server.
Pino
Structured logging for the API.
exceljs
The three spreadsheet exports.
Swagger
API documentation.
Next.js
The web application, with the investment and expense screens.
Recharts
Charts for spending by month and by category.
Docker
A multi-stage API image, running as a non-root user.

Want to talk about architecture, technical decisions and shipping to production?

Portrait of Thiago Sanches

Contact details

Available for remote work with international teams

Name
Thiago Sanches
Location
Brazil · UTC−3 · remote
Languages
Portuguese native · professional working proficiency in English
Download résumé

Experience

  • 5 years

    on enterprise systems

  • 70%

    less time per new report

  • 50%

    less latency on GraphQL

  • Workers

    asynchronous processing with Redis queues