Roadmap Product site

Surfaces

Express middleware v0.2.0

A zero-dependency, dev-only review-notes overlay for Node. Mount one middleware and a floating Red Pen button appears on your pages - leave typed notes, pin them to elements, and they persist to a local JSON file you can keep out of git.

Red Pen for Express is the web sibling of the WordPress plugin. It works in any Express app, and in any Connect-style app that uses the same middleware signature. It has no database, no build step, and no runtime dependencies - it ships as plain Node and serves its overlay client as a single script. Free, MIT. Dev credit: Lincoln Tracy.

Dev-only by default The middleware disables itself when NODE_ENV === 'production'. It is a tool for the people building the app, never for its visitors - so it is safe to leave mounted, and it simply goes quiet in production.

How it works

The middleware does two things. First, it watches outgoing HTML responses and injects a small overlay script tag before the closing body tag, so every page you serve gets the Red Pen button without you editing any templates. Second, it mounts a tiny notes API under a URL prefix (default /__redpen) that the overlay talks to. Notes are read from and written to a single JSON file in your project - by default .redpen/notes.json. There is no other moving part.

Install

Red Pen for Express is not published to npm yet. During development you install it as a local file: dependency, which resolves as a live symlink. Because it is a symlink rather than a copied package, any upgrade to Red Pen flows into every app that links it the next time that app restarts - there is no per-app reinstall step.

From the app you want to annotate:

# top-level app - path is relative to the app
npm install ../red-pen-express

# nested launcher (for example, an app at <name>/launcher)
npm install ../../red-pen-express

That writes a "red-pen-express": "file:../red-pen-express" entry into the consuming project's package.json. You can also use npm link if you prefer a globally linked package.

Mount it first

Add the middleware right after you create the app, before static file serving, compression, or your routes. Mounting it early ensures the overlay is injected into responses and the notes API is reachable. Both module systems are supported.

CommonJS (require)

const express = require('express');
const redPen = require('red-pen-express');

const app = express();
app.use(redPen({ file: __dirname + '/.redpen/notes.json' }));

// ...your routes...

ES Module (import)

import redPen from 'red-pen-express';

const app = express();
app.use(redPen({ file: here + '/.redpen/notes.json' }));

In an ES Module there is no __dirname by default - derive a directory variable (for example with fileURLToPath(import.meta.url)), or omit file entirely to default to the current working directory.

Keep notes out of git Add .redpen/ to your project's .gitignore. Notes are dev-local working data, not something you commit and share.

Options

OptionDefaultDescription
mount/__redpenURL prefix for the notes API and the overlay script.
file./.redpen/notes.jsonWhere notes are stored on disk.
enabledNODE_ENV !== 'production'Master on/off switch for the whole middleware.
injecttrueAuto-inject the overlay into HTML responses. Set to false to add the script tag yourself.

If you turn off auto-injection, include the overlay manually where you want it:

<script src="/__redpen/widget.js" defer></script>

Using the overlay

Start the app as usual. A red Red Pen button appears in the bottom-right corner. From there you can:

What is new in v0.2.0

Version 0.2.0 is the "notes UX parity" milestone - it brings the overlay's day-to-day note handling in line with the WordPress plugin. Every item below is in the current release.

FeatureWhat it does
Reply thread + inline formEach note renders its full reply thread plus a toggleable inline reply form, so a note can become a short conversation.
Edit a note after addingLoad a note back into the form to change its body, type, or priority. You can re-pin it to a different element or clear its anchor. The store stamps an editedAt timestamp on field edits.
Open / Resolved tabsThe old single flat list is replaced by Open and Resolved tabs, each showing a live count. Open is the default view.
Error toasts on failed savesA save that fails (network error or a non-2xx response) now surfaces a red toast instead of failing silently. Reads stay quiet.
Persistent add-form prefsThe add form remembers your last-used type and priority across sessions (stored in localStorage), so repeated notes of the same kind are faster to file.
Undo toast on resolveResolving a note shows an Undo toast; clicking it reopens the note. The toast only appears after the server confirms the resolve.

Storage

All notes for an app live in one file - by default .redpen/notes.json at the project root. It is a simple JSON document holding the array of notes (each with its id, body, type, priority, status, url/page, anchor, replies, and timestamps). Because it is just a file, you can inspect it, back it up, or wipe it by hand. See Core concepts for the full note model.

The notes API

The overlay talks to a small REST API under the mount prefix. You rarely call it directly, but it is documented so you can script against it if needed.

RoutePurpose
GET {mount}/notes?page=<path>List notes, optionally scoped to one page.
POST {mount}/notesCreate a note: { body, type, priority, url, page, anchor }.
PATCH {mount}/notes/:idUpdate a note: { status | body | priority | reply }.
DELETE {mount}/notes/:idDelete a note.
GET {mount}/widget.jsThe overlay client script.

Seeding it across many apps

Because the package installs as a live symlink, Red Pen is meant to ride along in every local app you build. It is currently wired into nine local apps this way. The pattern is always the same three steps: install the local dependency, mount the middleware first, and add .redpen/ to that app's .gitignore. Once linked, every future Red Pen release upgrades all of those apps on their next restart, with no reinstall.

Try the demo

npm install
npm run example   # http://localhost:4000

The demo serves a small page with the overlay injected, so you can exercise the full loop - add, pin, reply, edit, resolve, reopen, delete - against a real .redpen/notes.json.