Skip to main content

Bridge: Quality → Full-Stack

You've mastered CLI apps. Now let's build for the web.


What You Mastered in Part 4

SkillWhat You Learned
ESLintEnforce code quality rules
PrettierConsistent formatting
TypeScriptType safety
Pre-commit hooksAutomated checks
Quality gatesSpecWeave validation

What's Coming in Part 5

Part 4: "CLI application with quality tooling"

Part 5: "Web application with frontend and backend"

You'll transform your task tracker into a web application.


The Big Picture Shift

CLI Application:
┌────────────────────┐
│ Terminal │
│ ↓ │
│ Node.js │
│ ↓ │
│ File System │
└────────────────────┘

Full-Stack Application:
┌────────────────────┐
│ Browser │ ← React/Vue/Angular
│ ↓ │
│ HTTP API │ ← Express/Fastify
│ ↓ │
│ Database │ ← PostgreSQL/MongoDB
└────────────────────┘

Same logic, different architecture.


Connection Points

CLI Commands → API Endpoints

In Part 2-4, you had CLI commands:

node task.js add "Buy groceries"
node task.js list
node task.js complete 1

In Part 5, these become API endpoints:

POST /api/tasks        { "title": "Buy groceries" }
GET /api/tasks
PUT /api/tasks/1 { "completed": true }

Same operations, accessible from anywhere.

File Storage → Database

In Part 2-4, you saved to JSON:

await writeFile('tasks.json', JSON.stringify(tasks));

In Part 5, you'll use a database:

await db.tasks.insert({ title, completed: false });

Same data, better storage.

Tests → API Tests + Component Tests

In Part 3, you tested functions:

it('should add task', () => {
expect(addTask('Test')).toHaveProperty('id');
});

In Part 5, you'll test APIs and components:

// API test
it('POST /api/tasks creates task', async () => {
const res = await request(app).post('/api/tasks').send({ title: 'Test' });
expect(res.status).toBe(201);
});

// Component test
it('TaskList renders tasks', () => {
render(<TaskList tasks={[{ id: 1, title: 'Test' }]} />);
expect(screen.getByText('Test')).toBeInTheDocument();
});

Same testing skills, new contexts.


Self-Assessment

Before Part 5, you should be comfortable with:

  • Writing and testing Node.js functions
  • Understanding async/await
  • Using TypeScript types
  • Running quality checks (lint, format, test)
  • Using SpecWeave for development

Unsure about any of these? Review Parts 2-4.


Bridge Exercise

Before starting Part 5, refactor your task tracker:

Convert your CLI commands to functions that could work with any interface:

// Before: CLI-coupled
export async function handleCommand(args) {
if (args[0] === 'add') {
const task = await addTask(args[1]);
console.log(`Added: ${task.title}`);
}
}

// After: Interface-agnostic
export async function createTask(title: string): Promise<Task> {
// Pure business logic, no CLI concerns
return await addTask(title);
}

// CLI layer calls this
// API layer calls this
// UI layer calls this

This separation makes full-stack easier.


What Changes in Part 5

Parts 2-4Part 5
Terminal interfaceBrowser interface
One userMany users
Local fileNetwork database
Synchronous feelAsync everywhere
Run locallyDeploy to server

New Concepts Preview

Backend (Express)

import express from 'express';
const app = express();

app.get('/api/tasks', async (req, res) => {
const tasks = await taskService.getAll();
res.json(tasks);
});

app.post('/api/tasks', async (req, res) => {
const task = await taskService.create(req.body);
res.status(201).json(task);
});

app.listen(3000);

Frontend (React)

function TaskList() {
const [tasks, setTasks] = useState([]);

useEffect(() => {
fetch('/api/tasks')
.then(res => res.json())
.then(setTasks);
}, []);

return (
<ul>
{tasks.map(task => (
<li key={task.id}>{task.title}</li>
))}
</ul>
);
}

Database (Prisma)

// Define schema
model Task {
id Int @id @default(autoincrement())
title String
completed Boolean @default(false)
createdAt DateTime @default(now())
}

// Use it
const tasks = await prisma.task.findMany();

Architecture Preview

┌─────────────────────────────────────────────────────┐
│ FRONTEND │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Pages │→ │Components│→ │ API │→ fetch() │
│ └─────────┘ └─────────┘ └──Client─┘ │
└─────────────────────────────────────────────────────┘
↓ HTTP
┌─────────────────────────────────────────────────────┐
│ BACKEND │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Routes │→ │Services │→ │Database │→ prisma │
│ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────┘

Your CLI logic becomes the Services layer.


Skills You'll Reuse

Part 2-4 SkillPart 5 Application
Async/awaitAPI calls, database queries
Error handlingHTTP error responses
TypeScriptShared types for frontend/backend
TestingAPI tests, component tests
SpecWeaveFull-stack increments

Everything you learned applies — you're just adding new layers.


Ready?

Start Part 5: Full-Stack