@flowslens/cli

Trace any user action from the UI to the database. Maps every click through React/Next.js, NestJS/Express, Prisma, Mongoose and the MongoDB driver, flowcharts its decisions, explains the database schema in plain words, shows what a change breaks, finds de
983
Install
npm install @flowslens/cli
Latest Version:1.4.0
License:MIT
Last Updated:Oct 23, 2026
Links: GitHub  ·  npm
Maintainer: kishan-jaiswar

@flowslens/cli

Trace any user action from the UI to the database.

npm version npm downloads Node License: MIT

A free, local code-flow explorer for full-stack JavaScript and TypeScript. Point it at a React / Next.js frontend and a NestJS, Express, Fastify or Next.js API backend, and it shows every user action end to end — the click, the handler, the HTTP request, the route, the service, and the Mongoose, MongoDB or Prisma query — as a tree, a flowchart of every decision, and a local dashboard. A Database page explains every MongoDB collection and Prisma model in plain words: its fields, its links, and the user actions that add, change, remove and read its records. It also tells you what an edit breaks before you commit it, finds unused code, and answers questions for your AI assistant over MCP.

Use it to:

  • onboard onto an unfamiliar codebase: see what a button actually does, from the React component down to the database query
  • visualize a MongoDB or Prisma schema as an ER-style diagram, with no database connection
  • run impact analysis before a refactor: the blast radius of a change and every feature it reaches
  • find dead code: unused files, exports, dependencies and API endpoints
  • document features and the database as Markdown with Mermaid diagrams
  • review pull requests: what a branch changes about the app, the database included, as a PR comment

You join a project. You need to change one screen. So you start clicking through files: which handler runs, which endpoint it calls, which service answers, which collection it writes. Eight tools and an afternoon later you still do not know what else writes that collection.

Flowslens reads your source and answers in one command.

npx @flowslens/cli scan .

It only reads files. It never connects to a database, never runs your code, and never writes anything into your project.


First: will this work on my project?

Flowslens reads a specific set of stacks. Check here before installing — if your stack is on the right, you will get a file count and little else.

It reads today Not yet
React and Next.js (pages/ and App Router) Vue, Svelte, Angular, Astro
NestJS, Express, Fastify Django, Rails, Go, Java, .NET, PHP
Next.js pages/api + App Router handlers, Nuxt server/api GraphQL and tRPC resolvers
MongoDB via Mongoose or the native driver, and Prisma TypeORM, Sequelize, raw SQL
TypeScript or plain JavaScript with JSX (.js, .jsx, .mjs, .cjs) Queues, cron jobs, websockets

So the sweet spot is React/Next + NestJS, Express or Next.js API routes + Mongoose, MongoDB or Prisma. Any folder layout of those works — Flowslens decides what a file is by reading it, not by which directory it sits in, and a frontend and backend in separate repositories is a first-class case.

Point it at something unsupported and it says so plainly, rather than reporting zero and letting you assume it is broken.


Install

npm install -g @flowslens/cli

Or run it without installing anything:

npx @flowslens/cli scan .

Requires Node 18.18 or newer. Nothing else — no database, no config file, no plugin in your project.

The package is @flowslens/cli. The command is flowlens. The extra s is in the npm scope only.


Your first two minutes

1. Scan

cd ~/code/my-app
flowlens scan

This is the only command you need to remember. It prints what it found, what looks wrong, every feature it can trace, and the command to run next:

Feature flows (6)
─────────────────
id                            action               endpoint                       collections
────────────────────────────  ───────────────────  ─────────────────────────────  ───────────────────────────────────
customerspage-delete          Customers · Delete   DELETE /customers/:param       customers,auditlogs
orderform-submit-order        Submit Order         POST /orders                   customers,products,auditlogs,orders
customerform-create-customer  Create Customer      POST /customers                auditlogs,customers

next:  flowlens flow customerspage-delete
   or:  flowlens serve

2. Pick a feature and follow it

Copy any id from that table:

flowlens flow orderform-submit-order
Submit Order  (orderform-submit-order)
web/src/components/OrderForm.tsx:42
risk high (50)   evidence static

USER ACTION
└── [ui action]   Submit Order
    web/src/components/OrderForm.tsx:42
│
▼
FRONTEND
└── [handler]     OrderForm.handleSubmit
    web/src/components/OrderForm.tsx:15  sets: note, products
