A beginner-friendly TypeScript/Node.js guide
Welcome to another official UVF IT build guide! In this tutorial we’ll create a tiny clone of the REA (Reverse Engineer Anything) agent using plain TypeScript, Node.js, and a local SQLite database. The goal is to mimic the core idea – an agent that receives a task, stores it, and returns a mock analysis – without any heavy binaries or external services.
We’ll keep the stack free‑tier and lightweight: Node 22+, better-sqlite3 for persistence, and a tiny Express server for the API. In about 45 minutes you’ll have a runnable prototype you can extend with real analysis tools later.
npm scripts only.Initialize a new npm project and add the minimal dependencies. This gives us a clean package.json and pulls in the libraries we’ll need for a TypeScript‑based Express server and SQLite persistence.
We also install development tools to compile TypeScript on the fly, making the next steps fast and simple.
mkdir rea-clone && cd rea-clone
npm init -y
npm install express better-sqlite3
npm install -D typescript ts-node-dev @types/expressCreate a tiny SQLite helper that creates a tasks table and provides insert/query functions. The helper abstracts raw SQL so the rest of the app can work with plain objects.
We keep the file under 25 lines, using better-sqlite3 which is synchronous and perfect for a small prototype.
src/db.ts
import Database from 'better-sqlite3';
const db = new Database('tasks.db');
// Ensure the table exists
db.exec(`CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
description TEXT NOT NULL,
result TEXT
);`);
export interface Task {
id?: number;
description: string;
result?: string;
}
export function addTask(task: Task): number {
const stmt = db.prepare('INSERT INTO tasks (description, result) VALUES (?, ?)');
const info = stmt.run(task.description, task.result ?? null);
return Number(info.lastInsertRowid);
}
export function getAllTasks(): Task[] {
const stmt = db.prepare('SELECT id, description, result FROM tasks ORDER BY id DESC');
return stmt.all();
}In this step we create the heart of our clone: a tiny agent that pretends to analyse a user query. The function mockAnalyze simply waits a short time to simulate work and then returns a static description, giving us a predictable result for testing. Keeping it async lets us later swap in real analysis code without changing the surrounding API.
We place the logic in src/agent.ts so the server can import it cleanly. By isolating the mock here we keep the rest of the codebase tidy and make future upgrades straightforward.
src/agent.ts
export interface AnalysisResult {
id: number;
query: string;
result: string;
}
let nextId = 1;
/**
* Simulates a heavy analysis step.
* Returns a static string after 500ms.
*/
export async function mockAnalyze(query: string): Promise<AnalysisResult> {
const delay = (ms: number) => new Promise(res => setTimeout(res, ms));
await delay(500); // mimic processing time
const result: AnalysisResult = {
id: nextId++,
query,
result: `Mock analysis complete for "${query}". No real processing performed.`
};
return result;
}Now we expose the agent via a tiny HTTP API using Express. A POST to /task accepts a JSON payload with a query, runs mockAnalyze, stores the result in memory, and returns the generated task ID. A GET to /task/:id retrieves the stored analysis, letting callers poll for completion.
For quick manual testing we also serve a minimal HTML page at the root. The page contains a form that POSTs to /task and displays the returned ID, then automatically fetches the result. All of this lives in src/server.ts and stays under 25 lines for clarity.
src/server.ts
import express from 'express';
import bodyParser from 'body-parser';
import { mockAnalyze, AnalysisResult } from './agent';
const app = express();
app.use(bodyParser.json());
// In‑memory store for results
const store = new Map<number, AnalysisResult>();
app.post('/task', async (req, res) => {
const { query } = req.body;
if (!query) return res.status(400).json({ error: 'Missing query' });
const analysis = await mockAnalyze(query);
store.set(analysis.id, analysis);
res.json({ id: analysis.id });
});
app.get('/task/:id', (req, res) => {
const id = Number(req.params.id);
const result = store.get(id);
if (!result) return res.status(404).json({ error: 'Not found' });
res.json({ result: result.result });
});
// Simple UI for manual testing
app.get('/', (req, res) => {
res.send(`
<html><body>
<h2>Mock REA Agent</h2>
<form method="POST" action="/task" onsubmit="event.preventDefault(); submitTask();">
<input name="query" placeholder="Enter query" required />
<button type="submit">Run</button>
</form>
<pre id="output"></pre>
<script>
async function submitTask(){
const query=document.querySelector('input[name="query"]').value;
const resp=await fetch('/task',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({query})});
const {id}=await resp.json();
document.getElementById('output').textContent='Task ID: '+id+' (waiting...)';
const resultResp=await fetch('/task/'+id);
const {result}=await resultResp.json();
document.getElementById('output').textContent='Result: '+result;
}
</script>
</body></html>
`);
});
const PORT = 3000;
app.listen(PORT, () => console.log(`Server running http://localhost:${PORT}`));With the server code in place we can launch the application directly using ts-node. The server will start on port 3000, serve the HTML form, and accept API calls. Use the form, curl, or any HTTP client to POST a query and then fetch the mock analysis result.
Because the analysis is deterministic, you should always see the same static message, confirming that the plumbing works before you replace the stub with real reverse‑engineering tools.
npm install express body-parser ts-node typescript @types/express @types/node @types/body-parser
npx ts-node src/server.ts| Check | Command / Why it matters |
|---|---|
| Server starts without errors | Run npx ts-node src/server.ts and see "Server running http://localhost:3000" in console. |
| POST a task returns an ID | curl -X POST http://localhost:3000/task -H 'Content-Type: application/json' -d '{"query":"test"}' | jq .id |
| GET the same ID returns mock analysis | curl http://localhost:3000/task/1 | jq .result |
You now have a functional skeleton of a REA‑style agent. Replace the mockAnalyze stub with real tooling (e.g., child_process to run objdump) and expand the DB schema to track logs, timestamps, or multi‑step investigations. Happy hacking!