Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Local AI-agent tooling and working notes. Personal to each clone — not part of the project.
#
# Project documentation stays tracked: CONTEXT.md (domain glossary), docs/adr/ (architecture
# decisions) and docs/diagrams/ are shared, and committed code links to them.

# Agent configuration and skill libraries
.agents/
.claude/
skills-lock.json

# Agent-facing conventions (issue tracker, triage labels, domain-doc layout)
docs/agents/

# Local issue tracker: specs, tickets and scratch notes
.scratch/

# Per-repo agent instructions
CLAUDE.md
15 changes: 15 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ The endpoints are as follows:

- Accepts a batch of CRUD operations (PUT/PATCH/DELETE) from the client.

4. POST `/api/data/batch`

- Accepts a **transaction batch** — an ordered run of whole transactions from the head of the client's upload queue — and applies each one in its own database transaction, in order.
- Stops at the first failure. The response holds one result per transaction sent, in the same order and always the same length as the request, so the client never has to infer which transactions were applied. Transactions the batch never reached are reported as `not_attempted`.
- Optional `on_fatal_error` in the request body: `stop` (the default) ends the batch at a fatal failure; `skip` drops that transaction and carries on, so a queue blocked by a poison operation can still drain. The skipped transaction's result still reports `fatal_error` with the error classification, so the client can record that it discarded the transaction.
- `skip` applies to **fatal failures only**. A retryable failure always ends the batch.
- The client may complete through the last result whose status is `success` or `fatal_error`. A `retryable_error` or `not_attempted` result is not completable.

### Error classification

Every failure is sorted into one of two kinds, in `src/persistance/classify-error.ts`:

- **retryable** — the environment misbehaved (deadlock, lock timeout, connection loss, resource exhaustion). The client uploads the transaction again after a delay.
- **fatal** — the data is wrong and can never be stored (missing required field, constraint violation, malformed or out-of-range value, schema mismatch). The client discards the transaction.

## Packages

[node-postgres](https://github.com/brianc/node-postgres) is used to interact with the Postgres database when a client performs requests to the `/api/data` endpoint.
Expand Down
5 changes: 5 additions & 0 deletions backend/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import app from './app.js';
import config from './config.js';
import { getPersister } from './src/persistance/persister.js';

const PORT = process.env.PORT || config.port;

// Resolving the persister is lazy so that importing the app needs no database. Do it here, before
// listening, so a misconfigured database still fails at boot rather than on the first write.
await getPersister();

app.listen(PORT, () => {
console.log(`Server is running @ http://127.0.0.1:${PORT}`);
});
6 changes: 5 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"dev": "tsx watch index.ts",
"format": "prettier --write .",
"check": "tsc -b",
"test": "vitest run",
"generate-types": "openapi-typescript ../openapi.yaml -o src/generated/api.ts"
},
"dependencies": {
Expand All @@ -27,10 +28,13 @@
"@types/mssql": "^9.1.8",
"@types/node": "^22.7.5",
"@types/pg": "^8.20.0",
"@types/supertest": "^7.2.1",
"openapi-typescript": "^7.13.0",
"prettier": "^3.2.4",
"supertest": "^7.2.2",
"tsx": "^4.21.0",
"typescript": "^5.6.2"
"typescript": "^5.6.2",
"vitest": "^4.1.10"
},
"packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee"
}
Loading