│
▼
NETWORK
├── [api call]    POST /orders
│   web/src/components/OrderForm.tsx:16  body: customerId, products, note
└── [route]       POST /orders
    api/src/orders/orders.controller.ts:9  dto: CreateOrderDto
│
▼
BACKEND
├── [method]      OrdersController.create
├── [method]      OrdersService.create
└── [method]      AuditService.record
│
▼
DATABASE
├── [db op]       orders.create        schema: Order      create
├── [db op]       auditlogs.create     schema: AuditLog   create
└── [db op]       customers.findById   schema: Customer   read

3. Or click around instead

flowlens serve

A local dashboard on http://127.0.0.1:4177. On the left, every action in the app, grouped by the page the user meets it on — /customers, /orders — rather than the folder its code lives in: a form or dialog is followed up to the page that renders it, and each row shows whether it runs on open, on a click or on a submit, and the API it calls. / jumps to the search.

On the right, six tabs ask questions about the action you have open. Each carries its own headline number, so the worrying one is visible before you open it:

Docs · 7 steps   Decisions · 12 branches   Performance · not run   Tests · none   Issues & impact · 11 breaking   Unused · 14
Tab The question Where the answer comes from
Docs What does this action do, end to end? click → handler → request → route → service → collection
Decisions Which way can it go, and what decides? the source of every function it runs, from the handler to the query
Performance Where does the time go? runtime spans only — each step, and each query with its code
Tests What would catch it if you broke it? which test files import its files, and the cases still to write
Issues & impact What is wrong in it, and what could break? your edits type-checked before and after, findings, the graph
Unused What does nothing use? files, exports, dependencies and endpoints nothing reaches

Issues & impact is the one that changes how you work. It opens with three cards — your uncommitted changes, the bugs in this action, what a change to it would reach. For your edits, every function, component, hook, method and type you changed is compared with its committed text, every use is found with the TypeScript language service, and the project is type-checked as it is and with your files put back — so only the errors your edit introduced are shown, each placed on the page it breaks and said in plain words:

Your change breaks 11 places on 3 pages, in 27 actions

/medicines/[id]   7 breaks   affects Medicine detail page loads, Medicines · Delete, …
  MedicineDetailPage
    Missing `currency` — `formatCurrency` now needs it as argument 2.
    TS2554 Expected 2-3 arguments, but got 1.          7 places

What you changed
  formatCurrency   signature changed   11 breaks
  getStockLevel    body changed        1 to check

Before an edit, the same tab walks the graph backwards from every step and splits it into "shared with other features" and "only this one uses" — because editing one service for one screen is a five-minute change that can break four other screens. Infrastructure (a toast hook, a cache, an audit trail) is kept out of the way so the real findings are not competing with wallpaper. It needs no instrumentation and no tests, so it works on the first run.

Decisions draws the action as a flowchart, read from the code: a diamond for every if, switch, ?:, try/catch and early return or throw, a box for each step in plain words, a drum for each query, and every way out labelled with what the caller gets (422 Respond: Validation failed). Your own helpers and this.service.method() calls are followed into the class that answers; library calls are left out. Copy as text gives the same tree for a pull request — this is the example app's Submit Order:

▸ OrderForm.handleSubmit()
  ⇄ POST /orders  → api/src/orders/orders.controller.ts:9
    ▸ this.ordersService.create(dto)
      ▸ this.customersService.findById(dto.customerId)
        ⛁ customers.findById (read)
        ◆ customer?
        ├─ no
        │  ■ throws new NotFoundException('customer not found') [404]
        └─ yes ↓ continues
      ▸ this.productsService.assertAvailable(…)
        ⛁ products.countDocuments (read)
        ◆ count !== productIds.length?
        ├─ yes
        │  ■ throws new BadRequestException('one or more products are out of stock') [400]
        └─ no ↓ continues
      ⛁ orders.create (create)
      ▸ this.auditService.record('order.created', order._id)
        ⛁ auditlogs.create (create)
    ■ responds this.ordersService.create(dto) [201]
  • setProducts([])
  • setNote('')

