npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@malvo/server

v0.2.2

Published

Node.js/TypeScript server SDK for the Malvo API (Open Finance Brasil) — manage items, accounts, transactions, investments and webhooks, and mint connect tokens.

Readme

@malvo/server

Node.js / TypeScript server SDK for the Malvo API (Open Finance Brasil). One MalvoClient class manages the apiKey lifecycle and exposes one method per endpoint: connectors, items, accounts, transactions, investments, loans, bills, identity, categories, consents, webhooks and merchants — plus minting connect tokens for the hosted widget.

Talks to a single host: https://api.malvo.io. Data aggregation only — no payment initiation.

Methods take positional arguments and return paged envelopes:

const accounts = await malvo.fetchAccounts(itemId);
const txs = await malvo.fetchAllTransactions(accounts.results[0].id);

Short, un-prefixed type names are exported alongside the Malvo* ones (Item, Account, Transaction, Connector, Investment, Loan, IdentityResponse, CreditCardBills, PageResponse, …).

Install

npm install @malvo/server

Requires Node 18+ (uses the global fetch). Zero runtime dependencies.

Quickstart

import { MalvoClient } from "@malvo/server";

const malvo = new MalvoClient({
  clientId: process.env.MALVO_CLIENT_ID!,
  clientSecret: process.env.MALVO_CLIENT_SECRET!,
});

// apiKey is fetched lazily, cached, refreshed and retried for you.
const { results: connectors } = await malvo.fetchConnectors({ sandbox: true });

Keep secrets on the server. clientId / clientSecret (and the apiKey the SDK derives from them) must never reach the browser. The only value safe to send to a frontend is the 30-minute connect token.

The token chain

| Step | What | TTL | |---|---|---| | 1 | clientId + clientSecret → apiKey (managed internally by the SDK) | 2h | | 2 | malvo.createConnectToken(...)accessToken (the connect token) | 30 min | | 3 | Frontend opens the widget with the connect token | — |

Minting a connect token (Express)

import express from "express";
import { MalvoClient } from "@malvo/server";

const malvo = new MalvoClient({
  clientId: process.env.MALVO_CLIENT_ID!,
  clientSecret: process.env.MALVO_CLIENT_SECRET!,
});

const app = express();

app.post("/api/malvo/connect-token", async (req, res) => {
  // createConnectToken(itemId?, options?) — pass an itemId to mint an
  // update-mode token; omit it (undefined) for a new connection.
  const { accessToken } = await malvo.createConnectToken(undefined, {
    clientUserId: req.user.id,
    webhookUrl: process.env.MALVO_WEBHOOK_URL,
  });
  res.json({ accessToken }); // the frontend passes this to the widget
});

Reading data

After the item/created / item/updated webhook fires, read the item's data. fetchAllTransactions(accountId) walks the cursor and returns every transaction in one array:

const { results: accounts } = await malvo.fetchAccounts(itemId);

for (const account of accounts) {
  const txs = await malvo.fetchAllTransactions(account.id);
  for (const tx of txs) {
    console.log(tx.date, tx.description, tx.amount);
  }
}

Or grab a single page: fetchTransactions(accountId, options) (page-based, { results, total, page, totalPages }) or fetchTransactionsCursor(accountId, options) (cursor-based, { results, next }). For huge accounts, streamTransactions(accountId) is an async iterator that pages lazily.

Error handling

Every method throws MalvoApiError on a non-2xx response. Branch on the stable codeDescription, never on the human message:

import { MalvoApiError } from "@malvo/server";

try {
  await malvo.fetchItem(itemId);
} catch (err) {
  if (err instanceof MalvoApiError && err.codeDescription === "ITEM_NOT_FOUND") {
    // handle missing item
  } else {
    throw err;
  }
}

401 (apiKey expired) is retried automatically once, and 429 is backed off up to maxRetries times respecting Retry-After.

Webhooks

import { parseWebhookEvent } from "@malvo/server";

app.post("/webhooks/malvo", express.json(), (req, res) => {
  const event = parseWebhookEvent(req.body); // typed, throws on malformed input
  res.sendStatus(200); // ack fast; deduplicate by event.eventId

  if (event.event === "transactions/created") {
    // re-fetch the affected account's transactions
  }
});

Malvo webhooks have no HMAC signature — secure the endpoint with the custom headers you register (e.g. a bearer secret) and/or by allowlisting Malvo's egress IP from the Dashboard.

Webhooks are the source of truth. Persist connections and data from the item/* and transactions/* webhooks; don't rely on a single response.

Docs

Full reference: https://malvo.io/docs/sdks/node