Hide archived documents from the document list
Node · Node · intermediate · modification
Archived documents were cluttering the document list, so the list now hides them by default. The repo's `listForOrg` takes an `includeArchived` option and builds its filter from a list of conditions (the organisation, plus `archived: false` unless archived ones are asked for), and the route reads `?includeArchived=true|false`, answering `400 invalid_query` for anything else, so the "Show archived" toggle in the web app can ask for the full list. The document page (`GET /api/documents/:id`) is untouched and still opens archived documents.
Express 4 multi-tenant document API (CommonJS) with Prisma 5.22 on PostgreSQL 16. Existing, unchanged: `src/db.js` exports the shared `prisma` client; `src/app.js` mounts `app.use('/api/documents', requireAuth, documentsRouter)` with Express's default query parser and ends with an error handler that logs and answers `500 { error: 'internal' }`; `requireAuth` answers `401` without a session and otherwise sets `req.user = { id, orgId }` with `orgId` a positive integer. Every member may see all of their organisation's documents. Model: `Document { id Int @id @default(autoincrement()), orgId Int, title String, archived Boolean @default(false), updatedAt DateTime @updatedAt, @@index([orgId, archived]) }`; admins archive documents from the admin console (`src/routes/admin.js`, unchanged), which sets `archived = true` and reads documents with its own queries; `listForOrg` is called only from this route. An organisation has at most a few hundred documents, so the list is not paginated. The web app's toggle ships separately.
Requirements
- `GET /api/documents` keeps returning `200 { documents }` with the same fields (`id`, `title`, `archived`, `updatedAt`) and the same order (most recently updated first, then by id, highest first), but by default it now lists only documents that are both in the caller's organisation and not archived.
- `?includeArchived=true` lists every document of the caller's organisation, archived ones included. `?includeArchived=false` or no parameter gives the default list. Any other value (another spelling, an empty value, the parameter given twice) answers `400 { error: 'invalid_query' }`.
- A member never sees another organisation's documents, whatever the parameter says, as before this change.
- Unchanged: `GET /api/documents/:id` still returns one of the caller's organisation's documents, archived or not, and `404 { error: 'not_found' }` otherwise; database errors go to the app's error handler (`next(err)`).
Files touched
- src/routes/documents.js
- src/repos/documentRepo.js
--- src/routes/documents.js
const ID_PATTERN = /^[1-9][0-9]{0,8}$/;
+const FLAG_VALUES = ['true', 'false'];
router.get('/', async (req, res, next) => {
+ const { includeArchived = 'false' } = req.query;
+ if (!FLAG_VALUES.includes(includeArchived)) {
+ return res.status(400).json({ error: 'invalid_query' });
+ }
try {
- const documents = await documentRepo.listForOrg(req.user.orgId);
+ const documents = await documentRepo.listForOrg(req.user.orgId, {
+ includeArchived: includeArchived === 'true',
+ });
res.json({ documents });
} catch (err) {
--- src/repos/documentRepo.js
const LIST_FIELDS = { id: true, title: true, archived: true, updatedAt: true };
-async function listForOrg(orgId) {
+async function listForOrg(orgId, { includeArchived = false } = {}) {
+ const conditions = [{ orgId }];
+ if (!includeArchived) {
+ conditions.push({ archived: false });
+ }
+
return prisma.document.findMany({
- where: { orgId },
+ where: { OR: conditions },