Full-Stack Development for Beginners: Free Course + Project

A full-stack developer can follow one feature from the interface a person uses, through the server that applies rules, to the database that remembers the result. This free beginner course teaches that flow by building a working task board with semantic HTML, responsive CSS, browser JavaScript, a JSON API, Express and SQLite.

You will not memorise a wall of frameworks. You will build one complete application, inspect every boundary, test its core API and leave with a project you can extend and explain.

Full-stack development diagram connecting a browser interface to a Node.js Express API and SQLite database
The course project connects a browser interface, JSON API and database as one understandable system.
LevelBeginner
Study time18-25 hours
CostFree
Last reviewedAugust 9, 2026
Download the complete Task Board projectFrontend, API, SQLite database setup, automated tests and README.

Download course files

Course contents
  1. Outcomes and prerequisites
  2. The full-stack mental model
  3. Set up the project
  4. Build the semantic frontend
  5. Add responsive CSS
  6. Browser JavaScript
  7. HTTP and JSON APIs
  8. Build the Express server
  9. Persist data with SQLite
  10. Connect every layer
  11. Validation, security and errors
  12. Test the API
  13. Final project
  14. Portfolio and work
  15. FAQ and next steps

Learning outcomes and prerequisites

By the end, you should be able to explain a browser-server-database request, create accessible interface controls, design a small REST-style API, validate untrusted input, execute parameterised SQL, connect a frontend with fetch(), and test create/read/update/delete behaviour.

You need basic computer and file-management skills. Prior programming helps but is not required. If variables, functions and command-line folders are completely new, complete the Python Automation beginner course first or spend a few sessions on JavaScript fundamentals.

Free tools

  • Node.js 24 LTS and npm
  • A code editor such as Visual Studio Code
  • A modern browser with developer tools
  • Git for version control
  • The downloadable starter/final project above
Compatibility note: this project uses Node’s built-in node:sqlite module. It works without an extra database package in the tested Node.js 24.14.0 environment, but the API is still marked experimental in Node 24. Use it here for local learning; evaluate a supported database driver or managed database before production deployment.

Lesson 1: understand the complete request

Objective: trace one action through the entire stack.

1. BrowserUser submits a task
2. API routeServer validates JSON
3. DatabaseSQL stores the row
4. ResponseBrowser renders JSON

The frontend is not “the design” and the backend is not “everything difficult.” The frontend owns the browser experience: structure, presentation, interaction and accessibility. The backend owns server-side rules and access to protected resources. The database stores durable state. HTTP is the agreement that lets those pieces communicate.

For example, clicking Add task does not directly write to SQLite. Browser JavaScript sends a POST /api/tasks request. Express parses the JSON, rejects invalid titles and runs a parameterised insert. The server returns a 201 Created response containing the saved task. Only then does the browser add it to the visible list.

Checkpoint: draw the same four-step path for marking a task complete. Name the HTTP method, URL, data sent, database change and response.

Lesson 2: set up the project

Objective: create a repeatable local development environment.

Install an LTS release of Node.js, then confirm both tools:

node --version
npm --version

After downloading and extracting the course files, open the folder in a terminal:

npm install
npm test
npm start

Visit http://localhost:3000. Do not open public/index.html directly from the filesystem; the page expects its API to exist on the same server.

The folder structure separates browser files from server code:

