backlens 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +108 -44
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,29 +1,68 @@
1
1
  # backlens
2
2
 
3
- See every request your Node backend handles, every query it makes, and what is wrong with them. No code changes, no account, nothing leaves your machine.
3
+ **See what your Node backend really does on every request, find what is slow, and get the fix proven before you keep it.**
4
+
5
+ [![npm](https://img.shields.io/npm/v/backlens)](https://www.npmjs.com/package/backlens)
6
+ [![license](https://img.shields.io/npm/l/backlens)](LICENSE)
7
+ [![node](https://img.shields.io/node/v/backlens)](package.json)
4
8
 
5
9
  ```
6
10
  npx backlens
7
11
  ```
8
12
 
9
- Run it in your server's folder. Your browser opens the dashboard; the first time, pick which package.json script starts the app and which runs the tests. From then on everything happens in the browser: start, stop and restart the app, read its logs, run the tests, accept known issues, compare before and after.
13
+ One command, in your server's folder. No code changes, no account, nothing leaves your machine.
14
+
15
+ ---
16
+
17
+ ## What it does
18
+
19
+ backlens runs your app with a small recorder inside it and opens a dashboard in your browser. As you use your app, every request shows up with the database queries, Redis calls and outgoing HTTP calls it made, and the line of your code behind each one.
20
+
21
+ From that real traffic it finds what makes backends slow:
22
+
23
+ | Issue | What it looks like | Example |
24
+ |---|---|---|
25
+ | **N+1** | one query run once per item in a loop | 20 reviews → 20 `select name from users where id = $1` |
26
+ | **Repeated calls** | the same query with the same values twice in one request | two services both load the current user |
27
+ | **Calls that wait for each other** | independent queries awaited one after another | `await getCounts(); await getRows();` |
28
+ | **Missing indexes** | Postgres reads every row to filter a table | `where state = $1 order by updated_at desc`, no index |
29
+
30
+ Each issue points at the file and line that caused it, opens in your editor, and can be explained or fixed.
31
+
32
+ ## Fixes, with proof
33
+
34
+ **Suggest fix** shows the change as a diff. **Apply and check** then:
35
+
36
+ 1. keeps an exact copy of every file it touches
37
+ 2. applies the change and runs your typecheck (type errors go back to the model, up to twice)
38
+ 3. restarts your app, warms it up, and replays the request that showed the issue
39
+ 4. runs your tests
40
+
41
+ Only a change that removes the issue and keeps the tests green is marked **proven**, and you see the same request before and after:
42
+
43
+ ```
44
+ before after
45
+ time (median) 48 ms → 21 ms -56%
46
+ database queries 22 → 2 -91%
47
+ issue found → gone
48
+ ```
10
49
 
11
- The link backlens prints carries a private token: keep it to yourself, it controls your app. The dashboard listens on 127.0.0.1 only, refuses requests from other sites, and only ever runs scripts from your package.json by name.
50
+ Then **Keep** it, or **Revert** to the exact files you had.
12
51
 
13
- To run one exact command instead: `npx backlens -- node server.js`.
52
+ Some fixes need no AI: running independent calls together (`Promise.all`) and missing indexes are written by backlens itself. A missing index can be created on your dev database from the dashboard, checked against the query's plan, and undone.
14
53
 
15
- To keep it in the project, install it as a dev dependency with your package manager (`npm i -D backlens`, `pnpm add -D backlens`, `yarn add -D backlens`). In a pnpm or yarn workspace, use that tool: npm cannot install into a project whose package.json uses `workspace:` versions.
54
+ ## AI: none, local or Claude
16
55
 
17
- The dashboard uses port 7777, or the next free one if it is taken. `--port` picks one.
56
+ Set up in **AI settings**, entirely from the dashboard.
18
57
 
19
- ## What you get
58
+ | | Cost | Privacy | Good for |
59
+ |---|---|---|---|
60
+ | **No model** | free | nothing leaves your machine | plain explanations, the built-in fixes |
61
+ | **Local model** via [Ollama](https://ollama.com) | free | code stays on your machine | explaining; fixes are hit-and-miss on small models |
62
+ | **Claude**, your own key | a few cents per fix | what the preview shows is sent to Anthropic | the strongest fixes |
20
63
 
21
- - **Routes**: every endpoint your app registers (fastify and nest report them at startup; express routes appear once called), with calls, p50 and p95, and how many database, Redis and outgoing HTTP calls each request makes.
22
- - **Requests**: a waterfall of any single request, with the line of your code behind each call.
23
- - **Issues**: problems found in real traffic, worst first, each pointing at the code that caused it:
24
- - **Repeated calls**: the same query with the same values twice in one request. backlens names the two places the code paths part, which is where the second fetch comes from.
25
- - **N+1**: one query run three or more times with different values in one request.
26
- - **Calls that wait for each other**: two or more calls of the same kind, awaited back to back in one function. Shown as a hint: if they don't depend on each other, `Promise.all` runs them together.
64
+ - The local card links to the Ollama download, notices when it runs, suggests the `qwen2.5-coder` size that fits your memory and downloads it with a progress bar.
65
+ - A Claude key is pasted in the dashboard, checked for free, and saved in your user folder (`~/.config/backlens`, readable by you only), never in the project. Haiku explains; Sonnet or Opus fixes, depending on how hard the issue is. The cost is estimated before sending and a daily budget (default $1) is enforced. A typical fix costs about $0.03.
27
66
 
28
67
  ## In CI: fail the build on new issues
29
68
 
@@ -31,64 +70,89 @@ The dashboard uses port 7777, or the next free one if it is taken. `--port` pick
31
70
  npx backlens test -- npx vitest run
32
71
  ```
33
72
 
34
- Runs your test command with recording on, prints every issue it found, and exits 1 when there is a new warning. Tests that call the app over HTTP (supertest, fetch) are recorded like real traffic.
35
-
36
- - `--update-baseline` accepts everything found in this run as known, in `backlens.baseline.json`. Commit that file. An issue keeps its identity when lines above it move.
37
- - `--strict` fails on new hints too.
38
- - `--json report.json` writes the report for a CI artifact.
73
+ Runs your tests with recording on, prints every issue, and exits 1 on a new one. Tests that call your app over HTTP (supertest, fetch) count as real traffic.
39
74
 
40
75
  ```
41
76
  backlens 90 requests (207 queries, 241 redis, 0 http out), 11 of 47 routes called, 3 issues, 2 new
42
77
  new 3 calls are made twice in one request GET /v1/lessons/:id, 5/11 requests
43
78
  src/modules/access/access.service.ts:75 → src/modules/access/access.service.ts:55
44
- 36 routes never called by these tests
45
79
  backlens: 2 new issues. Fix them, or accept with --update-baseline.
46
80
  ```
47
81
 
48
- ## Missing indexes
82
+ - `--update-baseline` accepts what this run found, in `backlens.baseline.json`. Commit it.
83
+ - `--strict` fails on new hints too. `--json report.json` writes the report for a CI artifact.
49
84
 
50
- For each distinct SELECT, backlens asks Postgres for its plan with `EXPLAIN (GENERIC_PLAN)` (Postgres 16+). Plain EXPLAIN never runs the query, and the generic plan needs no parameter values. When a table is read row by row while filtering on columns that no index starts with, the Issues tab names the table and columns and gives the `CREATE INDEX` to add. It checks whether an index exists, not how fast the query was, so it is right on a small dev database too. When the query also sorts (`ORDER BY … LIMIT`), the sort columns go after the filter columns, so Postgres reads rows already in order.
85
+ ## The dashboard
51
86
 
52
- **Create on this database** builds the index on the database your app is connected to, through the app's own connection: backlens never sees a database password. It shows which database first, builds with `CONCURRENTLY` so writes are not blocked, checks that Postgres can use the index for the query, and **Undo** drops it again. Only an index on the columns backlens checked can be created this way, never arbitrary SQL. Use it on a development database, then add the SQL to a migration.
87
+ - **Map**: a live diagram of your routes, the tables and Redis keys they touch, and where the issues are.
88
+ - **Routes**: every endpoint with calls, p50, p95 and queries per request.
89
+ - **Issues**: worst first, with Explain, Suggest fix and the request that showed it.
90
+ - **Requests**: a waterfall of any request, with the code behind each call.
91
+ - **Tests**: run your test script and see what it hit.
92
+ - **Compare**: snapshot, change code, compare per route.
53
93
 
54
- ## Before and after
94
+ Start, stop, restart and read your app's logs from the same page.
95
+
96
+ ## Getting started
97
+
98
+ ```
99
+ cd your-server
100
+ npx backlens
101
+ ```
55
102
 
56
- In the Compare tab, save a snapshot, change your code, restart, use the same pages, and compare per route: queries and Redis calls per request, p50 and issues. Snapshots are kept in `.backlens/snapshots` in your project; add `.backlens/` to `.gitignore`. Timings only count as a change with 5 or more calls on both sides.
103
+ The first time, pick which `package.json` script starts the app (like `dev`) and which runs the tests. backlens remembers this in `.backlens/` and adds it to your `.gitignore`.
57
104
 
58
- ## Explain and fix, with proof
105
+ - Keep it in the project: `npm i -D backlens`, `pnpm add -D backlens` or `yarn add -D backlens` (in a pnpm or yarn workspace, use that tool).
106
+ - Run one exact command: `npx backlens -- node server.js`.
107
+ - Port 7777, or the next free one. `--port` picks one, `--no-open` skips the browser.
59
108
 
60
- Every issue has **Explain** and **Suggest fix**. A fix goes: a preview of exactly what would be sent and to whom, the model's answer as a diff, **Apply and check** (with an exact copy of each file kept first, uncommitted changes included; Revert refuses to overwrite edits you made after the fix was applied), then backlens rebuilds if needed, restarts your app, replays the request that showed the issue and runs your tests. Only a fix that removes the issue and keeps the tests green is marked proven. Keep it, or revert to the exact files you had.
109
+ ## Works with
61
110
 
62
- Who answers is set up in **AI settings**, all from the dashboard. The first-run setup asks too.
111
+ | | |
112
+ |---|---|
113
+ | Frameworks | express, fastify, nest, Next.js route handlers, plain `node:http` |
114
+ | Database | `pg` (Client and Pool): drizzle, Kysely, knex, raw SQL on Postgres |
115
+ | Cache | `ioredis` |
116
+ | Outgoing calls | global `fetch` |
117
+ | Modules | CommonJS and ESM, vitest included |
118
+ | Runtime | Node 20+, tested on macOS; Linux should work the same way |
63
119
 
64
- - **Local model** (free, private): runs on your computer through [Ollama](https://ollama.com). The card walks you through it: a link to install Ollama, then it notices when Ollama is running, suggests the model that fits your memory (`qwen2.5-coder` 3B for 8 GB, 7B for 16 GB, 14B for 32 GB) and downloads it with a progress bar. Small models explain well; their fixes are hit-and-miss, and every fix is checked anyway.
65
- - **Claude with your own key**: paste the key and backlens checks it for free before saving it in your user folder (`~/.config/backlens`, readable by you only), never in the project. `ANTHROPIC_API_KEY` in the environment wins if set. Explaining uses Claude Haiku 5.5; fixes use Sonnet 5.5 or Opus 5.5 depending on how hard the issue looks (files involved, issue type, test coverage, your past results), one step up after a failed check. A daily budget (default $1) is enforced before sending.
66
- - **No model**: explanations in backlens's own words.
120
+ Not yet: Prisma, mysql2, MongoDB, Windows.
67
121
 
68
- Some fixes never use a model: running independent calls together (`Promise.all`) and missing indexes are written by backlens itself, the same way every time.
122
+ ## Privacy and safety
69
123
 
70
- Values that look like secrets are masked, `.env` files and `node_modules` are never read, and requests kept for replay stay in memory only. Replaying a POST, PUT or DELETE runs it again against your dev database, so it is off unless you allow it in AI settings.
124
+ - **Values never leave your app.** Query parameters and results become short hashes; emails, IPs and ids in Redis keys are masked.
125
+ - **The dashboard is yours only.** It listens on 127.0.0.1, needs the private token in the link it prints, and refuses requests from other websites.
126
+ - **It runs only scripts from your `package.json`,** by name.
127
+ - **Files change only when you click Apply,** with a copy kept first. Revert never overwrites edits you made afterwards.
128
+ - **AI sees only what the preview shows.** Secret-looking values are masked; `.env` and `node_modules` are never read.
129
+ - **Replaying writes is off by default,** since it changes your dev database again.
130
+ - **Indexes are built from checked table and column names** through your app's own connection: backlens never sees a database password.
71
131
 
72
132
  ## How it works
73
133
 
74
- `backlens` starts the dashboard, then runs your command with `NODE_OPTIONS=--require backlens/register`. Inside your app, the register script:
75
-
76
- - times each incoming request on Node's own HTTP server, so it works with express, fastify, nest, Next.js and plain `http`, and records requests the client cancels. Framework internals such as `/_next/` assets and favicons are skipped;
77
- - wraps `pg` (Client and Pool), `ioredis` and global `fetch`;
78
- - records where each call came from. For ORMs like drizzle, which call the driver from a microtask, the call site is read from V8's async stack once the query settles. Code run from bundled chunks (Next.js with Turbopack or webpack) is traced back to your source files through their source maps.
134
+ `backlens` starts the dashboard, then runs your app with `NODE_OPTIONS=--require backlens/register`. Inside your app, the recorder:
79
135
 
80
- Query values are never sent, only a short hash so identical questions can be told apart from different ones. Emails, IP addresses and ids in Redis keys are masked. The dashboard listens on 127.0.0.1 only and keeps the last 1000 requests in memory.
136
+ - times each request on Node's own HTTP server, so any framework on top of it works;
137
+ - wraps `pg`, `ioredis` and `fetch`, and records the line of your code behind each call, through V8's async stack traces for ORMs like drizzle and through source maps for bundled code;
138
+ - asks Postgres for each distinct SELECT's plan with `EXPLAIN (GENERIC_PLAN)` (Postgres 16+), which never runs the query, and flags full-table reads no index covers.
81
139
 
82
- ## Limits of this version
140
+ ## Limits
83
141
 
84
- - Tested on macOS with Node 20+, on a nest + fastify + drizzle app and a Next.js 16 + drizzle app. Linux should work the same way; Windows is untested.
85
- - Requests, `pg` and `ioredis` are recorded in CommonJS and ESM apps (vitest included). The up-front route list needs fastify loaded through CommonJS, as nest does.
86
- - `pg`, `ioredis` and `fetch` only. No Prisma, mysql or mongo yet.
87
- - The index advisor needs Postgres 16 or newer, and looks at equality filters only.
88
- - The "wait for each other" hint skips calls whose input matches an earlier call's output, but it cannot see every dependency (a value changed before use, an order that matters for side effects), so read it as a question, not a verdict.
142
+ - The "wait for each other" hint skips calls that use an earlier call's result, but it cannot see every dependency: read it as a question, not a verdict.
143
+ - The index advisor needs Postgres 16+ and looks at equality filters and the sort above them.
144
+ - Timings on a dev machine are rough; query counts are exact.
89
145
 
90
146
  ## Develop
91
147
 
92
148
  ```
149
+ git clone https://github.com/shayandutta/backlens
150
+ cd backlens && npm install
93
151
  node --test test/
94
152
  ```
153
+
154
+ Bug reports and pull requests are welcome.
155
+
156
+ ## License
157
+
158
+ [MIT](LICENSE) © Shayan Dutta
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backlens",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Run your Node backend through backlens and see every request, every query, and what is wrong with them.",
5
5
  "license": "MIT",
6
6
  "type": "module",