Docs is the action end to end: At a glance first, one line per stage that opens in place, then only the stages the action has, with the rest named under Not in this action and the reason. The request step carries the body, guards, DTO check, the code it runs and a curl; Diagram shows the same steps as numbered cards. Generated from the graph by template, with no model in the loop, so it cannot invent a step; Copy as Markdown gives you the same text for a wiki or a handover note. Old #tab=flow|apis|timing|queries|changed|breaks|issues links still land in the right place.

An action that makes several requests is shown as a sequence, and the shapes are told apart rather than lumped together:

In your code What the Docs tab says
const a = await get(); post({ id: a.id }) needs the response from GET …
post(…).then(() => put(…)) only after POST … resolves
Promise.all([get(a), get(b)]) sent at the same time as GET …
useEffect(() => get(…), [user]) + a call setting user re-runs when the state set by GET … arrives
if (isEdit) put() else post() one step, two alternatives, joined with or
a call inside a catch only when the request fails

Every file:line in every tab opens your editor (?editor=vscode, cursor, idea, zed, …).

4. See the database

The navbar at the top of the dashboard switches between Feature flows and Database. The Database page lists every collection the code declares or queries, in words a non-developer can follow:

  • one search box: a label on screen or a key in the database
  • a health check: queries that skip the owner (tenant) filter, deletes that leave linked records behind, lookups no index serves, fields nothing fills or reads
  • a map of every collection, grouped by the folder its schema lives in or by database; hover one to see its links and the key each is joined by

Opening a collection shows an example record, who adds, changes, removes and reads its records (named by user action), what a delete does to linked records, its indexes and queries, and runtime timings when spans were recorded. Every field says what it holds, what it is called on screen, where the user types it and what changes it.

It reads Mongoose schemas (@Schema/@Prop, new Schema()), Prisma (schema.prisma), and the native MongoDB driver: a db.collection<T>() type becomes the schema, and createIndex calls become its indexes. The same report is in the terminal:

flowlens db                     # every collection
flowlens db customers           # one collection, in full
flowlens db --markdown --out docs/database.md   # a document with a Mermaid diagram

Links the code cannot show can be declared in flowlens.config.json:

{ "relations": [{ "from": "orders", "field": "ownerId", "to": "users" }] }

That is the whole workflow. Everything below is for when you want more.


More questions it answers

# "What is this file I'm reading?" — which features run through this line
flowlens where src/components/OrderForm.tsx:20

# "If I change this, what breaks?"
flowlens impact AuditService.record

# "What is already wrong here?" — broken calls, dead endpoints, shared writes
flowlens doctor

# "Write the docs for me" — a feature document in markdown
flowlens flow orderform-submit-order --markdown > docs/submit-order.md

# "What bugs does the code show?" — no auth, tenant leaks, N+1, with the line
flowlens findings --fail-on high

# "What does nothing use?" — files, exports, dependencies, endpoints
flowlens unused

# "What is in the database, and who writes it?" — collections, fields, links
flowlens db orders

# "What does this branch change about the app?" — markdown for a PR comment,
# database changes (collections, fields, indexes, links) included
flowlens diff --base main

impact is the one to reach for before a refactor: it reports the blast radius and every user-visible feature that would be affected.


Something went wrong

flowlens: command not found — either the global install is not on your PATH, or you skipped it. Use npx @flowslens/cli <command> instead, which always works.

"No flows found" or an almost-empty report. Flowslens prints the reason under Notes. The three common ones:

Note says Fix
Frontend found, but no backend routes Your backend is a separate repo: flowlens scan ./web ./api
No API calls detected Your requests go through a house-built wrapper — see below
Contains 40 .vue files, not parsed Unsupported stack; see the table at the top

Your team wraps HTTP in its own helpers. Flowslens already reads the common shape — verb in the function name, path in an options object:

getRequest({ url: getUsersList }); //  GET /users
patchRequestNoLoader({ url: getUser, params: `/${id}` }); //  PATCH /users/:id

If yours is named differently, describe it once (capture group 1 is the verb):

flowlens scan --request-fn '^(get|post|put|patch|delete)Api'

Routes and calls match nothing, but both were found. Usually a global prefix. /api is stripped from both sides by default; change it with --api-prefix /v2.

Your terminal draws boxes as garbage. FLOWLENS_ASCII=1 flowlens flows, or NO_COLOR=1 to drop colour.

SyntaxError on an old Node. You need 18.18 or newer; node --version.


Commands

