Free course ยท Beginner
Full-Stack Web Development Bootcamp
What you will learn
- Write semantic HTML and CSS, and lay out a page with flexbox and grid instead of tables
- Make a page interactive with JavaScript โ the DOM, events, forms and fetch
- Build a REST API with Node and Express that reads and writes to a database
- Design a small relational schema and write the SQL behind your own features
- Authenticate users properly instead of inventing your own login
- Deploy front end, API and database for free, and keep them running at a public URL
Course curriculum
5 modules ยท 15 lessons ยท a worked example and a practice task in every lesson
Module 1The browser: HTML and CSS1 of 53 lessons
Weeks 1โ3 โ the structure and the look. Every later bug is easier to find when the markup is honest.
Semantic HTML
Tags describe meaning, not appearance: header, nav, main, article, footer. Screen readers, search engines and the next developer all depend on that meaning being real.
The lesson
Semantic HTML means using the tag that says what the content IS:
header,nav,main,section,article,footer,button. A wall ofdivs looks identical on screen and tells every machine that reads the page โ a screen reader, a search crawler, your own future self โ nothing at all.Structure a page as one
h1, thenh2for each section, with the content under each heading. Buttons that do something are<button>; links that go somewhere are<a href>. Usedivonly when there is no better tag, and then only as a wrapper.The mistake is choosing a tag for how it LOOKS โ
h3because the text should be smaller, or adivwith a click handler where a button belongs. Styling is CSS's job. Tag by meaning, style by class, and keyboard and screen-reader users get working software for free.<header> <h1>Asha Patil</h1> <nav><a href="#projects">Projects</a> ยท <a href="#contact">Contact</a></nav> </header> <main> <section> <h2>Projects</h2> <article> <h3>Expense tracker</h3> <p>Records expenses and prints a monthly total per category.</p> <button>View the code</button> </article> </section> </main>Example A job card is an <article> with an <h3> title, not five <div>s โ the first is readable by a screen reader, the second is a wall of nothing.
Practice Take any page you use daily and rewrite the skeleton of it in semantic tags only, without any styling.
CSS, flexbox and grid
Flexbox lays out one row or column; grid lays out a whole layout. Knowing which one a problem wants is most of CSS confidence.
The lesson
CSS has three jobs and it is worth separating them in your head: layout (where the boxes go), typography (how the text reads) and colour. Flexbox lays out items in ONE direction โ a row of buttons, a navbar โ and grid places items in TWO directions at once โ a page with a sidebar, a gallery of cards.
The properties worth knowing cold:
display: flexwithjustify-contentandalign-itemsfor alignment;gapinstead of margins between items;display: gridwithgrid-template-columns: repeat(auto-fit, minmax(260px, 1fr))for a card list that reflows by itself; andmax-widthwithmargin: 0 autoto centre a content column. Learn those and half of web layout is done.The mistake is positioning everything absolutely so it matches one screenshot at one window size. Absolute positioning takes elements out of the flow, so nothing can reflow and the page breaks the moment the window changes. Lay out with flex and grid; reserve positioning for badges and overlays.
/* A card list that reflows by itself โ no media query needed. */ .cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; } .card { padding: 18px; border: 1px solid #e5e7eb; border-radius: 14px; background: #fff; } .card h3 { margin: 0 0 6px; }Example display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)) gives a responsive card grid with no media queries at all.
Practice Build a responsive card grid of six job cards that is three across on a laptop and one across on a phone.
Mobile-first and the viewport
Most Indian traffic is a phone, so the phone is the design, not the fallback. Start there, then add width.
The lesson
Mobile-first means you write the phone layout first and add complexity at wider widths, instead of designing a desktop page and shrinking it. Almost every fresher-portfolio visitor is on a phone, and a page that starts narrow cannot be broken by a narrow screen.
Two lines make it real. In the head:
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">โ without it a phone pretends to be a 980px desktop and zooms out. Then in CSS, write your base rules for small screens and add@media (min-width: 720px) { โฆ }for larger ones, never the reverse.The mistake is
position: fixedorwidth: 1200pxin the base rules, which produces horizontal scrolling on a phone. Check every page at 360px before you call it finished: a single long word or one wide image is enough to make the whole page slide sideways./* The base rules ARE the phone. Wider screens are the addition. */ .card { padding: 14px; font-size: 15px; } @media (min-width: 720px) { .card { padding: 22px; font-size: 16px; } }Example Writing base styles for a 390px screen and adding one @media (min-width: 783px) block afterwards produces a better result than shrinking a desktop layout later.
Practice Rebuild one page of your own starting at 390px, and check it at 320px, 390px and 1440px before adding anything.
Module 2JavaScript in the browser2 of 53 lessons
Weeks 4โ6 โ the page starts responding. This is also where hiring tests usually start.
The DOM and events
The DOM is the page as objects. Events are how the browser tells your code that a user did something.
The lesson
The DOM is the browser's live model of your HTML: every tag becomes an object you can read and change with JavaScript.
document.querySelector('.card')finds one element, and changing itstextContent,classListorstyleupdates the page immediately without a reload.Events are how the user talks to you.
addEventListener('click', handler)runs your function when something happens, and the same pattern coverssubmit,input,keydownandscroll. Keep the JS in its own file and usedeferwhen you load it, so the HTML is parsed before your script looks for elements.The mistake is attaching one listener per row inside a loop that re-renders โ 500 rows, 500 listeners, and the removed ones leak. Attach ONE listener to the container and read
event.targetto see what was clicked. That is event delegation, and it is the answer interviewers want.// Build a small list, then attach ONE listener to the list itself. const list = document.createElement('ul'); ['Python', 'SQL', 'React'].forEach((name) => { const li = document.createElement('li'); li.textContent = name; list.appendChild(li); }); list.addEventListener('click', (e) => { if (e.target.tagName === 'LI') console.log('You clicked ' + e.target.textContent); }); document.body.appendChild(list); console.log('Click a skill in the list above โ the log below shows which one.');Example document.getElementById("apply").addEventListener("click", fn) is the shape of every interactive feature you will ever write.
Practice Build a counter and a show/hide panel, each in vanilla JavaScript with no library.
Forms and validation
A form is the only place a visitor gives you data, so it is where most real bugs live โ required fields, wrong formats, double submission.
The lesson
A form is the only place a visitor gives you data, so it is the part that has to work. Wrap fields in a
<form>with a realsubmitevent, give every input a<label for=\"id\">, and use the righttypeโemail,tel,numberโ so phones show the right keyboard.Validate twice, for different reasons. In the browser,
requiredandtypestop honest mistakes before a request is sent. On the server, ALWAYS check again, because anyone can post to your endpoint with curl and skip your JavaScript entirely. Browser validation is a kindness; server validation is the security boundary.The mistake is showing a red border and nothing else. Say what is wrong and in which field: "Enter a phone number with 10 digits". And never clear the form after a failed submit โ retyping four fields because of the fifth is how visitors leave.
<form> <label for="phone">Mobile number</label> <input id="phone" name="phone" type="tel" required pattern="[0-9]{10}" placeholder="10 digits" /> <button type="submit">Get job alerts</button> </form> <!-- Try it: submit empty, then type 10 digits. The browser validates before any request is sent. Your server must check the same rule again, because anyone can skip this page entirely. -->Example Validating an email in the browser saves a round trip, but the SERVER must validate it again: anyone can disable your script.
Practice Build a job enquiry form with client-side validation for name, email and a 10-digit phone number, and show an inline error per field.
fetch, JSON and async
fetch asks a server for data without reloading the page. async/await is how you wait for the answer without freezing the interface.
The lesson
fetchasks a server for data and returns a promise โ a value that will exist later. Withasyncandawait, asynchronous code reads like ordinary code:const res = await fetch(url); const data = await res.json();. JSON is the format that comes back: objects, arrays, strings and numbers, exactly like the dictionaries and lists you already know.Three things to handle every time. Check
res.okbefore using the data, because a 404 or a 500 is not a JavaScript error and will not throw. Wrap it intry { โฆ } catch (e) { โฆ }so a dropped connection shows a message instead of a blank screen. And show a loading state: a user who sees nothing assumes the app is broken.The mistake is a hardcoded URL such as
http://localhost:3000/api/jobsโ fine on your laptop, dead for every visitor. Keep the API address in one place and choose it per environment, and you will not be the person who deploys a working app that fetches from nowhere.// The shape of every fetch: await, check ok, parse, catch. const data = { jobs: [{ title: 'Data Analyst' }, { title: 'QA Engineer' }] }; async function loadJobs() { // In a real app this line is: const res = await fetch('/api/jobs'); const res = { ok: true, json: async () => data }; if (!res.ok) throw new Error('Request failed: ' + res.status); const json = await res.json(); return json.jobs; } loadJobs() .then((jobs) => jobs.forEach((j) => console.log(j.title))) .catch((e) => console.error('Could not load jobs: ' + e.message));Example const res = await fetch("/api/jobs"); const jobs = await res.json(); โ forgetting the second await is the classic bug, and it fails silently.
Practice Load a public JSON API, render the results into cards, and show a loading state and an error state separately.
Module 3The server: Node, Express and the database3 of 53 lessons
Weeks 7โ11 โ the half of the job most self-taught developers skip, and exactly what a full-stack interview probes.
Your first API
An API is a set of URLs that return data instead of pages. Express makes routing them a few lines long.
The lesson
An API is a set of URLs your server answers with data instead of pages. Express gives you four of them in a few lines:
app.get('/api/jobs', โฆ)to list,app.post('/api/jobs', โฆ)to create, plus put and delete for edit and remove. Each handler reads the request and sends JSON back.Keep the same shape in every response โ
{ data: [...] }on success,{ error: 'message' }on failure โ and use the right status code: 200 for fine, 201 for created, 400 for a bad request, 401 for not signed in, 404 for missing, 500 for your own bug. Return the new record after a create so the client does not have to fetch again.The mistake is building the API and the front end at the same time. Finish three endpoints, test them with a browser tab or Postman, THEN connect the interface. Debugging two moving parts at once is how a two-week project becomes a two-month one.
Example app.get("/api/jobs", (req, res) => res.json(jobs)) is a working endpoint; adding a POST that pushes to the same array makes it a working small service.
Practice Build an API with four endpoints โ list, read one, create, delete โ backed by an array in memory first.
SQL and schema design
Tables, rows and columns. A job has a title, a company and a location; a company has many jobs. Writing that down is the whole skill.
The lesson
A schema is the shape of your data: tables, the columns in each, the type of every column, and which column links two tables. Spend twenty minutes drawing it on paper before writing any query โ a job board needs
users,jobsandapplications, and every line of code after that is easier because the shape is settled.Use an integer primary key on every table, a foreign key to point at another table, and add an index to any column you filter or join on.
created_aton every row costs nothing and answers "how many this month" for ever. Never store what you can calculate, and never put two values in one column.The mistake is storing a list of tags in one text column as "python,java,sql". Searching it is guesswork and counting it is impossible. One row per tag in a
job_tagstable is the fix, and it is the difference between a demo and a database.CREATE TABLE jobs ( id INT PRIMARY KEY, title VARCHAR(160) NOT NULL, company_id INT NOT NULL, created_at DATETIME ); CREATE TABLE applications ( id INT PRIMARY KEY, job_id INT NOT NULL, email VARCHAR(160) NOT NULL, status VARCHAR(20) DEFAULT 'applied' ); CREATE INDEX idx_applications_job ON applications (job_id);Example CREATE TABLE jobs (id INTEGER PRIMARY KEY, title TEXT NOT NULL, company_id INTEGER REFERENCES companies(id)); โ the NOT NULL and REFERENCES are what keep bad data out.
Practice Design three tables for your own project, create them in SQLite, insert ten realistic rows and query them back.
Authentication done properly
Never store a password. Hash it, and never invent your own hashing or your own session scheme.
The lesson
Authentication means the server knows who is asking. You store an email and a HASH of the password โ
bcryptโ never the password itself, so a stolen database does not hand over everybody's accounts. On a successful login you issue a token or a signed session, and the client sends it with every later request.Authorisation is the second half and the one freshers forget: being logged in is not the same as being allowed. Before any write, check that the record belongs to this user โ
if (job.user_id !== req.user.id) return res.status(403). Skipping that check is how one user deletes another's job, and it is the first thing a reviewer looks for in a portfolio project.The mistake is protecting pages in the front end and trusting that. Hiding a button is a user-interface decision; anyone can call the endpoint behind it. Every rule belongs on the server, and the front end only decides what to show.
Example bcrypt.hash(password, 10) on sign-up and bcrypt.compare(password, stored) on sign-in โ two lines that replace the plain-text password mistake that gets projects rejected.
Practice Add sign-up and sign-in to your API, store only the hash, and write one protected endpoint that refuses a request without a session.
Module 4Ship it: deploy, monitor, hand over4 of 53 lessons
Weeks 12โ16 โ from localhost to a link you can put on a resume. A deployed project is the single strongest fresher signal.
Environment variables and secrets
Database passwords and API keys live in the environment, never in the repository. A leaked key is the most common self-taught project failure.
The lesson
An environment variable is a value the app reads at run time instead of having it written into the code: the database password, the API key, the port. Your app asks the operating system for
process.env.DATABASE_URLand gets a different answer on your laptop and on the server, with no code change.Keep the names in a committed
.env.examplewith placeholder values, and the real values in.env, which goes in.gitignoreโ before the first commit, not after. If a key ever reaches a public repository, treat it as leaked and rotate it; deleting the commit does not un-leak what has been cloned.The mistake is committing
.envin the first five minutes because the build would not start otherwise. Spend that time on.gitignoreinstead. Environment variables are also why the same build works locally and in production, so this is setup that pays for itself all the way through.Example process.env.DATABASE_URL with a .env file locally and never committed โ add .env to .gitignore before the first commit, not after.
Practice Move every secret in your project into environment variables and confirm .env is ignored by git.
Deploy front end, API and database
Three parts, three free hosts, one working product. Deployment day is where a project becomes evidence.
The lesson
Deploying is three pieces that go to three different places: the React build to a static host, the API to a server or a free app platform, and the database to a managed Postgres or MySQL instance. A deployed project is the single strongest item a fresher can put in front of an interviewer, because it cannot be faked and it can be opened on the spot.
Do them in that order and prove each one as you go: get the front end showing a static page, then point it at a local API, then move the API and set its
DATABASE_URL, then seed one row and check it appears through the deployed URL. Give the API a health route like/api/healththat answers{ ok: true }โ it makes the deploy debuggable.The mistake is a build that only works locally: the API address is hardcoded, the CORS origin is
localhost, and the database is your own. Each of those is one line of configuration, and finding them AFTER deploying is a long evening. Read every URL out of an environment variable from day one.Example A static front end on a free static host, the API on a free app host and a free managed database is enough for a portfolio project โ cost is not the reason freshers do not deploy.
Practice Deploy all three parts and open your own application from a different device.
The README that gets you shortlisted
Most reviewers never run your code. They read the README and skim the history, so write the README for someone with ninety seconds and no patience.
The lesson
Your README is the page a recruiter reads when the link works. Five sections, in this order: what the app does in two lines, a screenshot, the tech used, how to run it locally, and the live URL. A reader should get the point in fifteen seconds without scrolling.
Add the two paragraphs that actually differentiate you: one problem you hit and how you solved it, and one decision you would make differently now. Nobody expects a fresher to have built something flawless; everybody notices the candidate who can explain their own trade-offs.
The mistake is a README that lists technologies and nothing else โ "React, Node, MongoDB" tells a reviewer which tools you touched and nothing about whether you can build. The same three lines of prose, applied to every project you have, is what turns a student repo into a portfolio.
Example Screenshots, a one-line description, the live link, the stack, the three hardest problems you solved, and the setup steps โ in that order.
Practice Write the README for your deployed project and get one honest person to follow only its steps on a clean machine.
Module 5Testing, security and shipping5 of 53 lessons
Prove it works, close the obvious holes, and put it live with a pipeline.
Tests for the API you just built
A few API tests turn "it works when I click it" into something a machine checks on every change. Test the routes, the happy path and the failures.
The lesson
Test against a separate test database the suite creates and drops, so a run never touches the data you are using to develop.
Test status codes and the body together; an endpoint that returns 200 with an error message inside is a bug the front end will trip over.
import request from 'supertest'; import app from '../app.js'; test('POST /api/jobs creates a job', async () => { const res = await request(app) .post('/api/jobs') .send({ title: 'Data Analyst', city: 'Pune' }); expect(res.status).toBe(201); expect(res.body).toHaveProperty('id'); }); test('POST /api/jobs rejects a missing title', async () => { const res = await request(app).post('/api/jobs').send({ city: 'Pune' }); expect(res.status).toBe(400); });Example A test that posts a bad payload and asserts 400 guards the validation you wrote and prevents a later "small change" from removing it.
Practice Write three tests for your API: a successful request, a missing field, and an unauthorised request.
Security basics every full-stack app needs
A handful of habits cover most of the risk: validate everything the client sends, never trust the client for permissions, set a few safe headers, and rate-limit the login.
The lesson
Never build a query by joining strings - parameterised queries are what stop injection, and they cost nothing to use from the start.
Return the smallest error you can: a message that says whether an email exists helps an attacker and helps nobody else. "Invalid email or password" for both cases is the safe answer.
import helmet from 'helmet'; import rateLimit from 'express-rate-limit'; app.use(helmet()); // sensible security headers const loginLimit = rateLimit({ windowMs: 60_000, max: 5 }); app.post('/api/login', loginLimit, loginHandler); // parameterised query - never string-joined const rows = await db.query( 'SELECT id, password_hash FROM users WHERE email = ?', [email] );Example A rate limit of five login attempts a minute turns a password-guessing script into a slow, detectable trickle instead of a crash test.
Practice Add input validation, a rate limit on login and three security headers to your API, then note what each one prevents.
A pipeline and a live URL
A build that runs your tests on every push, then deploys the green version, is the last piece that makes a project look like real software - and gives you a URL to put on your resume.
The lesson
Deploy the front end and the API separately if that is simpler; a static site host plus a small server host with a database is a complete stack for a portfolio piece.
Keep secrets - the database URL, the JWT key - in the host environment, never in the repository, and set the same values on every environment the app runs in.
name: ci on: push: branches: [main] jobs: build-test-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: 20 } - run: npm ci - run: npm test - run: npm run build # then deploy the built output to your host (static + API)Example A GitHub Actions workflow that installs, tests and deploys on the main branch means a mistake fails in the pipeline instead of on the live site.
Practice Add a CI workflow that runs your tests, and connect it to a free host so main deploys itself.
About Full-Stack Web Development Bootcamp
Build and deploy a working web application โ your own front end, your own API and your own database โ and host it at a public URL.
Final-year students and graduates who want a developer role and need a deployed project rather than a list of tutorials watched.
What you will be able to do at the end
- Write semantic HTML and CSS, and lay out a page with flexbox and grid instead of tables
- Make a page interactive with JavaScript โ the DOM, events, forms and fetch
- Build a REST API with Node and Express that reads and writes to a database
- Design a small relational schema and write the SQL behind your own features
- Authenticate users properly instead of inventing your own login
- Deploy front end, API and database for free, and keep them running at a public URL
How the course is structured
4 modules and 12 lessons, arranged so each one ends with something you have built. Every lesson carries a worked example and a practice task โ the practice is the course, the reading is only the setup. Plan for 16 weeks ยท about 10 hours a week.
The full syllabus โ every lesson, its example and its practice task โ is in the Course curriculum below. Nothing is locked and nothing needs an account.
Your weekly routine
- Four 2-hour sessions a week: one concept, then build the smallest working version of it before the next session.
- A three-hour weekend block with no new material โ only finishing and refactoring what you already built.
- Once a week, explain the previous week's code to someone else, or write it out as if you were explaining it. Interviewing is that skill.
What you will have built by the end
- A deployed job-board style application with sign-up, listings and an employer view
- A REST API with real SQL storage behind it
- A public live URL and a README short enough to be read in ninety seconds
Where this leads for a fresher
- Junior full-stack or backend developer
- Front-end developer (with your deployed project as proof)
- Web developer internship
- Support-to-development moves inside a services company
Titles vary between companies; the evidence does not. A deployed project, a set of queries you can explain, or a case study with real testing behind it is what a fresher interview has to work with.
Frequently asked questions
Do I need a degree or a computer science background?
No. Web development is one of the few fields where a deployed, working project is evidence a recruiter accepts in place of a CS degree. Many of the roles freshers apply for say "any graduate".
Should I learn Angular / React / a framework too?
After this course, add ONE framework โ the one the job ads in your city ask for. Framework-hopping is the most common way freshers spend a year and end up with nothing deployed. Fundamentally, this course is what makes a framework quick to pick up.
Is this course free?
The lessons, examples and practice tasks are free. Every project here can be built with free tools and hosted on free tiers, so nothing about finishing it requires payment.
How do I know if I am ready to apply?
Take the free Career Pulse check โ it weighs your skills, communication and project proof against the roles you are targeting. A pipeline of "applied 80, heard nothing" usually means a fixable gap, not bad luck.
Not sure which of these you need first? The free Career Pulse check scores your skills, communication and goal clarity in about three minutes and tells you which gap to close first. Take the free check.