full-stack-development-for-beginners/
|-- public/
|   |-- index.html
|   |-- styles.css
|   `-- app.js
|-- test/
|   `-- server.test.js
|-- server.js
|-- package.json
`-- README.md

Exercise: stop the server with Ctrl+C, restart it and explain why the browser page stops working while the server is unavailable.

Lesson 3: build a semantic frontend

Objective: create an interface that works with browser and assistive-technology defaults.

The project uses real elements for their intended jobs: <main> for primary content, <form> for submission, <label> for the input name, <button> for actions and <ul> for the task collection. This gives keyboard behaviour and useful semantics before JavaScript adds anything.

<form id="task-form">
  <label for="task-title">New task</label>
  <input id="task-title" maxlength="120" required>
  <button type="submit">Add task</button>
  <p id="message" role="status" aria-live="polite"></p>
</form>

A placeholder is not a label: it disappears when typing and may not be announced consistently. The status region lets error messages be announced without moving focus.

Exercise: use only the keyboard to reach the input and submit button. Then temporarily replace the button with a clickable <div> and note which behaviours you would have to rebuild.

Lesson 4: make the interface responsive

Objective: create a layout that remains usable on a narrow screen.

Start with flexible sizing instead of fixed desktop dimensions. The project caps the content width, lets the input grow and switches the form row to a column under 500 pixels:

.form-row { display: flex; gap: 8px; }
input { flex: 1; min-width: 0; }

@media (max-width: 500px) {
  .form-row { flex-direction: column; }
}

Use browser responsive mode at 320, 390, 768 and 1280 pixels. Check horizontal scrolling, focus indicators, readable line length, button size and long task titles. A page is not responsive merely because it has a viewport meta tag.

Lesson 5: render data safely with browser JavaScript

Objective: turn API data into interactive DOM elements without injecting HTML.

Create elements and assign user content with textContent. Do not concatenate a task title into innerHTML; task titles are untrusted input.

const item = document.createElement("li");
const title = document.createElement("span");
title.textContent = task.title;
item.append(title);

The project attaches change and click listeners to each task. If an update fails, the interface reports the error and reverses the checkbox instead of pretending the database changed.

Checkpoint: enter <img src=x onerror=alert(1)> as a task. It should appear as text, not execute. This does not replace a full content-security strategy, but it demonstrates why safe DOM APIs matter.

Lesson 6: learn the HTTP and JSON contract

Objective: choose meaningful methods, routes and status codes.

  • GET /api/tasks reads the collection.
  • POST /api/tasks creates a task and returns 201.
  • PATCH /api/tasks/:id changes completion state.
  • DELETE /api/tasks/:id deletes one task and returns 204.

A route is a contract, not just a URL. The request defines method, path, headers and body. The response defines status, headers and body. Browser fetch() resolves even when a server returns 400 or 500, so the helper checks response.ok before treating the body as success.

const response = await fetch("/api/tasks", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ title: input.value })
});

if (!response.ok) throw new Error("The task was not saved.");

Lesson 7: build the Express API

Objective: receive JSON, validate it and return predictable responses.

Express 5 requires Node.js 18 or newer; this course uses the current Node 24 LTS line. The JSON middleware has a small body limit because this API expects tiny objects, not file uploads.

app.disable("x-powered-by");
app.use(express.json({ limit: "10kb" }));
app.use(express.static(path.join(__dirname, "public")));

app.post("/api/tasks", (request, response) => {
  const title = typeof request.body.title === "string"
    ? request.body.title.trim()
    : "";

  if (!title || title.length > 120) {
    return response.status(400).json({ error: "Invalid task title." });
  }
  // Insert follows here.
});

Client-side required improves the interface, but it is not security. Anyone can call an API without your form, so the server repeats validation.

Lesson 8: persist tasks with SQLite

Objective: create a table and run parameterised queries.

CREATE TABLE IF NOT EXISTS tasks (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  title TEXT NOT NULL CHECK(length(title) BETWEEN 1 AND 120),
  done INTEGER NOT NULL DEFAULT 0 CHECK(done IN (0, 1)),
  created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
)

The database repeats important constraints because application bugs happen. The insert uses a placeholder, keeping data separate from SQL syntax:

const result = db.prepare(
  "INSERT INTO tasks (title) VALUES (?)"
).run(title);

Never build SQL by joining raw input into a query string. Parameterisation is a core defence against SQL injection.

Exercise: add a nullable due_date column. Decide where date format validation belongs and how the API should represent "no due date."

Lesson 9: connect every layer

Objective: keep browser state and stored state consistent.

On page load, loadTasks() fetches saved rows and renders them. Submission waits for the server-created object before displaying it. Updates send the new boolean value. Deletes remove the visible row only after the API confirms success.

This sequence prevents a common beginner bug: changing the interface first and discovering later that the server rejected the change. Advanced applications may update optimistically, but they also implement rollback and conflict handling.

Checkpoint: open developer tools, select the Network panel and add a task. Inspect request method, request JSON, response status and response JSON. Then refresh the page to prove the database, not the DOM, holds the task.

Finished Full-Stack Task Board project showing three saved tasks and accessible controls
The tested course project after the browser, Express API and SQLite database are connected.

Lesson 10: validation, security and error handling

Objective: recognise what a learning project must add before production.

  • Validate types, length and allowed values on the server.
  • Use parameterised SQL and safe DOM rendering.
  • Return 400 for invalid input, 404 for missing resources and 500 only for unexpected failures.
  • Do not expose stack traces, secrets or database paths to users.
  • Keep secrets in environment variables and exclude .env from Git.
  • Add authentication and authorisation together; login alone does not prove a user may edit a resource.
  • Use HTTPS, secure cookies, CSRF protection and rate controls when the deployment model requires them.
  • Back up persistent data and test restoration.

The downloadable app deliberately has no accounts. Adding homemade authentication casually would teach unsafe habits. Learn password hashing, sessions, cookie flags, authorisation and recovery flows before storing real user credentials.

Lesson 11: test behaviour, not implementation details

Objective: verify the API contract with an isolated database.

Node includes a stable test runner. The supplied test starts Express on a random local port, uses an in-memory SQLite database and exercises the API with real HTTP requests:

test("task API supports create, read, update and delete", async (t) => {
  const { app, db } = createApplication(":memory:");
  const server = app.listen(0);
  // POST, GET, PATCH and DELETE assertions follow.
});

Run npm test. A useful next test checks a title longer than 120 characters. Another verifies 404 responses for missing task IDs.

Final project: turn the task board into a useful product

Objective: extend the supplied app without breaking its existing contract.

Choose one audience: students tracking assignments, freelancers tracking deliverables or a small team tracking release tasks. Add:

  1. due dates with clear validation;
  2. priority or category;
  3. open/completed filters;
  4. edit-title support;
  5. at least four new API tests;
  6. empty, loading and error states;
  7. a short threat model and production-limitations section in the README.

Acceptance checklist

  • The page works at 320px without horizontal scrolling.
  • Every control is keyboard usable and visibly focused.
  • Refreshing preserves data.
  • Invalid input produces a useful 400 response.
  • SQL never includes concatenated user input.
  • User content is rendered with textContent.
  • Tests pass from a clean install.
  • The README explains setup, architecture, decisions, limits and screenshots.

Portfolio, jobs and realistic freelance use

A task board alone does not prove professional readiness. Your explanation can make it useful evidence. Publish a clean repository with setup steps, an architecture diagram, screenshots, API routes, database schema, test output and a short record of trade-offs.

Full-stack skills support junior web-development roles, internal business tools, dashboards, small CRUD systems and integrations. Paid client work also requires requirements discovery, estimates, accessibility, security, deployment, backups, maintenance and communication. Do not accept sensitive authentication, payment or health-data work before you can handle those risks.

Strong portfolio upgrades include importing CSV data, adding role-based permissions after studying authentication, integrating a documented API, or deploying with a managed database. The goal is not a giant feature list; it is a small system that works, is tested and can be explained.

Frequently asked questions

Is this full-stack development course completely free?

Yes. The article and downloadable project files are free and do not require an account.

Do I need JavaScript experience?

No, but basic variables, functions, arrays and asynchronous code will make the project easier. Pause and practise each concept rather than copying the finished files.

Why does the course avoid React at first?

React is useful, but it can hide browser fundamentals from a new learner. Build one application with HTML, CSS and JavaScript first; then frameworks become easier to evaluate rather than memorise.

Which Node.js version should I use?

Use Node.js 24 LTS for this project. The supplied files were tested with Node.js 24.14.0. Do not use Node 20, which is end-of-life.

Is Node’s SQLite module production ready?

Not in the tested Node 24 line; it is still marked experimental there. It keeps this local course project simple, but production applications should assess support requirements and database alternatives.

Does this course include a certificate?

No. Your working application, tests, documentation and ability to explain decisions are the evidence produced by this course.

What should I learn next?

Strengthen JavaScript, SQL, Git and HTTP first. Then choose a frontend path such as React and a production backend/database path based on the work you want to do.

Primary references and next steps

Continue learning: return to the MetaCyberGuru Academy roadmap, practise backend automation in the Python Automation course, or see how tool-based integrations work in the Model Context Protocol guide.