Command What it answers
flowlens stack [project] What is this built with? Frameworks, with versions.
flowlens scan [project] Build the graph, and list what it found.
flowlens flows [project] Which user actions reach the backend?
flowlens flow <id> Everything one click does, end to end.
flowlens flow <id> --markdown Generate a living feature document.
flowlens where <file>:<line> What is this code for? Features running through it.
flowlens impact <symbol> If I change this, what breaks?
flowlens doctor [project] Broken API calls, dead endpoints, shared writes.
flowlens findings [project] Bugs the code shows, each with the line to open.
flowlens unused [project] Files, folders, exports and deps nothing uses.
flowlens db [collection] Collections, fields, links and who uses each one.
flowlens diff --base main What this branch changes about the app — for a PR.
flowlens serve [project] The dashboard.
flowlens mcp [project] The graph as tools for an AI assistant (MCP).
flowlens init [project] Detect the layout and write flowlens.config.json.
flowlens instrument [project] Set up runtime tracing: new files only, dev only.
flowlens trace [project] Merge recorded runtime spans into the graph.

[project] defaults to the current directory, and scan works from any subdirectory of it. Add --json to any command for machine-readable output.

findings and diff take --fail-on high|medium|low to fail a CI job. serve generates a new span-collection token on every run; --token <t> (or $FLOWLENS_TOKEN) fixes it, so a tracer URL saved in .env.local keeps working. To let an AI assistant query the graph: claude mcp add flowlens -- npx -y @flowslens/cli mcp .


Two repositories, one graph

A frontend and backend in sibling folders is the case Flowslens is built for — the seam between them is the whole point:

flowlens scan ./my-web ./my-api
flowlens scan ./api ./web ./mobile     # several consumers of one API

Scanning every consumer at once also sharpens the findings: an endpoint that looks dead against one frontend may simply be called by the mobile app.


Save your settings

Tired of retyping flags? flowlens init writes a flowlens.config.json you can commit, so everyone on the team gets the same graph:

{
  // Scanned together when no paths are given on the command line.
  "roots": [".", "../shop-api"],

  // Stripped from BOTH frontend URLs and backend routes.
  "apiPrefixes": ["/api"],

  // Your request layer: capture group 1 is the HTTP verb.
  "requestFunctionPattern": "^(get|post|put|patch|delete)Request",

  "ignore": ["legacy", "generated"],
}

Paths in roots are resolved relative to the config file and should use /, so one committed file works for everyone on any OS.


Where it puts things

Nothing goes into your project. The graph lives in your OS cache, keyed by project path, so git status after a scan is empty:

OS Location
Linux $XDG_CACHE_HOME/flowlens, else ~/.cache/flowlens
macOS ~/Library/Caches/flowlens
Windows %LOCALAPPDATA%\flowlens\Cache

scan and serve both print the exact path. FLOWLENS_CACHE moves it. Two commands write to your project, because writing is what you asked them to do: flowlens init writes a config file, and flowlens instrument adds the tracing files — new files only, printing the lines for files you already have rather than editing them.


Optional: prove it actually ran

Everything above is read from source, so a step means "this path can run". Add @flowslens/runtime to your app and a step becomes confirmed — it did run, and here is how long it took. Entirely optional; the CLI works without it. On a Next.js App Router app, flowlens instrument . sets it up for you (--print shows what it would write).


Related packages

Documentation

This page covers everything you need to use the CLI. The engine and the tracer document themselves on their own npm pages: @flowslens/core and @flowslens/runtime. Run flowlens --help for every command and option.

Bug reports and feature requests: jaiswarkishan78@gmail.com.

Licence

MIT

Related Packages

@flowslens/core

Static analysis engine for full-stack TypeScript and JavaScript: maps each UI ac...

1,021 0
code-visualizer

Your codebase companion for JavaScript/TypeScript, in the browser, the terminal...

627 0
@flowslens/runtime

Zero-dependency, development-only request tracer for Node.js, Next.js, Express,...

945 0
owlwarden

Offline security scanner for Node web apps and AI coding agents. OWASP rules wit...

0 0
qleaner

A CLI tool for analyzing and identifying unused files, images, imports, and dead...

7,949 6

Version History

Version Released License
1.4.0 MIT
1.3.1 MIT
1.3.0 MIT
1.2.0 MIT
1.1.2 MIT