Programming for the Web

Full-stack web development across the NESA syllabus — data transmission, web protocols, security, front-end and back-end development, SQL, open source, and Progressive Web Apps.

Year 12 — HSC ~10 weeks Outcomes: SE-12-01 to 09
21Dot points
2Parts
9Outcomes
~10Weeks
Syllabus Part 1 Data transmission using the web

Applications of Web Programming

🎯 (SE-12-03)

📌 Explore the applications of web programming

  • interactive website/webpages
  • e-commerce
  • progressive web apps (PWAs)

Interactive Websites and Webpages

Interactive websites use client-side JavaScript to respond to user actions in real time — without requiring a full page reload. The Document Object Model (DOM) is the programming interface that allows JavaScript to read and manipulate the structure, style, and content of a live HTML page. Examples include form validation, dropdown navigation, image carousels, and live search filtering. Modern Single Page Applications (SPAs) such as those built with React or Vue extend this further, dynamically rendering entire pages client-side.

A worked example shows what "without a full page reload" means. This JavaScript listens for typing in a search box and hides the list items that do not match, so the page filters instantly and nothing is sent to the server.

JavaScript: live search filter (runs in the browser)
const box = document.querySelector('#search');
const items = document.querySelectorAll('#game-list li');

box.addEventListener('input', () => {              // fires on every keystroke
  const term = box.value.toLowerCase();
  items.forEach(li => {
    li.hidden = !li.textContent.toLowerCase().includes(term);
  });
});

If the list holds Reef Runner, Kelp Quest and Star Sailor, typing qu leaves only Kelp Quest visible, and clearing the box brings all three back. The input event is the trigger, the DOM is the thing changed, and the rest of the page is untouched.

E-Commerce

E-commerce platforms use web programming to support the entire buying lifecycle: product browsing, shopping cart management, payment processing, and order tracking. Key technical requirements include secure HTTPS connections (to protect payment data), session management (to maintain cart state across pages), database integration (to query product catalogues and stock levels), and integration with payment gateway APIs (such as Stripe or PayPal). Examples include Amazon, eBay, and Shopify-powered stores.

A checkout is a good example of front-end and back-end work being shared. The browser makes the page easy to use, but every step that involves money or stock is finished on the server.

StageWhere it happensWhat is done
Browse and searchBrowser and serverThe page requests product data; the server queries the product tables and returns it.
Add to cartServer (session)The cart is tied to a session so it survives moving between pages.
Enter card detailsPayment gatewayThe card number goes to the gateway over HTTPS, so the shop never has to store it.
Confirm the orderServer and databaseThe server recalculates the total from its own prices, checks stock and writes the order.
Receipt and trackingServer and emailThe server sends a confirmation and later status updates.

Progressive Web Apps (PWAs)

PWAs are web applications built with standard web technologies (HTML, CSS, JavaScript) that offer a native-app-like experience. They are installable on the user's home screen, can work offline via service workers, and receive push notifications. Starbucks and Pinterest are companies that have released PWAs. PWAs are significant because they eliminate the need for a separate iOS/Android codebase while still appearing as a native application to the end-user.

Scenario A local bakery wants to take pre-orders. An interactive page shows today's stock and updates the total as customers choose items; e-commerce features take a secure card payment; and making it a PWA lets regulars add it to their home screen and reorder with one tap, without the cost of building separate iOS and Android apps.
ApplicationMain technologiesStrengthLimitation
Interactive websiteHTML, CSS, JavaScript, a serverWorks in any browser with nothing to installNeeds a connection for most features
E-commerce siteInteractive front end, sessions, database, payment API, HTTPSHandles the whole buying process securelyNeeds careful security and privacy handling
Progressive web appManifest, service worker, HTTPSInstallable, works offline, one code baseFewer device features than a native app
Exam tip: "Explore" applications An explore or scenario question usually gives a business and asks which kind of web application suits it. Name the type, point to a feature of the scenario that needs it (for example, "regulars reorder on a train with poor reception, so offline support from a PWA helps") and add one limitation. NESA's 2025 online paper included a weather-app scenario that asked for a PWA compared with an interactive website (an observation of one published exam, not a rule for future papers). Marks are lost for describing all three types in general terms without linking any of them to the scenario.

How Data Is Transferred on the Internet

🎯 (SE-12-03)

📌 Investigate and practise how data is transferred on the internet

  • data packets
  • internet protocol (IP) addresses, including IPv4
  • domain name systems (DNS)

Data Packets

When data is sent over the internet, it is broken into small, manageable units called data packets. Each packet contains a header (addressing and control information) and a payload (the actual data fragment). Packets from the same transmission may take different routes through the network and are reassembled in the correct order at the destination. This packet-switching model means the failure of one route does not disrupt the overall transmission — other paths are used instead.

Animated diagram

Data packets: split up, sent separately, put back together

One message becomes four packets that cross the internet by different routes and are reassembled at the server.

What to notice
  • Routers only read the header: the destination IP address tells them where to send each packet next.
  • Packets can arrive out of order; the sequence numbers let TCP rebuild the message in the right order.
  • A gap in the sequence numbers is how the receiver knows a packet was lost, so it can ask for it again.

Choose a journey with the tabs. Step with ◀ and ▶ (or the arrow keys), play or pause with Space.

Try thisWhy is it useful for packets from one message to take different routes? Give one advantage and one problem it creates.

Two layers of headers travel with each packet. The IP header carries the source and destination IP addresses, a hop limit (so a packet lost in a loop is eventually discarded) and the protocol that comes next. The TCP header carries the source and destination port numbers, the sequence number and a checksum for error detection. The payload is one slice of the message.

Worked example: one reply, three packets

GameShelf's server sends a 2,400-byte page to a browser. Suppose the network allows 1,000 bytes of payload per packet (real limits are about 1,500 bytes for the whole packet, but round numbers keep the arithmetic clear). The page needs 2,400 ÷ 1,000 = 2.4, rounded up to 3 packets.

PacketSequence (byte offset)PayloadWhat happens
10bytes 0 to 999Arrives first via router A
21000bytes 1000 to 1999Lost on the way (a router is overloaded)
32000bytes 2000 to 2399Arrives second via router B, so it is out of order
  1. The browser acknowledges packet 1 ("I have everything up to byte 999").
  2. Packet 3 arrives but the next expected sequence number is 1000. The gap tells TCP that packet 2 is missing, so it keeps packet 3 and does not acknowledge past byte 999.
  3. The server gets no acknowledgement for packet 2 in time and retransmits it.
  4. Packet 2 arrives. TCP now has bytes 0 to 2399 in order and hands the complete page to the browser.

This is why the web feels reliable even though the network underneath does not promise delivery. Before any of this, TCP opens the connection with a three-way handshake: the client sends SYN, the server replies SYN-ACK, and the client answers ACK.

IP Addresses and IPv4

Every device on a network is assigned an Internet Protocol (IP) address — a unique numerical label used to identify it for communication purposes. IPv4 (Internet Protocol version 4) addresses are 32-bit numbers expressed in dotted-decimal notation, divided into four octets (e.g., 192.168.1.1). IPv4 supports approximately 4.3 billion unique addresses. Due to address exhaustion, Network Address Translation (NAT) is used so that multiple devices on a private network can share a single public IP address. IPv6 was introduced to address this limitation using 128-bit addresses.

Worked example: reading an IPv4 address

An IPv4 address is 32 bits, written as four octets of 8 bits, each shown as a decimal number from 0 to 255. Converting 192.168.1.1 one octet at a time:

OctetDecimalBinaryWorking
1st19211000000128 + 64
2nd16810101000128 + 32 + 8
3rd1000000011
4th1000000011

With 32 bits there are 232 = 4,294,967,296 possible addresses. IPv6 uses 128 bits, written in hexadecimal groups (for example 2001:db8::1, a reserved documentation address), which gives 2128 addresses.

KindRangeMeaning
Private10.0.0.0 to 10.255.255.255; 172.16.0.0 to 172.31.255.255; 192.168.0.0 to 192.168.255.255Used inside a home, school or business network; not routed on the public internet. NAT lets these devices share one public address.
Loopback127.0.0.1"This computer". A web server run on your own machine is reached here.
PublicEverything else (for example 8.8.8.8)Unique across the internet; assigned through your ISP.
Python: classifying addresses with the ipaddress module
import ipaddress

for text in ["192.168.1.1", "172.20.4.9", "8.8.8.8", "127.0.0.1"]:
    ip = ipaddress.ip_address(text)
    print(text, "private:", ip.is_private, "loopback:", ip.is_loopback)
Output
192.168.1.1 private: True loopback: False
172.20.4.9 private: True loopback: False
8.8.8.8 private: False loopback: False
127.0.0.1 private: True loopback: True

The second address is private because 172.16 to 172.31 is a private block, not just 172.16. This is a common trap in questions that ask which address could belong to a school network.

Try it

IPv4 address explorer

Read an IPv4 address as four octets and as 32 bits, and see its network and host parts. Enable JavaScript to use this tool. In short: an IPv4 address is four numbers from 0 to 255, which are 32 bits, and some of the first bits name the network.

Domain Name System (DNS)

The Domain Name System (DNS) translates human-readable domain names (e.g., www.google.com) into machine-readable IP addresses (e.g., 142.250.70.142). When a user types a URL, the browser queries a DNS resolver (usually provided by their ISP), which traverses a hierarchy of DNS servers (Root → TLD → Authoritative) to resolve the domain. The resolved IP address is then cached locally (for a duration defined by TTL — Time To Live) to speed up future requests.

DNS Resolution Order Browser cache → OS cache → Router cache → ISP Resolver → Root Name Server → TLD Server (e.g., .com) → Authoritative Name Server → IP address returned.

Worked example: resolving a name

A student opens www.gameshelf.example (a made-up name, so it will not resolve on the real internet) for the first time. The steps are the same for any real site:

  1. The browser checks its own DNS cache, then the operating system's cache. Both miss.
  2. The OS asks the configured resolver (usually run by the ISP or the school network). It has no cached answer either.
  3. The resolver asks a root server, which does not know the address but says which servers look after the .example top-level domain.
  4. The TLD server does not know the address either but names the authoritative name server for gameshelf.example.
  5. The authoritative server answers with the record, for instance an A record giving an IPv4 address, and a TTL (time to live) such as 3600 seconds.
  6. Every cache on the way back keeps the answer for the TTL, so the next lookup within the hour is answered instantly and no root, TLD or authoritative server is asked.
RecordHoldsUsed for
AAn IPv4 addressFinding the web server for a name
AAAAAn IPv6 addressThe same, for IPv6
CNAMEAnother name (an alias)Pointing www at the main name, or at a CDN
MXThe mail server for the domainTelling SMTP where to deliver email

The TTL explains the "Try this" above: a changed address keeps being served from caches until each TTL runs out, so the change spreads over minutes to hours.

Hands-on: watch it happen

  1. Open a terminal and run nslookup www.education.nsw.gov.au (on macOS or Linux, dig www.education.nsw.gov.au is an alternative). You should see the resolver that answered and one or more addresses. The exact numbers can change between runs and between networks; that is normal.
  2. Run tracert www.education.nsw.gov.au on Windows, or traceroute www.education.nsw.gov.au on macOS and Linux. Each numbered line is one router (a hop) that the packets crossed, with its response time. Some lines may show * because a router does not answer.
  3. Run ping 127.0.0.1. Every reply comes from your own computer, which is the loopback address described above.

Sequence diagram

How DNS finds an IP address

The browser asks a resolver, which asks the root, TLD and authoritative servers in turn.

What to notice
  • A cached answer skips every step after the first.
  • Each server only knows where to ask next, until the authoritative server gives the address.

Try thisWhy does a changed website address sometimes take hours to work everywhere?

Sequence diagram

An HTTP request and response

A GET request fetches a page; a POST request sends form data and is redirected.

What to notice
  • The server talks to the database; the browser never does.
  • After a POST, a redirect stops the form being resubmitted on refresh.

Try thisOpen your browser's Network tab and find a GET and a POST request on a real site.

▶ Watch & Learn

Two short videos that show how data physically travels as packets and how the browser and server talk using HTTP — ideal background before the protocols section.

Putting it together: one page request

StepWhat happensProtocol or address involved
1The browser turns the name in the URL into an addressDNS, port 53
2It opens a reliable connection to the serverTCP three-way handshake to the server's IP address
3For https:// addresses, it agrees encryptionTLS handshake, port 443
4It sends the requestHTTP GET, split into packets
5The server sends the page backHTTP response, packets that may take different routes
6TCP reorders the packets and the browser renders the pageTCP sequence numbers
Exam tip: "Investigate and practise" Data-transfer questions reward tracing a specific example rather than listing terms. Use the stimulus: give the address or name it mentions, say which step (DNS, TCP, packets) is being asked about and what would go wrong if that step failed. For DNS, name the cache first and the resolver, root, TLD and authoritative servers in order. Common mistakes are saying DNS "stores the website" (it only stores addresses), saying packets always take the same route, and confusing the IP address (which machine) with the port (which program on that machine).

Web Protocols and Their Ports

🎯 (SE-12-03)

📌 Investigate and describe the function of web protocols and their ports

  • HTTP, HTTPS
  • TCP/IP
  • DNS
  • FTP, SFTP
  • SSL, TLS
  • SMTP, POP 3, IMAP

The protocols and their ports

Protocol Port(s) Function
HTTP 80 Hypertext Transfer Protocol — transfers plain-text web content between client and server. Unencrypted; vulnerable to interception.
HTTPS 443 HTTP Secure — HTTP encrypted with TLS/SSL. Required for any site handling sensitive data (login, payments). Provides confidentiality and integrity.
TCP/IP Various Transmission Control Protocol / Internet Protocol — the foundational communication suite of the internet. TCP ensures reliable, ordered, error-checked delivery of packets; IP handles addressing and routing.
DNS 53 Domain Name System — resolves human-readable domain names to IP addresses. Uses UDP for most queries and TCP for very large responses and zone transfers.
FTP 21 (control), 20 (data) File Transfer Protocol — transfers files between a client and server. Unencrypted; largely replaced by SFTP in modern deployments.
SFTP 22 SSH File Transfer Protocol — secure, encrypted file transfer using SSH. Standard for uploading files to web servers safely.
SSL / TLS Inside other protocols (443 for HTTPS) Secure Sockets Layer / Transport Layer Security — cryptographic protocols that provide secure communication over a network. TLS is the modern successor to the deprecated SSL.
SMTP 25 (server to server), 587 (submission) Simple Mail Transfer Protocol — sends outgoing email from a client to a mail server, or between mail servers. Port 587 is used for authenticated submission.
POP3 110 (995 with TLS) Post Office Protocol 3 — downloads email from a server to a local client, then typically deletes it from the server. Best for single-device access.
IMAP 143 (993 with TLS) Internet Message Access Protocol — synchronises email between server and multiple clients without deleting from the server. Best for multi-device access (e.g., phone and laptop).

Ports: which program should receive the data?

An IP address finds the computer; a port is a 16-bit number (0 to 65,535) that finds the program on that computer. A web server, a mail server and an SSH server can all run on one machine because each listens on its own port. Ports 0 to 1023 are the "well-known" ports reserved for standard services such as those in the table, 1024 to 49151 are registered for particular applications, and the rest are used for short-lived connections that a client opens. When you visit https://www.gameshelf.example/games?max_cost=30 the browser fills in port 443 itself, because it is the default for https. Writing http://localhost:8000 names a different port, which is how a program under development is usually reached.

Anatomy of a URL
https://www.gameshelf.example:443/games?max_cost=30#top
\___/   \___________________/ \_/ \____/ \_________/ \_/
scheme        host           port  path     query    fragment
(protocol)  (found by DNS)  (default          (data sent to the server)
                             for https)

Where each protocol sits

Protocols work in layers, each relying on the one below. This is why a question can ask "which protocol finds the address, which delivers it reliably and which carries the page?" and expect three different answers.

LayerJobProtocols in this dot point
ApplicationDefines the conversation for one kind of serviceHTTP, HTTPS, FTP, SFTP, SMTP, POP3, IMAP, DNS
SecurityEncrypts and authenticates the connectionTLS (and its predecessor SSL); SFTP uses SSH for the same purpose
TransportDelivers data to the right program, in order and complete (or not)TCP (reliable, ordered); UDP (fast, no guarantees) is used by most DNS queries
InternetAddresses and routes packets between networksIP (IPv4 and IPv6)

TCP suits web pages, files and email, where a missing byte would corrupt the result. UDP suits things such as a DNS lookup or a live video call, where a fast answer matters more than resending every lost packet. HTTP/3, the newest version of HTTP, runs over a UDP-based transport called QUIC, but the syllabus expects the classic picture of HTTP over TCP.

HTTP in detail: one request, one response

HTTP is a text-based request and response protocol. The browser sends a request line, headers and, for some methods, a body; the server answers with a status line, headers and a body. This is the exchange behind GET /games?max_cost=30 on the GameShelf site (the Server and Date headers depend on the software and the time):

HTTP request (browser to server)
GET /games?max_cost=30 HTTP/1.1
Host: www.gameshelf.example
Accept: application/json
Cookie: session=7f3a9c
HTTP response (server to browser)
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 103

[{"Cost":20.0,"Name":"Kelp Quest"},{"Cost":25.0,"Name":"Reef Runner"},{"Cost":30.0,"Name":"Lava Lab"}]

The Content-Length of 103 is the number of bytes in the body. The Host header matters because one server can host many sites. HTTP is stateless: the server treats every request separately, which is why cookies and sessions (covered in the next section) are needed to recognise a returning user.

MethodMeaningExample
GETRead a resource; should not change anythingGET /games/4
POSTSend data to be processed, often creating somethingSubmitting a sign-up form
PUTReplace a resourceSaving an edited profile
DELETERemove a resourceDELETE /games/4
Status codeMeaningTypical cause
200 OKThe request workedA page or JSON returned normally
301 / 302Moved permanently / found elsewhere (redirect)An old URL, or a redirect after a form POST
400 Bad RequestThe request is malformed or invalid?max_cost=abc where a number was needed
401 UnauthorisedThe user is not authenticatedNo valid login or token
403 ForbiddenAuthenticated, but not permittedA student requesting an admin page
404 Not FoundNo such resourceA mistyped path such as /games/99
500 Internal Server ErrorThe server's code failedAn unhandled exception in the back end

The first digit is the family: 2xx success, 3xx redirection, 4xx the client's mistake, 5xx the server's mistake. That pattern lets you reason about an unfamiliar code.

Email: sending and collecting are different jobs

SMTP only sends and relays mail: your phone hands the message to your provider's server over SMTP, and that server hands it on to the recipient's server over SMTP. To collect the mail, the recipient's device uses IMAP, which keeps the mail on the server and synchronises every device, or POP3, which downloads it (usually removing it from the server). A person who reads email on a phone and a laptop needs IMAP, because with POP3 each message would end up on only one of them.

Insecure and secure versions

Several of the protocols above have a version that adds encryption. The insecure ones send passwords and content as readable text, so anyone on the path (a shared Wi-Fi network, for example) can read them.

PurposeInsecureSecureWhat changes
Web pagesHTTP, 80HTTPS, 443HTTP is wrapped in TLS
File transferFTP, 21SFTP, 22SFTP is a different protocol built on SSH, not FTP with TLS added (that variant is called FTPS)
Reading emailPOP3 110, IMAP 143POP3 over TLS 995, IMAP over TLS 993The same protocols inside a TLS connection
Sending emailSMTP 25SMTP submission 587 (with STARTTLS), or 465The connection is upgraded to TLS and the user logs in

SSL (Secure Sockets Layer) is the older protocol, and all its versions are now considered insecure and are disabled by modern browsers. TLS (Transport Layer Security) replaced it, and TLS 1.3 is the current version. People still say "SSL certificate" out of habit, which is why the syllabus uses both names.

Which protocol?

ScenarioProtocol and portReason
A student uploads a website to the school's server and the password must not be readable on the waySFTP, 22Encrypted file transfer
A teacher wants the same inbox on a phone and a laptopIMAP, 143 (993 with TLS)Mail stays on the server and devices synchronise
The browser needs the IP address for a domain nameDNS, 53Name to address
A shop's checkout page must protect card detailsHTTPS, 443HTTP inside TLS
A mail server passes a message to another mail serverSMTP, 25SMTP relays between servers

Try it

Which protocol?

Practise pairing a job with its protocol and port. Enable JavaScript to use this tool. In short: HTTP 80, HTTPS 443, FTP 21, SFTP 22, SMTP 25, POP3 110, IMAP 143 and DNS 53.

Exam tip: "Investigate and describe" protocols NESA's published familiarisation questions include items that ask you to match features to protocols so learn each protocol as a pair: its job and its port. A full-mark description says what the protocol does, its port, and whether it is secure. Questions often hinge on a detail: SMTP sends but does not retrieve; POP3 downloads and IMAP synchronises; SFTP is not FTP over TLS; 401 and 403 are different. Do not describe TLS as a port or as a website feature: it is a protocol that secures a connection.

Securing the Web

🎯 (SE-12-04, SE-12-07)

📌 Explain the processes for securing the web

  • Secure Sockets Layer (SSL) certificates
  • encryption algorithms
  • encryption keys
  • plain text and cipher text
  • authentication and authorisation
  • hash values
  • digital signatures

SSL/TLS Certificates

An SSL/TLS certificate is a digital document issued by a trusted Certificate Authority (CA) (e.g., Let's Encrypt, DigiCert) that verifies a website's identity and enables encrypted HTTPS connections. When a browser connects to a site, it checks that the certificate is: (1) issued by a trusted CA, (2) not expired, and (3) issued for the correct domain. Without a valid certificate, browsers display a "Not Secure" warning, deterring users and search engines alike.

A certificate is a small signed file with a fixed set of fields. Browsers trust it because of a chain of trust: the server's certificate is signed by an intermediate authority, whose certificate is signed by a root authority, and the root certificates are already stored in the browser or operating system. The browser never has to know the website in advance; it only has to trust the root.

Certificate fieldExampleWhat the browser does with it
Subject (the domain)www.gameshelf.exampleCompares it with the address in the URL bar; a mismatch means a warning
IssuerAn intermediate CAFollows the chain up to a root it already trusts
Validity datesNot before / not afterRejects an expired (or not yet valid) certificate
Public keyThe server's public keyUses it in the handshake to check the server holds the matching private key
SignatureThe CA's digital signature over all of the aboveVerifies it with the CA's public key, so any tampering is detected

A certificate proves that whoever controls the domain asked a CA for it. It does not prove that the site is honest: a scam site can have a valid certificate for its own domain. The padlock means "your connection to this domain is encrypted", not "this site is safe".

Encryption Algorithms

Encryption algorithms transform readable plaintext into unreadable ciphertext, protecting data from interception. Symmetric encryption (e.g., AES-256) uses a single shared key for both encryption and decryption — fast and efficient, but the key must be securely exchanged beforehand. Asymmetric encryption (e.g., RSA-2048) uses a mathematically linked key pair: a public key to encrypt and a private key to decrypt. TLS uses asymmetric encryption to securely exchange a session key, then switches to faster symmetric encryption for the remainder of the connection.

Encryption Keys

An encryption key is a string of bits used by an algorithm to encrypt or decrypt data. Public keys are freely shared and used to encrypt data sent to the key's owner. Private keys are kept secret by the owner and used to decrypt data or create digital signatures. Key length determines security strength: a 256-bit AES key has 2256 possible combinations, making brute-force attacks computationally infeasible.

Symmetric (e.g. AES)Asymmetric (e.g. RSA)
KeysOne shared secret keyA public key and a private key
SpeedFast; suits large amounts of dataSlow; suits small amounts of data
Main problemHow do two strangers share the key safely?Public keys must be known to belong to the right owner (hence certificates)
Used in HTTPS forEncrypting the page and form dataProving identity and agreeing the session key

Worked example: plain text to cipher text

Real algorithms such as AES are far stronger, but a Caesar cipher shows the vocabulary. The algorithm is "shift each letter along the alphabet"; the key is how far (here 3). Anyone who knows the algorithm still cannot decrypt without the key, and that is the point of having a key.

Python: a Caesar cipher (a teaching example, not real security)
def caesar(text, key):
    result = ""
    for ch in text:
        if ch.isalpha():
            base = ord("A") if ch.isupper() else ord("a")
            result += chr((ord(ch) - base + key) % 26 + base)
        else:
            result += ch                     # leave digits and symbols alone
    return result

plain_text = "Pay $25 to Ava"
cipher_text = caesar(plain_text, 3)          # encrypt with key 3
print(cipher_text)
print(caesar(cipher_text, -3))               # decrypt: shift back by the same key
Output
Sdb $25 wr Dyd
Pay $25 to Ava

A Caesar cipher has only 25 useful keys, so a computer tries them all in an instant. AES-256 has 2256 keys, which is why key length matters, and why a good algorithm is one that stays secure even when everyone knows how it works.

Authentication and Authorisation

Authentication is the process of verifying who a user is (e.g., verifying a username and password match a database record). Authorisation is the process of verifying what an authenticated user is permitted to do (e.g., checking if a logged-in user has the "admin" role before allowing access to the dashboard). Modern web applications use JSON Web Tokens (JWT) or OAuth 2.0 to manage these processes securely across multiple services.

Worked example: two different questions

A school portal asks two separate questions on every protected request, and the HTTP status code tells you which one failed.

SituationQuestion that failedResponse
No login cookie or token, or a wrong passwordAuthentication: who are you?401 Unauthorised
A logged-in student requests /admin/marksAuthorisation: are you allowed?403 Forbidden
A logged-in teacher requests /admin/marksBoth pass200 OK
Python: authorisation as a role check (illustrative)
ROLES = {"ava": "admin", "ben": "student"}          # in real life: a database table

def can_delete_game(username):
    return ROLES.get(username) == "admin"           # only admins may delete

print(can_delete_game("ava"))     # authenticated as ava, and ava is an admin
print(can_delete_game("ben"))     # authenticated as ben, but a student
Output
True
False

Multi-factor authentication (MFA) strengthens the first question by asking for two different kinds of proof: something you know (a password), something you have (a phone or security key) or something you are (a fingerprint). Two passwords are not two factors, because both are things you know.

Sequence diagram

Logging in with a JSON Web Token

The server checks the password once, then trusts the signed token on later requests.

What to notice
  • The password is compared with a stored hash, never stored as plain text.
  • Later requests are verified by the token's signature without a database lookup.

Try thisWhat should happen when the token expires?

Cookies and sessions

HTTP is stateless, so after you log in the server needs a way to recognise your next request. It does this with a session: the server creates a record (who you are, what is in your cart) and gives the browser a long random session ID in a cookie. The browser sends the cookie automatically with every later request to that site, and the server looks the session up.

The server sets the cookie in a response header
HTTP/1.1 200 OK
Set-Cookie: session=7f3a9c41d2; Secure; HttpOnly; SameSite=Lax; Max-Age=1800; Path=/
AttributeEffectProtects against
SecureSent only over HTTPSInterception on an unencrypted connection
HttpOnlyJavaScript cannot read itA cross-site scripting attack stealing the session ID
SameSiteLimits sending the cookie on requests started by other sitesCross-site request forgery
Max-Age / ExpiresLifetime of the cookie (1800 seconds is 30 minutes)A stolen or forgotten session staying valid for ever
  1. The user submits the login form; the server checks the password hash.
  2. The server creates a session, stores it and sends Set-Cookie with a new random ID.
  3. Each later request carries Cookie: session=…; the server finds the session and knows who is asking.
  4. On logout or timeout the server deletes the session, so the old cookie is worthless.

Good session management uses unpredictable IDs, issues a new ID at login (so an attacker cannot plant a known one), sets a timeout and deletes the session on logout. The alternative shown in the JWT diagram keeps the state in a signed token instead of on the server; each approach trades server storage against how easily a session can be cancelled.

Hash Values

A hash function is a one-way mathematical function that converts any input into a fixed-length output called a hash value (or digest). For example, the SHA-256 algorithm produces a 64-character hexadecimal hash. Hash functions are deterministic (same input always produces the same hash) but irreversible — it is computationally infeasible to reconstruct the original input from the hash. Web applications use hashing to store passwords securely: the hash of the password is stored, never the password itself. Algorithms like bcrypt add a random salt before hashing to prevent rainbow table attacks.

Digital Signatures

A digital signature is a cryptographic mechanism that proves the authenticity and integrity of a message or document. The sender hashes the content, then encrypts the hash with their private key to produce the signature. The recipient decrypts the signature using the sender's public key and compares it to their own hash of the received content. If they match, the message has not been tampered with and genuinely originated from the claimed sender. Digital signatures provide non-repudiation — the sender cannot later deny having sent the message.

Worked example: hashing a password

A hash has three properties you can see for yourself: the same input always gives the same output, the output has a fixed length, and a tiny change to the input gives a completely different output.

Python: SHA-256 and a salted, slow hash
import hashlib

for password in ["password1", "Password1"]:
    print(password, hashlib.sha256(password.encode()).hexdigest())

# A salted, deliberately slow hash (the fixed salts here only make the output repeatable;
# real systems generate a new random salt for every user)
print(hashlib.pbkdf2_hmac("sha256", b"password1", b"x9Zk", 100_000).hex()[:32])
print(hashlib.pbkdf2_hmac("sha256", b"password1", b"Q7pd", 100_000).hex()[:32])
Output
password1 0b14d501a594442a01c6859541bcb3e8164d183d32937b851835442f69d5c94e
Password1 19513fdc9da4fb72a4a05eb66917548d3c90ff94d5419e1f2363eea89dfee1dd
46882ae63343ff32527bc80e66619bc7
31abdbf53828c47bc0f844de3a934420
  1. Changing one letter's case changes every character of the digest, and both digests are 64 hexadecimal characters long.
  2. The same password with two different salts gives two unrelated hashes, so two users with the same password have different stored values, and a table of precomputed hashes (a rainbow table) is useless.
  3. The 100,000 repetitions make each guess slow. That barely delays a real login but multiplies the cost of guessing billions of passwords. bcrypt, scrypt and Argon2 are designed for this, which is why plain fast SHA-256 alone is not the right choice for passwords.
  4. To check a login, the server hashes the submitted password with the stored salt and compares it with the stored hash. It never needs the original password and cannot recover it.

The steps of a digital signature reuse the same pieces, in a different order:

StepSigning (sender)Verifying (receiver)
1Hash the message to get a digestHash the received message to get its own digest
2Encrypt the digest with the sender's private key: this is the signatureDecrypt the signature with the sender's public key to recover the sender's digest
3Send the message and the signatureCompare the two digests: equal means unaltered and from the key's owner

Animated diagram

Putting it together: how HTTPS secures a connection

This whole dot point in one animation: a certificate, hash values, a digital signature, public and private keys, and plain text becoming cipher text.

What to notice
  • Asymmetric encryption is slow, so HTTPS uses it only to check who the site is and to deliver the session key. The data itself is encrypted with the fast symmetric session key.
  • Checking the certificate is checking a digital signature: the CA signed a hash of the certificate with its private key, and the browser checks it with the CA's public key.
  • Eve can copy everything that passes, but without the private key she only ever sees cipher text.
  • TLS 1.3 agrees the session key with a key exchange (Diffie–Hellman) rather than sending it in a locked box, but the idea is the same: asymmetric cryptography sets up a shared symmetric key.

Compare connections with the tabs. Step with ◀ and ▶ (or the arrow keys), play or pause with Space.

Try thisSwitch to Imposter site. Which check catches the fake? What would Eve need to pass it? (Answer: the CA's private key, to make a valid signature.)

The TLS handshake step by step

The animation above shows the idea. The handshake that makes it happen has these steps, at the depth the syllabus needs:

  1. The browser opens a TCP connection to port 443, then sends a ClientHello listing the TLS versions and cipher suites (algorithm combinations) it supports.
  2. The server replies with the choice it made, and sends its certificate, together with proof that it holds the matching private key.
  3. The browser checks the certificate: signed by a trusted CA, still in date, and issued for exactly this domain. If any check fails, the user sees a warning.
  4. Both sides use a key exchange to work out the same session key. The key itself never crosses the network in a form an eavesdropper can use.
  5. Each side sends a Finished message, protected by the new key, to prove nothing was altered during the handshake.
  6. From here, the HTTP request and response are encrypted with fast symmetric encryption using the session key.

Sequence diagram

The TLS handshake

Certificate checks and a shared session key come first; only then does the HTTP request travel, as cipher text.

What to notice
  • The certificate check happens before any private data is sent.
  • Only after both Finished messages does data travel, and from then on an eavesdropper sees only cipher text.
  • This is simplified: TLS 1.3 combines steps and encrypts the certificate as well, and TLS 1.2 uses a slightly different exchange, but the sequence of ideas is the same.

Try thisA hacker on the cafe Wi-Fi presents a certificate for a different domain. Which step fails, and what does the user see?

Cross-site scripting (XSS)

Encryption protects data on its way to the site; it does nothing if the site itself sends the attacker's code to other users. The Course Specifications describe cross-site scripting as injecting malicious code into an otherwise safe website, usually through user input that is not sufficiently sanitised before it is processed and stored on the server. Students are expected to interpret JavaScript fragments about it.

JavaScript: a vulnerable comment box
const comment = document.querySelector('#comment').value;      // whatever the user typed
document.querySelector('#comments').innerHTML += '<p>' + comment + '</p>';

The fault is innerHTML combined with unchecked input: the browser treats the text as HTML, so any tags in it become real elements. If an attacker submits <img src="x" onerror="alert('XSS')"> the image fails to load, the onerror code runs, and the same thing happens for every visitor who is shown the comment. This is a stored attack when the server saves the comment and serves it to everyone. It is a reflected attack when the input comes from the URL and is echoed straight back on the page. In place of an alert, real attacks try to read the page, submit forms as the user or steal cookies.

JavaScript: the fix, treat input as text
const comment = document.querySelector('#comment').value;
const p = document.createElement('p');
p.textContent = comment;                       // shown as text, never run as HTML
document.querySelector('#comments').appendChild(p);

On the server, the same defence is output encoding: turning characters such as < into &lt; before they go into a page. Template engines such as Jinja2 do it automatically for template variables, and Python's html.escape does it by hand:

Python: escaping user input
import html
print(html.escape("<script>alert(1)</script>"))
Output
&lt;script&gt;alert(1)&lt;/script&gt;

The browser now displays the characters instead of running them. Defence in depth adds validation on input (reject or limit what is not expected), a Content Security Policy header that stops inline scripts running, and HttpOnly cookies so a script cannot read the session ID even if one gets through. Compare this with SQL injection, which attacks the database using unchecked input; the parameterised queries in the SQL section are its defence.

Exam tip: "Explain" securing the web An explain answer links each mechanism to the problem it solves. For HTTPS, say that the certificate proves the site's identity (checked through the CA's digital signature), asymmetric encryption is used to agree a session key, and symmetric encryption then protects the data quickly. Use the words plain text and cipher text correctly, and never say a hash can be "decrypted". Keep authentication (who) and authorisation (what they may do) apart, and use an example. For XSS questions, identify the unsanitised input in the code fragment, say what an attacker could make it do, and name a fix such as textContent or output encoding. Common mistakes are saying HTTPS stops all attacks, and mixing up the public and private keys (the public key encrypts a message to its owner or verifies a signature; the private key decrypts or signs).

Big Data and Web Architecture

🎯 (SE-12-03, SE-12-05)

📌 Investigate the effect of big data on web architecture

  • data mining
  • metadata
  • streaming service management

Big data is usually described by three properties, the "three Vs": volume (too much to store on one server), velocity (arriving continuously, such as clicks and video views) and variety (text, images, video and logs together). A web application that must store and process data like this cannot rely on a single server and one database, so its architecture changes.

ConcernSmall web appBig-data web architecture
ServersOne web serverMany servers behind a load balancer, added as demand grows
StorageOne relational databaseData split across many machines, often with NoSQL stores and separate storage for media
DeliveryEverything from one placeCopies served from a CDN close to the user
ProcessingQueries run as requests arriveLogs and events collected continuously and analysed in batches or as streams
FailureThe site goes downOther servers take over

Data Mining

Data mining is the process of discovering patterns, correlations, and insights within large datasets using statistical and machine learning techniques. Web platforms mine user interaction data (clicks, search queries, time on page) to personalise recommendations, optimise advertising, and predict future behaviour. For example, Netflix mines viewing history to recommend shows; Google mines search patterns to rank results. Ethical concerns include user consent, data sovereignty, and the potential for discriminatory algorithmic outcomes.

Worked example: mining a pattern

GameShelf stores five customers' baskets. Data mining looks for items that appear together. Two measures are widely used: support (how many of all baskets contain both items) and confidence (of the baskets with the first item, how many also have the second).

Python: does buying Reef Runner predict buying Kelp Quest?
baskets = [
    {"Reef Runner", "Kelp Quest"},
    {"Reef Runner", "Kelp Quest", "Lava Lab"},
    {"Reef Runner", "Star Sailor"},
    {"Kelp Quest", "Lava Lab"},
    {"Reef Runner", "Kelp Quest"},
]
a, b = "Reef Runner", "Kelp Quest"
with_a = sum(a in basket for basket in baskets)
with_both = sum(a in basket and b in basket for basket in baskets)

print("baskets with", a + ":", with_a)
print("baskets with both:", with_both)
print("support:", with_both / len(baskets))
print("confidence:", with_both / with_a)
Output
baskets with Reef Runner: 4
baskets with both: 3
support: 0.6
confidence: 0.75

Three of the four Reef Runner buyers also bought Kelp Quest (confidence 0.75), so the site could suggest Kelp Quest on the Reef Runner page. Five baskets are far too few to trust; the same calculation on millions of baskets is what makes the pattern reliable, and why mining depends on big data. It also shows the ethical limit: the recommendation is useful, but the same method applied to health or location data could reveal things people never chose to share.

Metadata

Metadata is "data about data" — descriptive information that provides context about other data without being the primary content itself. On the web, metadata includes HTTP headers (content type, cache directives), HTML <meta> tags (page description, author, viewport settings), and file metadata (creation date, author, geolocation embedded in photos). Metadata is critical for search engine indexing (SEO), content management, and security analysis. Importantly, metadata can reveal sensitive information even when primary content is private — for example, a photo's EXIF data can expose a user's exact GPS location.

Worked example: metadata you can inspect

Metadata is often invisible but easy to find. Every HTTP response carries some, every page can carry more in its <head>, and every photo carries its own.

WhereExampleWhat it tells a system
HTTP headerContent-Type: image/webp, Cache-Control: max-age=3600How to treat the file and how long it can be cached
HTML <meta> tag<meta name="description" content="Browse Australian indie games">What a search engine can show under the page title
Photo file (EXIF)Camera model, time taken, GPS latitude and longitudeWhere and when the photo was taken, even though the image shows no map
Streaming serviceWhat you watched, when, on what device, how long before you stoppedYour habits, without reading any private message

HTTPS hides page content from people on the network, but the destination address, the size and timing of traffic are still visible. That is one reason metadata is sensitive: it can be collected in bulk and mined without anyone opening a single file. Under the Privacy Act 1988 and the Australian Privacy Principles, information about an identifiable person is personal information whether it is content or metadata, so a service must say what it collects and why.

Streaming Service Management

Streaming services (e.g., YouTube, Netflix, Spotify) manage enormous volumes of concurrent data delivery. Key architectural solutions include: Content Delivery Networks (CDNs) — geographically distributed server networks that cache and serve content from the nearest node to reduce latency; Adaptive Bitrate Streaming (ABR) — which dynamically adjusts video quality based on the user's current bandwidth; and load balancers that distribute incoming requests across multiple server instances to prevent any single server from being overwhelmed.

How a streaming service copes with millions of viewers

  1. Each video is encoded several times, at different quality levels, and cut into short segments of a few seconds. A small text file (a manifest) lists the segments for each quality.
  2. The segments are copied to CDN servers in many cities. When you press play, DNS or the CDN's routing sends you to a nearby one, which cuts latency and keeps traffic off the origin server.
  3. The player fetches segments one at a time and measures how quickly they arrive. This is adaptive bitrate streaming: if the connection slows it asks for the lower-quality version of the next segment, so the picture gets softer instead of freezing.
  4. A load balancer spreads requests for the site itself (search, login, recommendations) across many identical servers, and stops sending traffic to any that fail. When a popular show is released, more servers are started to cope.
  5. Viewing events (play, pause, stop) stream into storage where they are mined to make recommendations.
Exam tip: "Investigate" big data NESA's 2025 online paper included a stimulus question about metadata and data mining for a streaming service (one published exam, so an example of a style, not a prediction). An investigate answer needs a cause and an effect for each Including item: what is collected, how it is used and what the architecture or privacy consequence is. For example: "The service records what viewers watch (metadata). Mining it shows that viewers of one series also watch another, which powers recommendations, but the volume of data means it must be stored across many servers, and users' habits could be revealed, so consent and secure storage matter." Avoid answers that only define the terms.
HSC writing sample "Big data has changed web architecture from single servers to distributed systems. Streaming services such as Netflix store copies of popular shows on CDN servers close to viewers, so one data centre is not overloaded and playback starts quickly. However, mining viewing data to personalise recommendations raises privacy concerns, because users may not know how much of their behaviour is recorded."
Syllabus Part 2 Designing web applications

The Role of the W3C

🎯 (SE-12-05)

📌 Investigate and explain the role of the World Wide Web Consortium (W3C) in the development of applications for the web

  • Web Accessibility Initiative (WAI)
  • internationalisation
  • web security
  • privacy
  • machine-readable data

The World Wide Web Consortium (W3C) is an international community, led by its member organisations and founded by Tim Berners-Lee, that develops open standards for the web. A standard starts as a draft, is reviewed and tested by implementers, and only becomes a W3C Recommendation once it has been shown to work in more than one browser. The value to developers is that code written to a standard behaves the same way in every compliant browser, and that no single company controls how the web works. HTML and the DOM are now maintained as a "living standard" by the WHATWG community, which works with the W3C; CSS, WCAG, WebAuthn and many other web standards are W3C's own.

Web Accessibility Initiative (WAI)

Develops the Web Content Accessibility Guidelines (WCAG) — the international standard for making web content accessible to people with disabilities (visual, auditory, cognitive, motor). Compliance levels: A, AA (the level Australian government guidance expects), AAA. Practices include alternative text for images, keyboard-navigable interfaces, and sufficient colour contrast ratios.

Internationalisation (i18n)

Ensures web technologies support all languages, scripts, and cultures globally. This includes Unicode character encoding (UTF-8), right-to-left (RTL) text support for Arabic/Hebrew, locale-specific date/number formatting, and tools for translating and localising web content without changing the underlying code structure.

Web Security

The W3C develops security-related standards such as Content Security Policy (CSP) headers (which restrict the sources from which scripts, styles, and images can be loaded, mitigating XSS attacks), Subresource Integrity (SRI) (verifying third-party CDN resources haven't been tampered with), and the Web Authentication API (WebAuthn) for passwordless login.

Privacy

The W3C's Privacy Interest Group (PING) helps review new web specifications for privacy, and W3C specifications include privacy considerations. The aim is that new browser features cannot be used for fingerprinting or tracking users without consent. Replacing third-party cookies has been proposed by browser makers (for example Google's Privacy Sandbox) and debated in W3C groups. The Do Not Track signal was never widely honoured by websites, and its W3C working group has closed. Australian law adds its own duties under the Privacy Act 1988.

Machine-Readable Data

The W3C develops standards for structured, semantic data that machines (search engines, AI systems) can interpret. This includes Schema.org vocabulary embedded as JSON-LD or microdata in HTML (enabling rich search results), the Resource Description Framework (RDF), and Semantic HTML5 elements (<article>, <nav>, <section>) that provide meaning beyond visual presentation.

Worked example: is this text readable? (WAI and WCAG)

WCAG requires a contrast ratio of at least 4.5:1 for normal text at level AA (3:1 for large text). The ratio compares the brightness of the text and background colours, from 1:1 (identical) to 21:1 (black on white). This program follows the WCAG formula for relative luminance:

Python: checking WCAG contrast against a white background
def luminance(hex_colour):
    r, g, b = [int(hex_colour[i:i + 2], 16) / 255 for i in (1, 3, 5)]
    def channel(c):
        return c / 12.92 if c <= 0.03928 else ((c + 0.055) / 1.055) ** 2.4
    return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b)

def contrast(colour_a, colour_b):
    lighter, darker = sorted([luminance(colour_a), luminance(colour_b)], reverse=True)
    return (lighter + 0.05) / (darker + 0.05)

for colour in ["#000000", "#767676", "#999999", "#6366F1", "#4F46E5"]:
    ratio = contrast(colour, "#FFFFFF")
    print(colour, round(ratio, 2), "passes AA" if ratio >= 4.5 else "fails AA")
Output
#000000 21.0 passes AA
#767676 4.54 passes AA
#999999 2.85 fails AA
#6366F1 4.47 fails AA
#4F46E5 6.29 passes AA

The fourth line is a useful surprise: the popular indigo #6366F1 is 0.03 short of the AA threshold for normal text on white, so a slightly darker #4F46E5 (used in the CSS examples on this page) is the safer choice. Accessibility is decided by measurement, not by whether a colour "looks fine" to the person who chose it. WCAG is organised around four principles, often remembered as POUR: content must be perceivable, operable, understandable and robust.

Worked example: internationalisation and machine-readable data in the markup

HTML: language, encoding, text direction and structured data
<html lang="en-AU">
<head>
  <meta charset="utf-8">                 <!-- UTF-8 can represent every script -->
  <title>Reef Runner | GameShelf</title>
  <script type="application/ld+json">
  { "@context": "https://schema.org", "@type": "VideoGame",
    "name": "Reef Runner", "offers": { "@type": "Offer", "price": "25.00", "priceCurrency": "AUD" } }
  </script>
</head>
<body>
  <p lang="ar" dir="rtl">مرحبا</p>       <!-- Arabic, written right to left -->
</body>
</html>
  • The lang attribute tells screen readers which pronunciation rules to use and lets browsers offer translation.
  • UTF-8 and dir="rtl" let one page mix scripts correctly, which is internationalisation: designing so that translation (localisation) needs no code changes.
  • The JSON-LD block is machine-readable data: a search engine can read that this page is a video game costing 25.00 AUD without guessing from the layout.
W3C areaProblem it solvesDeveloper action
WAI / WCAGPeople are excluded by low contrast, missing alt text or mouse-only controlsContrast checks, alt text, keyboard operation, labels
InternationalisationText is garbled or reads the wrong way in other languagesUTF-8, lang, dir, no hard-coded formats
Web securityInjected scripts and tampered third-party filesContent Security Policy, Subresource Integrity, WebAuthn
PrivacyTracking and fingerprinting without consentMinimise data collected, respect browser privacy settings and consent
Machine-readable dataSoftware cannot tell what a page is aboutSemantic HTML, JSON-LD, Schema.org
Exam tip: "Investigate and explain" the W3C Answer in three moves: what the W3C does (publishes open standards that browsers implement), which Including item the question is about, and the effect on a developer or user, tied to a concrete practice such as alt text, lang, a Content Security Policy header or JSON-LD. A common error is to say the W3C "runs the internet" or "makes the browsers"; it does neither. Another is to say WCAG is only about blind users, when it also covers hearing, motor and cognitive needs.
HSC writing sample "The W3C shapes web applications by publishing open standards that every browser follows. Its Web Accessibility Initiative produces WCAG, which is why developers add alt text and keyboard navigation so people using screen readers can use a site. Standards for internationalisation, such as UTF-8, let one application display English, Arabic and Chinese correctly."

Modelling a Web Development System

🎯 (SE-12-02, SE-12-06)

📌 Model elements that form a web development system

  • client-side (front-end) web programming
  • server-side (back-end) web programming
  • interfacing with databases that are based on Structured Query Language (SQL) or non-SQL

Data flow diagram

Data flow diagram: a web application

How data moves between the user, the server-side processes and the database.

What to notice
  • The user is an external entity; the database is a data store.
  • Processes are numbered so they can be refined into lower-level DFDs.

Try thisAdd a process for logging in. Which data store would it read?

A web development system has four parts that pass data to each other. Keeping them separate lets each be changed, tested and secured on its own.

ElementRuns whereJobGameShelf example
Front endThe user's browserShow the interface and respond to the userHTML page, CSS layout, JavaScript that shows the price filter
APIThe boundary between the twoA defined set of URLs and message formats the front end can callGET /games?max_cost=30 returning JSON
Back endThe web serverApply rules, check who is asking, talk to the databaseA Python function that validates max_cost and runs a query
DatabaseA database server or fileStore data permanentlyThe Games, Publishers and Developers tables

Worked example: the lifecycle of one request

A visitor moves a slider to "under $30" on GameShelf. Follow the data through the model, and compare it with the data flow diagram above.

StepElementWhat happensData
1Front endA JavaScript event handler reads the slider value30
2Front end to APIThe browser sends an HTTP requestGET /games?max_cost=30
3Back endThe route function checks that 30 is a number and that the user may see this dataValidated max_cost
4Back end to databaseA parameterised query is runSELECT Name, Cost FROM Games WHERE Cost <= ? with 30
5Database to back endMatching rows are returnedKelp Quest, Reef Runner, Lava Lab
6Back end to APIThe rows are converted to JSON and sent with status 200[{"Name": "Kelp Quest", "Cost": 20.0}, ...]
7Front endJavaScript builds list items from the JSON and updates the pageThree list items in the DOM

The database is reached only in steps 4 and 5, and only by the back end. The browser has no direct route to it, so a curious user cannot send their own SQL. In DFD terms, the visitor is the external entity, "Validate request" and "Get games" are processes, and Games is the data store.

Client-Side (Front-End) Web Programming

The client side refers to all code that executes within the user's web browser. Front-end code consists of three complementary technologies: HTML (structure and content), CSS (visual presentation and layout), and JavaScript (interactivity and dynamic behaviour). The browser downloads these files from the server, parses them, and renders the visual page. Client-side code is visible to the user (via browser dev tools) and should never contain sensitive data such as API keys or database credentials.

Server-Side (Back-End) Web Programming

The server side refers to code that executes on the web server — invisible to the client. The server receives HTTP requests, applies business logic (authentication, data processing, calculations), queries the database, and returns a response (HTML page or JSON data). Common back-end languages include Python (Django, Flask), JavaScript (Node.js/Express), Ruby (Rails), and PHP. The server environment is protected from end-users and may contain sensitive configuration, credentials, and proprietary algorithms.

Databases — SQL and Non-SQL

SQL (Relational) Databases

Store data in structured tables with rows and columns. Relationships between tables are defined by primary and foreign keys. Enforce data integrity via ACID transactions. Examples: PostgreSQL, MySQL, SQLite. Best for: structured, consistent data with complex relationships (e.g., e-commerce orders, user accounts).

Non-SQL (NoSQL) Databases

Store data in flexible formats — documents (JSON), key-value pairs, graphs, or column families. Schema-free, allowing rapid iteration. Examples: MongoDB (documents), Redis (key-value), Neo4j (graphs). Best for: unstructured or evolving data (e.g., social media posts, real-time analytics, content catalogues).

APIs: how the layers talk

An API (application programming interface) is the contract between front end and back end. Web APIs commonly follow REST conventions: each resource has a URL, and the HTTP method says what to do with it. Data usually travels as JSON.

ActionMethod and URLBodySuccess status
List gamesGET /gamesnone200
Read one gameGET /games/4none200 (404 if absent)
Create a gamePOST /gamesJSON for the new game201 Created
Change a gamePUT /games/4JSON with the changes200
Delete a gameDELETE /games/4none204 No Content
JavaScript (front end): call the API and update the page
async function showGames(maxCost) {
  const response = await fetch('/games?max_cost=' + encodeURIComponent(maxCost));
  if (!response.ok) {                        // 400, 404, 500 ...
    document.querySelector('#status').textContent = 'Could not load games';
    return;
  }
  const games = await response.json();        // parse the JSON body
  const list = document.querySelector('#game-list');
  list.replaceChildren();                     // clear the old items
  games.forEach(game => {
    const li = document.createElement('li');
    li.textContent = game.Name + ' ($' + game.Cost.toFixed(2) + ')';
    list.appendChild(li);
  });
}
showGames(30);

With the running example's data, the list would show Kelp Quest ($20.00), Reef Runner ($25.00) and Lava Lab ($30.00). The matching back-end route is shown in the back-end request section. Note textContent again: the data is displayed as text, so a game called <b> cannot inject markup.

Worked example: the same data in SQL and non-SQL databases

The relational design splits one game across three tables and joins them when needed. A document database keeps everything about a game together in one record.

Non-SQL (document): one game stored as one JSON document
{
  "id": 4,
  "name": "Star Sailor",
  "release_date": "2023-03-20",
  "cost": 45.00,
  "publisher": { "name": "Pixel Harbour" },
  "developer": { "first_name": "Ava", "last_name": "Nguyen" }
}
SQL (relational)Non-SQL (document)
StructureFixed columns; every row has the same fieldsFlexible; each document may have different fields
Publisher's name changesUpdate one row; every game sees the change through the foreign keyThe name is copied into every game document, so every one must be updated
Show a game with its publisherA join across tablesOne read of one document
Best suited toRelated, consistent data such as orders and accountsVaried or fast-changing data such as activity feeds and catalogues

Neither is "better". The choice depends on the data's shape and how it is used, and many systems use both.

Exam tip: "Model" a web system To model is to represent how the parts fit together, usually with the Course Specifications' data flow diagram notation: external entities as rectangles, processes as circles, data stores as open-ended rectangles and labelled flows. Label every flow with the data it carries, keep the database behind the back-end processes, and never draw a flow directly from the user to a data store. Written answers should say which code runs in the browser and which on the server, and why sensitive rules (prices, permissions) belong on the server.

Web Browsers and Developer Tools

🎯 (SE-12-03, SE-12-06)

📌 Explore and explain the influence of a web browser on web development, including the use of developer (dev) tools

Your code only works if the browser runs it the way you expect. Browsers use different engines, support features at different times and handle errors differently, so developers test in more than one and use the browser's built-in developer tools to find out what is really happening.

Browser Rendering Engines

Different browsers use different rendering engines to parse HTML/CSS and render the visual page: Blink (Chrome, Edge, Opera), WebKit (Safari), and Gecko (Firefox). Differences in engine implementations can cause the same CSS to render slightly differently across browsers — a core challenge of front-end development known as cross-browser compatibility. Developers use tools like Can I Use (caniuse.com) to check which CSS/JavaScript features are supported across browser versions before using them in production.

Whatever the engine, the browser follows the same pipeline, and knowing it explains many bugs and slow pages:

  1. It parses the HTML into the DOM (a tree of elements) and the CSS into a matching set of style rules.
  2. It combines them into a render tree of the elements that will actually be shown (an element with display: none is left out).
  3. It works out the layout: the size and position of every box.
  4. It paints the pixels to the screen.

A script that changes the DOM or CSS forces some of these steps to be done again, and a script or stylesheet in the <head> can hold up the first paint. Browsers also add new features at different times, so a developer either checks support on Can I Use or tests for the feature before relying on it:

JavaScript: feature detection with a fallback
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js');       // browser supports service workers
} else {
  console.log('No service worker support: the site still works online.');
}

In CSS the equivalent is the @supports rule. The principle is progressive enhancement: build a working baseline for every browser, then add newer features where they are available.

Developer (Dev) Tools

All modern browsers include built-in developer tools (opened with F12 or right-click → Inspect) that allow engineers to inspect, debug, and optimise web applications in real time:

Elements PanelInspect and live-edit the HTML DOM tree and CSS styles. See computed styles, box model, and layout information.
ConsoleExecute JavaScript, view runtime errors, warnings, and console.log() output for debugging.
Network TabMonitor all HTTP requests made by the page — see request/response headers, payload data, status codes, and load times.
PerformanceRecord and analyse page load and runtime performance — identify JavaScript bottlenecks and rendering slowdowns.
LighthouseAutomated audit tool that scores a page on Performance, Accessibility, SEO, and PWA compliance with actionable recommendations.
Device ModeSimulate different screen sizes and device pixel ratios to test responsive design without a physical device.
Scenario A form's "Submit" button does nothing on a phone. In dev tools, the Console shows a JavaScript error, the Network tab shows no request was sent, and Device Mode reproduces the bug on a narrow screen. The cause: a hidden element covers the button at small widths, and one CSS change fixes it.

Hands-on walkthrough: five minutes in the dev tools

Do this on any page, including your own school's public website. Your changes affect only your copy in your browser and disappear when you reload, so you cannot break the real site. The panel names below are Chrome's; Firefox's are close (Inspector for Elements, Debugger for Sources, Storage for Application).

  1. Elements. Right-click a heading and choose Inspect. Double-click its text in the tree and type something new: the page changes instantly. In the Styles pane, untick a color rule and watch it change back. The Computed tab shows the final value of each property after the cascade. Expected result: the page looks different, but reloading restores it.
  2. Console. Type document.title and press Enter: it prints the page title. Type document.querySelectorAll('img').length for the number of images. Broken JavaScript shows up here as a red error message with a file name and line number, which is where debugging starts.
  3. Network. With the panel open, reload the page. Each row is one request: name, status code, type, size and time. Click a row to see the request and response headers. Filter by Fetch/XHR to see API calls only. Tick Disable cache and reload to see the cost of a first visit, then untick it and reload to see cached files served from disk. Expected result: repeat loads show fewer bytes transferred; a failed request shows a 4xx or 5xx status in red.
  4. Throttling and Device Mode. Choose a slow connection preset in the Network panel, then switch on Device Mode and pick a phone. Reload. Expected result: the waterfall stretches out and you can see which files delay the page.
  5. Performance. Click Record, reload, then stop. The timeline shows when the page first painted, when scripts ran and any long tasks that froze the page. Metrics such as Largest Contentful Paint are marked on it.
  6. Application. Under Cookies, find the session cookie set by a site you are logged into and read its flags: Secure, HttpOnly and SameSite. Under Service Workers you can see, unregister or bypass a PWA's worker.
  7. Lighthouse. Run an audit and read the scores and the list of suggestions, such as images that are too large or missing alt text.
PanelQuestion it answers
ElementsWhat HTML and CSS is the browser really using for this element?
ConsoleDid my script fail, and where?
SourcesWhat is my code doing line by line? (breakpoints and stepping)
NetworkWhich requests were made, how big and how slow, and what did the server reply?
PerformanceWhere did the time go: loading, scripting, layout or painting?
ApplicationWhat is stored in the browser: cookies, local storage, caches, service workers?
LighthouseHow does the page score on performance, accessibility, SEO and PWA checks?

The dev tools show you what the browser received, so they are also a reminder of the security rule from the front-end section: nothing in the browser can be trusted. A user can edit a price in the Elements panel or replay a request from the Network panel with different values, which is why the server must check everything again.

Exam tip: "Explore and explain" the browser's influence Give two kinds of influence: browsers differ (rendering engines, support for features, so developers test in several and use feature detection) and browsers provide tools (the dev tools). For each tool, name the panel, say what it shows and give a debugging use tied to the scenario, such as "the Network tab shows a 404 for the stylesheet, so the CSS path is wrong". Answers that only list the panels without a use score little.

Cascading Style Sheets (CSS)

🎯 (SE-12-02, SE-12-06)

📌 Investigate cascading style sheets (CSS) and its impact on the design of a web application

  • consistency of appearance
  • flexibility with browsers or display devices
  • CSS maintenance tools

Consistency of Appearance

CSS enables a single stylesheet to control the visual appearance of every page across an entire website. CSS Custom Properties (variables) allow designers to define a value once (e.g., --brand-colour: #4F46E5) and reuse it throughout, ensuring colour, typography, and spacing remain consistent. Changing a single variable instantly updates every element that references it. Design systems (such as Google's Material Design or Apple's Human Interface Guidelines) are implemented through CSS to enforce consistent visual language across large products.

CSS — Variables and Responsive Design
/* Define design tokens as CSS variables */
:root {
    --primary-colour: #4F46E5;
    --font-body: 'Inter', sans-serif;
    --spacing-md: 1rem;
}

/* Apply consistently across components */
.btn-primary {
    background: var(--primary-colour);
    font-family: var(--font-body);
    padding: var(--spacing-md) calc(var(--spacing-md) * 2);
}

/* Media query for responsive design (flexibility with devices) */
@media (max-width: 768px) {
    .grid-3col { grid-template-columns: 1fr; }
}

Reading CSS and HTML together (Course Specifications)

The Course Specifications expect you to interpret code fragments in CSS and HTML. A rule is a selector that picks elements, followed by braces holding property: value; declarations, and /* … */ is a comment. Here is the Specifications' style of example, with what the browser does with it.

HTML fragment
<html>
<head><title>My Website</title></head>
<body>
<h1>Welcome!</h1>
<p id="welcome">Welcome to my website!</p>
<p class="red-text">This text should be red</p>
<p>My website also has <span class="red-text">red text</span> here</p>
</body>
</html>
CSS
/* This selector targets an HTML element */
h1 {
  font-size: 18px;
}

/* This selector targets an HTML element with a specific "id" */
#welcome {
  font-style: italic;
}

/* This selector targets an HTML element with a specific "class" */
.red-text {
  color: red;
}
  • h1 matches the heading only, so "Welcome!" is drawn at 18px.
  • #welcome matches the one element with that id, so only the first paragraph is italic. An id must be unique on a page.
  • .red-text matches every element with that class, so the second paragraph and the word "red text" inside the third paragraph are red, but the rest of the third paragraph is not.
SelectorMatchesExample
Element (type)Every element of that namep
ClassElements with that class (reusable).red-text
IDThe one element with that id#welcome
DescendantAn element anywhere inside anotherbody p
CombinedBoth conditions on one elementp.red-text
GroupSeveral selectors sharing one blockh1, h2 { … }
Pseudo-classAn element in a particular statea:hover

The cascade and specificity

Several rules can set the same property on the same element, so the browser needs a rule for choosing. That rule is the cascade. It compares, in order: (1) importance (a declaration marked !important beats one that is not), (2) whether the style is inline (in the element's own style attribute), (3) specificity, and (4) source order, where the later rule wins a tie. A selector's specificity is three counts read left to right: IDs, then classes (with attributes and pseudo-classes), then element names. Compare column by column, starting at the left; a higher number in an earlier column wins no matter what is to its right.

SelectorIDsClassesElementsSpecificity
p001(0, 0, 1)
body p002(0, 0, 2)
.red-text010(0, 1, 0)
p.red-text011(0, 1, 1)
#welcome100(1, 0, 0)
nav ul li a:hover014(0, 1, 4)

Worked examples: which colour wins?

ElementCompeting rulesWinner and reason
<p class="red-text">p { color: black; } then .red-text { color: red; }Red: (0, 1, 0) beats (0, 0, 1). Order does not matter here.
<p class="red-text">.red-text { color: red; } then p.red-text { color: blue; }Blue: (0, 1, 1) beats (0, 1, 0).
<p id="welcome" class="red-text">#welcome { color: green; } and p.red-text { color: blue; }Green: one ID (1, 0, 0) beats any number of classes (0, 1, 1).
<p class="red-text big">.red-text { color: red; } then .big { color: black; }Black: both are (0, 1, 0), so the later rule wins.
<p id="welcome">#welcome { color: green; } and p { color: black !important; }Black: !important is compared first. Use it sparingly; it makes styles hard to override.
<span class="red-text"> inside <p>p { color: black; } onlyBlack: color is inherited, so the span takes its parent's value when no rule targets it directly. A rule aimed at the span itself always beats an inherited value.

CSS specificity and cascade explorer

Count a selector's specificity, compare two selectors, then switch rules on and off and see which declaration wins.

The box model

Every element is drawn as a box made of four layers: content at the centre, then padding (space inside the border), the border and the margin (space outside). Most layout bugs are box-model bugs, and the Elements panel draws the four layers in colour for the selected element.

CSS: the layers of a box
.card {
  width: 200px;             /* content width */
  padding: 10px;            /* inside the border */
  border: 2px solid #333;
  margin: 20px;             /* outside the border */
  box-sizing: content-box;  /* the default: width is the content only */
}
/* Total width taken on the page: 200 + 2*10 + 2*2 + 2*20 = 264px.
   With box-sizing: border-box, width: 200px would include padding and border. */

Flexibility with Browsers and Display Devices

Responsive web design ensures a single codebase adapts to any screen size — from a 27-inch desktop monitor to a 375px mobile screen. CSS media queries apply different styles at defined breakpoints. Flexbox and CSS Grid provide flexible layout systems that redistribute space and reflow content automatically. The viewport meta tag instructs the browser to scale the page to the device's width, preventing the "zoomed-out desktop site" appearance on mobile.

Try it

Responsive layout explorer

Change the screen width and see which media queries apply. Enable JavaScript to use this tool. In short: base rules fit a phone and min-width media queries add columns and a sidebar as the screen gets wider.

Worked example: one layout, three screen sizes

Without the viewport meta tag, phones pretend to be about 980px wide and shrink the page. With it, the layout can respond to the real width. This code puts three cards in a row on wide screens and stacks them on narrow ones. It uses flexbox, relative units and one media query, with no separate mobile site.

HTML: tell the browser to use the device width
<meta name="viewport" content="width=device-width, initial-scale=1">
CSS: a flexible row that stacks on narrow screens
.cards {
  display: flex;
  flex-wrap: wrap;           /* let items drop to the next line */
  gap: 1rem;
}
.card {
  flex: 1 1 250px;           /* grow, shrink, and start at 250px wide */
  font-size: 1rem;           /* rem scales with the user's font setting */
}
@media (max-width: 600px) {
  .card { flex-basis: 100%; }   /* one card per row on phones */
}
img { max-width: 100%; height: auto; }   /* pictures never overflow their box */
  • At about 1000px wide, three 250px cards fit on one row and share the leftover space.
  • At 390px (a common phone width), only one card fits per line, so they stack; the media query makes this explicit for widths up to 600px.
  • Percentages, rem and max-width make sizes relative, so they adapt to the browser window and to users who enlarge their text. Fixed pixel widths do not.

For flexibility across browsers, developers write standard CSS, check support (Can I Use, @supports) and test in more than one engine. For flexibility across display devices, they also design for touch (tap targets of at least about 44px), for high-resolution screens (images supplied at more than one size with srcset) and for user preferences such as prefers-color-scheme and prefers-reduced-motion.

CSS Maintenance Tools

Sass / SCSSA CSS preprocessor that adds variables, nesting, mixins, and functions to CSS, then compiles to standard CSS. Greatly improves maintainability of large stylesheets.
PostCSSA tool that transforms CSS via JavaScript plugins — can auto-prefix vendor prefixes (autoprefixer), lint CSS, and minify output for production.
CSS-in-JSLibraries (e.g., Styled Components, Emotion) that write CSS directly inside JavaScript components, scoping styles automatically and enabling dynamic theming.

Worked example: a maintenance tool at work

Sass lets you nest rules and reuse values; a build step compiles it to plain CSS that browsers understand. If the brand colour changes, you edit one variable.

SCSS source (compiled by Sass)
$brand: #4F46E5;

.nav {
  background: $brand;
  a { color: white; }
  a:hover { text-decoration: underline; }
}
Compiled CSS that the browser receives
.nav { background: #4F46E5; }
.nav a { color: white; }
.nav a:hover { text-decoration: underline; }

Other tools work on the finished CSS: a linter (such as Stylelint) flags mistakes and inconsistent style, and a minifier removes whitespace and comments to cut file size for production. Plain CSS custom properties, shown earlier, now cover much of what Sass variables were used for, without a build step.

Exam tip: "Investigate" CSS Two kinds of question are likely: interpret a code fragment ("which text is red?" or "which rule applies?") and explain benefits ("how does CSS make a site consistent and adaptable?"). For fragments, match each selector to the elements it targets, then apply the cascade: importance, specificity, then source order; show the specificity numbers. For benefits, name a feature and its effect: one shared stylesheet or custom properties give consistency, media queries and flexible units give flexibility across devices, and tools such as Sass or a minifier give maintainability. Remember that a class selector targets elements with that class (.red-text) and an ID selector one element (#welcome); swapping them is the most common slip.

Version Control

🎯 (SE-12-09)

📌 Investigate the reasons for version control and apply it when developing web application

Web projects change constantly, often with several people editing the same files. Version control records every change so you can see what changed, undo mistakes and work on new features without breaking the live site.

ReasonWhat goes wrong without it
History and rollbackA bad change reaches the live site and nobody can tell what the working version was.
BranchingHalf-finished features are mixed into the code everyone relies on.
CollaborationTwo developers overwrite each other's edits to the same stylesheet.
AccountabilityNo record of who changed the login code, or why.

Version control systems (VCS) track every change made to a codebase over time, enabling developers to: roll back to any previous working state if a new change breaks functionality; branch the codebase to develop features or fix bugs in isolation without affecting the main production code; merge contributions from multiple developers working simultaneously; and maintain a complete audit trail of who changed what and why. Git is the dominant distributed VCS; GitHub, GitLab, and Bitbucket are the popular remote hosting platforms.

Shell — Core Git Workflow for Web Development
# Initialise a new repository
git init

# Create a feature branch (isolates work from main; "git switch -c" is the newer equivalent)
git checkout -b feature/user-login

# Stage changed files
git add index.html styles.css app.js

# Commit with a descriptive message
git commit -m "Add user login form with client-side validation"

# Push branch to remote repository (GitHub)
git push origin feature/user-login

# Merge feature into main once reviewed
git checkout main
git merge feature/user-login

Worked example: a branch, a merge and a conflict

Ava and Ben are building the GameShelf site. This sequence uses real Git output (your commit codes will differ, because each is calculated from the content, author and time). First Ava tells Git who she is once, then creates the project:

Shell: set up a repository and make the first commit
git config --global user.name "Ava"
git config --global user.email "ava@example.com"

git init -b main                       # start tracking this folder; the first branch is main
git add index.html styles.css          # stage the files for the next commit
git commit -m "Add home page and styles"
git log --oneline
Output of git log --oneline
68ffb32 Add home page and styles

Ava now works on a search box in her own branch, so that main always holds working code. Meanwhile Ben adds an about page directly on main (in a team this would normally also be a branch and pull request).

Shell: branch, commit twice, then Ben commits on main
git switch -c feature/search           # create and move to a new branch
# ... edit index.html ...
git add index.html
git commit -m "Add search box"
# ... edit styles.css ...
git commit -am "Style the search box"  # -a stages changes to files Git already tracks

git switch main
# ... Ben adds about.html ...
git add about.html
git commit -m "Add about page"
git log --oneline --graph --all
Output of git log --oneline --graph --all
* 0408178 Style the search box
* 6a62b66 Add search box
| * a2ef249 Add about page
|/  
* 68ffb32 Add home page and styles

The graph shows the two lines of work splitting at the first commit. The search branch has two commits that main does not have, and main has one that the branch does not. Because they changed different files, Git can combine them automatically:

Shell: merge the feature into main
git merge feature/search -m "Merge feature/search"
git log --oneline --graph
git branch -d feature/search           # the branch has done its job
Output
Merge made by the 'ort' strategy.
 index.html | 1 +
 styles.css | 1 +
 2 files changed, 2 insertions(+)

*   22ae2e4 Merge feature/search
|\  
| * 0408178 Style the search box
| * 6a62b66 Add search box
* | a2ef249 Add about page
|/  
* 68ffb32 Add home page and styles
Deleted branch feature/search (was 0408178).

Diagram

The branch and merge above, as a picture

Each circle is a commit; the merge commit M has two parents and joins the two lines of work.

What to notice
  • Both branches start from the same commit, so nothing is lost or copied twice.
  • The merge commit records that two histories were combined, which is why git log --graph shows a diamond.

Try thisRedraw the diagram if Ben had also added a commit on main after Ava's merge. Where would C5 go?

When two people change the same line: a merge conflict

Now suppose Ava renames the heading in a branch and Ben renames the same line differently on main. Git cannot know which is right, so it stops the merge and marks the file:

Output of git merge feature/title
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
index.html with conflict markers
<<<<<<< HEAD
<h1>Game Shelf</h1>
=======
<h1>GameShelf Store</h1>
>>>>>>> feature/title
<input id="search" type="search">
<p>Browse games.</p>
  1. The section between <<<<<<< HEAD and ======= is what the current branch (main) has; the section below it is the incoming branch's version.
  2. Ava and Ben talk, agree on "GameShelf Store", and edit the file so only that line remains, deleting the three marker lines.
  3. They run git add index.html, then git commit to finish the merge. git status shows the file as unmerged (UU) until this is done.

Conflicts are normal, and small, frequent merges keep them small. They are a human decision, not a failure of the tool, and the fix is a conversation as much as an edit.

Try it

Git sandbox

Commit, branch and merge, and see a merge conflict. Enable JavaScript to use this tool. In short: a branch is a label on a commit, a merge joins two branches, and a conflict happens when both changed the same file.

Working with a remote and other habits

Command or practicePurpose
git clone URLCopy a remote repository, with its whole history, to your computer
git push origin feature/searchSend your branch's commits to the shared remote (GitHub, GitLab, Bitbucket)
git pullFetch others' commits and merge them into your branch; do it before starting work each day
Pull requestA request on the hosting platform to merge a branch, with a place for review and comments
git revert <commit>Undo a commit by making a new commit that reverses it, keeping history honest
.gitignoreLists files never to commit, such as node_modules/, .env and *.log
Commit messagesShort, in the imperative ("Add search box"), one change per commit, so history reads as a story

Never commit passwords, API keys or database credentials. Once pushed, they remain in the history even if the file is later deleted, so a leaked key must be cancelled and replaced. Git is the tool; GitHub is one website that hosts Git repositories.

Exam tip: "Investigate" and "apply" version control Give reasons and show use. Reasons: history and rollback, branching to isolate work, merging and collaboration, an audit trail. Application: describe a specific sequence, such as "create feature/login, commit the form, push it, open a pull request for review, then merge into main". If asked what happens on a conflict, say that two changes touched the same lines, Git marks the file, and developers choose the final content, then commit. Do not confuse Git (the version control system) with GitHub (a hosting service).

Code Libraries for Front-End Development

🎯 (SE-12-06)

📌 Explore the types and significance of code libraries for front-end web development

  • frameworks that control complex web applications
  • template engines
  • predesigned CSS classes

A code library is pre-written code you can reuse instead of writing everything yourself. Front-end developers rely on three main kinds, which save time and make large applications more consistent.

Frameworks for Complex Web Applications

Front-end frameworks provide a structured architecture for building large, complex web applications. They enforce separation of concerns (UI components, state management, routing) and provide build tools that optimise code for production.

ReactA component-based UI library by Meta. Uses a virtual DOM for efficient re-rendering. One of the most widely used front-end libraries.
Vue.jsA progressive framework with a gentle learning curve. Combines a template-based syntax with reactive data binding. Popular for smaller to medium-scale applications.
AngularA full-featured framework by Google. Uses TypeScript, has built-in routing, forms, and HTTP client. Common in large enterprise applications.

Why frameworks are useful

The Course Specifications say students should understand why front-end frameworks are useful; they are not expected to know or code in any particular one. The reasons apply to all of them:

  • Components: the interface is built from small reusable pieces (a card, a menu, a button), each with its own markup, style and behaviour, so a change is made once.
  • Automatic updates: the developer describes what the page should look like for the current data, and the framework updates the DOM when the data changes. This removes a large amount of hand-written DOM code.
  • Structure: conventions for routing (which component shows for which URL), state (where shared data lives) and folders let teams work on one large code base without clashing.
  • Tooling: build steps bundle, minify and split code for fast delivery, and there are testing and debugging tools and a large community.

The difference is easiest to see side by side. Both fragments show a list of games, but only the second keeps itself up to date:

Without a framework: the developer updates the DOM by hand
function addGame(name) {
  games.push(name);
  const li = document.createElement('li');
  li.textContent = name;
  document.querySelector('#game-list').appendChild(li);   // must remember to update the page
}
With a framework (Vue-style template, illustrative)
<ul>
  <li v-for="game in games">{{ game }}</li>      <!-- the framework redraws when games changes -->
</ul>

The costs are real: a learning curve, extra code to download (which slows loading), a build process, and dependence on a project that must be kept up to date.

Template Engines and CSS Frameworks

Template engines allow developers to inject dynamic data into HTML templates on the server before sending the page to the client, avoiding duplicate HTML markup. Template engines support variables, control structures (loops, conditionals), and template inheritance.

A worked template example uses Jinja2, the template engine used by Flask. The template is one HTML layout with placeholders; the Python code supplies the data.

Python: filling a template with data
from jinja2 import Template

page = Template("""<ul>
{% for g in games %}  <li>{{ g.name }} - ${{ "%.2f"|format(g.cost) }}{% if g.cost > 40 %} (premium){% endif %}</li>
{% endfor %}</ul>""")

print(page.render(games=[{"name": "Kelp Quest", "cost": 20},
                         {"name": "Star Sailor", "cost": 45}]))
Output
<ul>
  <li>Kelp Quest - $20.00</li>
  <li>Star Sailor - $45.00 (premium)</li>
</ul>

The {% %} blocks are control structures (a loop and a condition) and {{ }} inserts a value. Because the layout is written once, the header and navigation can be shared by every page through template inheritance. Flask switches on escaping for its HTML templates (the plain Template class above does not), so a value such as <script> is displayed rather than run, which links this section to cross-site scripting.

CSS frameworks provide pre-built components and responsive utility classes to accelerate styling and ensure consistent design across pages. They typically include grid systems, responsive breakpoints, and pre-styled UI components.

Predesigned CSS classes work the other way: you write almost no CSS and instead add ready-made class names to the HTML.

HTML: Bootstrap-style predesigned classes (illustrative)
<div class="container">
  <div class="row">
    <div class="col-12 col-md-4"><button class="btn btn-primary">Buy</button></div>
  </div>
</div>

Here container, row and col-* make a responsive grid (a full-width column on phones, one third of the row from medium screens), and btn btn-primary gives a styled button. The trade-off is that sites built with the same framework can look alike, and the whole stylesheet is downloaded even if only a few classes are used.

TypeExamplesWhy it matters
FrameworksReact, Vue, AngularOrganise complex interfaces into reusable components that update automatically when data changes
Template enginesJinja2, Handlebars, EJSFill one HTML layout with different data, so pages share a structure without copy-and-paste
Predesigned CSS classesBootstrap, Tailwind CSSGive consistent, responsive styling by adding class names such as btn btn-primary
HSC writing sample "Code libraries are significant because they let a small team build a professional interface quickly. Bootstrap's predesigned classes made the booking site responsive without custom CSS, and Jinja2 templates meant the header and navigation were written once. The trade-off is dependence on third-party code, which must be kept up to date for security."
ConsiderationBenefit of a libraryRisk or cost
Development timeReady-made components and layoutsTime to learn the library first
ConsistencyShared classes and components look and behave the sameSites can look generic
PerformanceOptimised, widely tested code and build toolsMore code to download and run
Security and upkeepFixes arrive as updatesOutdated versions carry known vulnerabilities; updates can break code
IndependenceLarge communities and documentationDependence on a third party that may stop maintaining it

When a library is loaded from a CDN, the integrity attribute (Subresource Integrity) lets the browser check that the file is unchanged: <script src="https://cdn.example/lib.js" integrity="sha384-…" crossorigin="anonymous"></script>, where the value is a hash of the expected file. If the hash does not match, the browser refuses to run it.

Exam tip: "Explore" code libraries Name the type (framework, template engine or predesigned CSS classes), say what problem it solves and give one trade-off. You do not need to know a framework's syntax: the Course Specifications say students are not expected to know or code in a specific framework. Write about frameworks in general terms (components, automatic DOM updates, structure). A frequent mistake is to describe a framework as a programming language, or to claim that using one always makes a page faster.

Open-Source Software

🎯 (SE-12-05)

📌 Explain the use and development of open-source software in relation to web development

Much of the modern web is built on open-source software. Web servers (Apache, Nginx), programming languages (Python, Node.js), frameworks (React, Django), and databases (PostgreSQL, MySQL) are all open source. The NPM (Node Package Manager) ecosystem alone hosts millions of open-source packages that developers can import with a single command (npm install package-name).

"Open source" means the source code is published under a licence that lets others read, use, modify and share it. It is not the same as "free of charge" (many open-source products are sold as services), and it is not the same as "public domain": the author still holds the copyright, and the licence is what grants the permissions. In Australia the licence works because of the Copyright Act 1968, which gives the author exclusive rights that the licence then shares on stated conditions.

Comparing the licences

LicenceTypeYou mayYou mustTypical use
MITPermissiveUse, change and sell it, including in closed productsKeep the copyright and licence noticeMany JavaScript libraries
Apache 2.0PermissiveThe same, plus an explicit patent grantKeep notices and state significant changesLarger corporate projects
GPL v3CopyleftUse and change it, and sell copiesRelease your version, if you distribute it, under the GPL with source codePrograms whose authors want improvements to stay open
ProprietaryClosedUse it as the licence allowsPay any fees and follow the terms; no source accessCommercial software

The GPL's requirement is triggered by distributing the combined program. Software that only runs on your own server and is used over the web is not distributed, which is why some projects use the AGPL, a variant that also covers network use. When in doubt, read the licence or ask; that is a professional habit, not a legal technicality.

Worked example: can GameShelf use these?

SituationDecisionReason
The paid mobile app bundles an MIT-licensed date libraryAllowedKeep the library's copyright notice in an "about" or licences screen
The team wants to ship a closed-source desktop version containing GPL v3 codeNot without releasing that code under the GPLDistributing a combined program triggers the copyleft requirement; the team could instead choose a permissively licensed alternative
A developer copies a function from a forum post with no licence statedDo not use itNo licence means no permission: copyright still applies
The site uses a library that has had no update for several yearsReviewThe licence allows it, but the security risk of an unmaintained dependency may not

How open-source software is developed

  1. A maintainer publishes the code in a public repository with a licence, a README and instructions for contributing.
  2. Users report bugs and suggest features as issues.
  3. A contributor forks (copies) the repository, creates a branch, makes a change, and opens a pull request back to the original.
  4. Maintainers and other contributors review the change, request edits, and merge it if it meets the project's standards and tests.
  5. New versions are released, and users update their dependencies.

This is the same Git workflow as the version-control section, applied across a community. The benefits are low cost, many reviewers, fast fixes and no lock-in to one supplier. The costs are uneven quality, projects that are abandoned, and the fact that a bug in a popular component affects everyone who uses it. The Heartbleed flaw of 2014, a bug in the widely used OpenSSL library, is a documented example of both: a large share of the web's servers depended on one small project.

Security dependency risk and managing dependencies

Relying on open-source libraries introduces a supply chain security risk: if a maintainer introduces a vulnerability (accidentally or maliciously), every project that depends on it is affected. Engineers should regularly check for known vulnerabilities in dependencies, update promptly, and include only libraries with active maintenance and community trust.

package.json: a project declares what it depends on
{
  "name": "gameshelf",
  "version": "1.0.0",
  "dependencies": {
    "express": "^4.19.0",
    "dompurify": "^3.1.0"
  }
}
  • npm install downloads each dependency and the dependencies of those, so a handful of declared packages can mean hundreds of installed ones.
  • A lock file records the exact versions installed, so every developer and the live server run identical code.
  • npm audit compares the installed versions against a database of known vulnerabilities; running it regularly, and updating, is the practical defence against supply-chain risk.
  • Prefer packages with recent releases, active maintainers and a clear licence, and remove ones you no longer use.
Exam tip: "Explain" open-source software Cover both halves of the dot point: use (licences and their conditions, saving cost and time, dependency risks) and development (community contribution through forks, pull requests and review). Give one benefit and one risk, each with an example. When licences come up, say what you must do (for example keep a notice with MIT; release source under the GPL if you distribute) rather than simply naming them. Do not say open source means "no rules" or "free of copyright".

Managing Load Times

🎯 (SE-12-07, SE-12-08)

📌 Investigate methods to support and manage the load times of web pages/applications

Minification & Compression

Minification removes whitespace, comments, and unused code from HTML/CSS/JS files, reducing file size. GZIP/Brotli compression further compresses files before transmission — the server compresses the file, the browser decompresses it. Text files such as HTML, CSS and JavaScript compress very well, often to a fraction of their original size.

Caching

HTTP Cache-Control headers instruct the browser to store static assets (images, CSS, JS) locally for a defined period. On repeat visits, cached files load instantly without a server request. Service workers in PWAs enable advanced programmatic caching strategies.

Content Delivery Networks (CDN)

A CDN distributes static assets across servers in multiple geographic locations. Users receive files from the nearest server, reducing latency. Popular CDNs include Cloudflare, AWS CloudFront, and Fastly.

Lazy Loading

Images and heavy components are only loaded when they enter the user's viewport, rather than all at once on page load. Implemented with the loading="lazy" HTML attribute or JavaScript Intersection Observer API. Significantly reduces initial page weight.

Code Splitting

Bundlers (Webpack, Vite) split a large JavaScript application into smaller chunks that are only loaded when needed. A user visiting the home page doesn't need to download the JavaScript for the account settings page until they navigate there.

Core Web Vitals

Google's set of real-world performance metrics: LCP (Largest Contentful Paint — how fast main content loads), INP (Interaction to Next Paint — responsiveness), and CLS (Cumulative Layout Shift — visual stability). Google uses them as one of many signals in search ranking.

Scenario A school news site loads slowly on a phone. Lighthouse shows three 4 MB photos. Resizing and compressing them to WebP (about 300 KB each), adding loading="lazy" to images below the fold and turning on caching cuts the data for a first load from about 12 MB to about 1 MB, and repeat visits reuse the cached files. The worked example below shows the arithmetic.

Worked example: where do the seconds go?

Transfer time is the size of the data divided by the connection speed. Because file sizes are in megabytes (MB) and speeds in megabits per second (Mbps), multiply the size by 8 first: time in seconds = size in MB × 8 ÷ speed in Mbps. Take a school news page on an 8 Mbps mobile connection:

VersionWhat is downloaded on first loadSizeTransfer time
Original0.5 MB of HTML, CSS and JavaScript, plus three 4 MB photos12.5 MB12.5 × 8 ÷ 8 = 12.5 s
Photos resized and converted to WebP (0.3 MB each); text compressed to about 0.15 MB (assumed)0.15 MB + 3 × 0.3 MB1.05 MB1.05 × 8 ÷ 8 = 1.05 s
Plus lazy loading: only the first photo is needed on screen0.15 MB + 0.3 MB0.45 MB0.45 s to first content; the other photos load as the reader scrolls
Repeat visit with cachingCached files reused; the server is only asked if the page has changedClose to 0 MBClose to 0 s of transfer

The sizes and the 30% compression figure are assumptions chosen to make the arithmetic clear. Your own numbers come from the Network panel. Transfer time is also only part of the story: every separate file also costs at least one round trip, and the browser cannot draw the page until it has processed any stylesheet or script in the head that blocks rendering.

HTML: several methods in one page
<head>
  <link rel="stylesheet" href="/css/site.9f3a1c.css">   <!-- fingerprinted file name, see caching below -->
  <script src="/js/app.js" defer></script>               <!-- download while parsing; run after the HTML is parsed -->
</head>
<body>
  <img src="/img/hero-600.webp" width="600" height="400" alt="Students at the science fair">
  <img src="/img/photo2-600.webp" width="600" height="400" alt="Robotics team"
       loading="lazy">                                    <!-- fetched only when scrolled near -->
</body>
  • defer lets the browser keep drawing the page while the script downloads. A plain script in the head stops parsing until it has run.
  • The width and height attributes reserve space, so nothing jumps when the image arrives (this helps the layout-shift measure, CLS).
  • The first image is not lazy-loaded, because it is the largest contentful paint (LCP); lazy loading it would delay the main content.
Response headers for a fingerprinted CSS file
Cache-Control: public, max-age=31536000, immutable
Content-Encoding: br

A one-year max-age is safe here because the file name contains a fingerprint (9f3a1c). When the CSS changes, the build produces a new name, so browsers download it at once, and the old cached copy is simply never asked for again. Content-Encoding: br means the server compressed the file with Brotli and the browser decompresses it.

MethodWhat it reduces
Image resizing and modern formatsBytes (usually the biggest saving)
Minification and compressionBytes of text files
CachingRepeat downloads, and so bytes and requests on later visits
CDNDistance, and so latency
Lazy loading and code splittingWork and bytes needed before the first screen
defer and removing render-blocking filesWaiting time before the first paint

To find out which method to apply, measure first: run Lighthouse, read the Network waterfall for the biggest files and the longest waits, and change the largest cost first. Re-measure afterwards, because a change that does not improve the numbers is only added complexity.

Exam tip: "Investigate" load times NESA's 2025 online paper included a load-times item within a weather-app scenario (one published exam). Good answers name two or three methods, say what each one reduces (bytes, requests, distance or blocking) and link them to the stimulus: for a page full of photos, resizing images and lazy loading matter more than minifying a small script. Mention that caching helps repeat visits but not the first one. Avoid a bare list of techniques with no reasons.

Try it

Page load explorer

Turn on performance techniques and watch a waterfall of requests shrink. Enable JavaScript to use this tool. In short: compress and resize files, cache them, serve them from nearby and lazy-load what is below the fold.


Web Content Management Systems

🎯 (SE-12-06)

📌 Research, experiment with and evaluate the prevalence and use of web content management systems (CMS)

A Content Management System (CMS) is a software platform that allows users to create, edit, organise, and publish digital content via a graphical interface — without requiring direct coding knowledge. CMSs run a very large share of the websites on the internet (W3Techs publishes the current figures), and WordPress alone is used by a large share of all sites.

What a CMS is made of

  • Content store: a database holding pages, posts, users and settings, separate from the design.
  • Admin interface: forms and an editor so non-programmers can write, review and publish.
  • Templates or themes: the code that turns stored content into HTML pages, so changing the theme restyles the whole site.
  • Plugins or extensions: add-ons for features such as forms, shops and SEO, written by third parties.
  • Roles and workflow: permissions (author, editor, administrator) and drafts, so an article can be reviewed before it goes live.

The separation of content from presentation is the key idea, and it explains the headless variant below: once content is stored separately, any front end can display it.

CMS Type Examples Evaluation
Traditional (Coupled) WordPress, Drupal, Joomla Pros: All-in-one, large plugin ecosystem, easy for non-developers. Cons: Tightly coupled front-end limits design flexibility; common target for security exploits due to widespread use.
Headless CMS Contentful, Sanity, Strapi Pros: Content served via API to any front-end (web, mobile, IoT); maximum design flexibility; better performance. Cons: Requires developer to build the front-end separately; higher initial complexity.
Visual / No-Code Webflow, Squarespace, Wix Pros: Drag-and-drop interface; fast deployment; hosted infrastructure included. Cons: Limited customisation; vendor lock-in; less suited for complex custom functionality.

Architecture diagram

Traditional and headless CMS

A traditional CMS joins content and presentation; a headless CMS serves content to any front end through an API.

What to notice
  • Headless separates where content is stored from where it is shown.
  • One headless back end can serve a website, an app and other devices.

Try thisA school wants its news on the website and in an app. Which CMS type suits it?

Evaluating a CMS: a weighted decision matrix

To evaluate is to make a judgement against criteria. A weighted matrix makes the criteria and the reasoning visible. Here a school compares the three types. Each option is scored 1 (poor) to 5 (excellent) on each criterion, and each score is multiplied by the criterion's weight, which reflects what matters to this school. The scores are the writer's judgements for illustration, not measurements.

CriterionTraditionalHeadlessVisual / no-codeWeight: website and appWeight: newsletter site only
Easy for non-coders53535
Low cost43234
Design flexibility35221
Low security upkeep24443
Content reuse across website and app25151
Weighted total: website and app516946
Weighted total: newsletter only524948

Working for the traditional CMS in the first scenario: 5×3 + 4×3 + 3×2 + 2×4 + 2×5 = 15 + 12 + 6 + 8 + 10 = 51. The same scores give different winners when the weights change: headless wins clearly when the content must appear in an app as well, and the traditional CMS narrowly wins when the school only wants a simple newsletter site. The judgement depends on the context, which is what an evaluation should say.

Research and experiment: a plan you can carry out

  1. Research prevalence. W3Techs publishes the share of websites using each CMS, and it changes over time, so cite the figure with its date. WordPress alone is used by a large share of all websites, which is why it is also a common target for attackers.
  2. Experiment. Install a traditional CMS on your own computer (a local WordPress install is free). Create a page, a post and a menu, change the theme, and add one plugin.
  3. Inspect what the plugin cost. Open the Network panel before and after adding it and record the change in the number of requests and total size. Run Lighthouse and record the performance and accessibility scores both times.
  4. Check upkeep. Note how many updates are waiting (core, themes, plugins). Each unpatched plugin is a possible way in.
  5. Evaluate. Fill in a matrix like the one above with your own scores and evidence, and state a recommendation for a named client.
Exam tip: "Research, experiment with and evaluate" a CMS An evaluation answer states criteria, weighs strengths and weaknesses of each type against the scenario, and ends with a justified recommendation. Do not just list pros and cons. Use the scenario's details (who edits the content, how many platforms need it, how much developer time there is) to decide. Cite prevalence only with its source and date, and never call WordPress "a coding language": it is a CMS written in PHP.
HSC writing sample "A traditional CMS such as WordPress suits the school's newsletter because staff can publish articles without coding and plugins add features quickly. Its popularity makes it a common attack target, so plugins must be kept updated. A headless CMS would suit a school that wants the same content in a website and a mobile app, but it needs developers to build the front end."

The Contribution of Back-End Development

🎯 (SE-12-07)

📌 Assess the contribution of back-end web development to the success of a web application

Back-end development is responsible for all logic that occurs on the server — the invisible engine that powers every user-facing feature. Its contribution to project success encompasses several critical dimensions:

Technology Selection

The choice of back-end framework determines the project's scalability, maintainability, and development speed. Django (Python) provides a "batteries-included" approach with an ORM, admin panel, and authentication built in — accelerating development of data-heavy applications. Node.js/Express enables the same language (JavaScript) on front-end and back-end, simplifying the development team's skill requirements. Flask offers a minimal, flexible microframework for APIs. The right technology choice directly impacts delivery timelines and long-term maintenance cost.

Business Logic and Data Integrity

The back end enforces all business rules that protect data integrity. For example, validating that a user's age is above 18 before processing a purchase, calculating order totals including applicable tax rates, or checking inventory levels before confirming a stock reservation. Enforcing these rules on the server (rather than only client-side) is essential because client-side code can be bypassed by malicious users using browser developer tools.

Security Enforcement

The back end is the primary security boundary of a web application. It implements authentication (password hashing with bcrypt, JWT token management), authorisation (role-based access control), input sanitisation (preventing SQL injection and XSS), and rate limiting (preventing brute-force login attacks). A poorly secured back end exposes the entire database and all user data to attackers.

To assess a contribution is to judge how much it matters and why. A useful way to organise that judgement is to test the back end against criteria that a web application is measured by, and to give evidence for each.

CriterionBack-end contributionWhat happens if it is weak
SecurityPassword hashing, sessions, role checks, parameterised queries, output encoding, rate limitingAccounts taken over, data stolen, site defaced
Correctness and integrityServer-side validation and business rules; transactions that succeed or fail as a wholeWrong totals, oversold stock, corrupt records
PerformanceEfficient queries, indexes, caching, sending only the data neededSlow pages and timeouts under load
ScalabilityStateless handlers, shared session store, load balancing, a database that can growThe site fails when it becomes popular
MaintainabilityClear structure, tests, logging, a suitable frameworkEvery change is slow and risky

Worked example: enforcing a rule on the server

The browser's form might limit the quantity box to 1 to 5, but anyone can send a request with any value. The back end applies the rule again and answers with a status code that says what was wrong:

Python: server-side validation of an order quantity
def validate_order(quantity_text, stock):
    if not quantity_text.isdigit():
        return (400, "Quantity must be a whole number")
    quantity = int(quantity_text)
    if quantity < 1:
        return (400, "Quantity must be at least 1")
    if quantity > stock:
        return (409, f"Only {stock} in stock")
    return (200, f"Order accepted: {quantity}")

for text in ["2", "0", "abc", "-1", "9"]:
    print(repr(text), validate_order(text, 5))
Output
'2' (200, 'Order accepted: 2')
'0' (400, 'Quantity must be at least 1')
'abc' (400, 'Quantity must be a whole number')
'-1' (400, 'Quantity must be a whole number')
'9' (409, 'Only 5 in stock')

Notice that "-1" is caught by the first test, because the minus sign is not a digit. Testing with awkward inputs like this is how you find gaps. Status 409 (Conflict) tells the client the request was valid but cannot be done in the current state.

Worked example: efficiency is a back-end matter too

Slow pages are often caused by too many database queries. The first version below asks one question per publisher; the second asks once. On the running database the loop needs four queries (one for the publishers and one for each of the three), and with 1,000 publishers it would need 1,001. The join needs one however many there are.

Python: many small queries against one grouped query
# Slow: 1 query for the publishers, then 1 more for each publisher (the "N + 1" pattern)
for pub_id, name in db.execute("SELECT Publisher_ID, Name FROM Publishers").fetchall():
    titles = db.execute("SELECT Name FROM Games WHERE Publisher_ID = ?", (pub_id,)).fetchall()
    print(name, len(titles))

# Better: one query does the counting
sql = """SELECT Publishers.Name, COUNT(Games.ID)
         FROM Publishers JOIN Games ON Games.Publisher_ID = Publishers.Publisher_ID
         GROUP BY Publishers.Name"""
for name, total in db.execute(sql):
    print(name, total)
Output (the same counts either way; the second version runs 1 query instead of 4)
Games Inc 4
Pixel Harbour 2
Coral Studios 1
Coral Studios 1
Games Inc 4
Pixel Harbour 2

Making the judgement

A strong assessment weighs the back end against the other parts. A back end that is secure, correct and quick gives users a site they can trust, so its contribution to success is large. But it cannot rescue a confusing interface or a slow front end (an oversized image is a front-end problem), and a well-built site still fails if the business idea does not work. So the judgement is that the back end is essential and often decisive for trust, data integrity and scalability, but it is one of several contributors, and it is most visible when it fails.

Exam tip: "Assess" the back end Give at least two contributions, each with a piece of evidence from the scenario, and then a judgement about how much they matter. For example: "Server-side validation stops a user changing a price with developer tools, which protects the business's revenue (security and integrity); using a cache for the product list keeps pages fast when many customers browse (performance). These are essential to the site's success, though a poor interface could still drive customers away." NESA's 2025 online paper asked about session management and code efficiency in a web context (one published exam), so know how sessions work and why fewer, better-designed queries make pages faster.
HSC writing sample "The back end contributes most to the application's success through security and data integrity. Because prices and stock levels are checked on the server rather than in the browser, a user cannot change a price with developer tools. Choosing Django also sped up development, since its built-in authentication and admin panel did not have to be written from scratch."

The Back-End Web Request Process

🎯 (SE-12-07)

📌 Observe and describe the back-end process used to manage a web request

  • role of webserver software
  • web framework
  • objects
  • libraries
  • databases

The Request-Response Lifecycle

Web Server SoftwareSoftware such as Nginx or Apache receives the raw HTTP request and routes it to the correct application handler. Nginx is also used as a reverse proxy and for serving static files efficiently.
Web FrameworkThe framework (e.g., Flask, Django, Express) parses the request URL and HTTP method, matches it against defined routes, and calls the appropriate handler function (view/controller).
ObjectsThe framework provides Request and Response objects — structured wrappers around the raw HTTP data. The Request object gives access to headers, query parameters, form data, and cookies; the Response object is used to construct the reply.
LibrariesThe handler uses imported libraries to perform specialised tasks — authentication libraries (e.g., Passport.js), email sending libraries (e.g., Nodemailer), file handling, or cryptographic operations.
DatabasesThe handler queries the database (via raw SQL or ORM) to retrieve, insert, update, or delete records. The database returns results which the handler uses to construct the response payload (JSON or rendered HTML).
Full Request Lifecycle Browser → DNS → Web Server (Nginx) → Framework Router → Handler Function → Library Calls → Database Query → Data returned → Handler builds Response → JSON/HTML sent back → Browser renders result.

Sequence diagram

The back-end process for one web request

One request passes through DNS, the web server, the framework, a handler and the database.

What to notice
  • The framework's router decides which handler runs.
  • Queries are parameterised before they reach the database.

Try thisWhere could caching skip the database for popular pages?

Worked example: a Flask route from URL to response

Flask is a small Python web framework. This program (illustrative, using the GameShelf database created in the SQL section) shows each element of the dot point in one place: the framework, its request and response objects, a library, and the database.

Python (Flask): two routes for GameShelf
import sqlite3
from flask import Flask, jsonify, request, abort, render_template_string

app = Flask(__name__)                          # the web framework object

def get_db():
    db = sqlite3.connect("gameshelf.db")       # library: Python's built-in sqlite3
    db.row_factory = sqlite3.Row               # rows behave like dictionaries
    return db

@app.route("/games")                           # route: this URL pattern -> this function
def list_games():
    max_cost = request.args.get("max_cost", type=float)    # Request object: the ?max_cost=30 part
    if max_cost is None:
        abort(400, description="max_cost must be a number")
    db = get_db()
    rows = db.execute(
        "SELECT Name, Cost FROM Games WHERE Cost <= ? ORDER BY Cost ASC",
        (max_cost,),                           # the ? placeholder keeps data separate from the SQL
    ).fetchall()
    db.close()
    return jsonify([dict(r) for r in rows])    # Response object: JSON body, status 200

@app.route("/games/<int:game_id>")             # <int:game_id> captures the number from the URL
def show_game(game_id):
    db = get_db()
    row = db.execute("SELECT Name, Cost FROM Games WHERE ID = ?", (game_id,)).fetchone()
    db.close()
    if row is None:
        abort(404)                             # no such game: send 404 Not Found
    return render_template_string("<h1>{{ name }}</h1><p>${{ '%.2f' % cost }}</p>",
                                  name=row["Name"], cost=row["Cost"])

Save it as app.py, create the database, and run flask run (the server listens on 127.0.0.1:5000). From another terminal, curl -i prints the full response including headers. This is what four requests return (the Server and Date header lines depend on your software and clock, and are trimmed here):

curl -i "http://127.0.0.1:5000/games?max_cost=30"
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 103

[{"Cost":20.0,"Name":"Kelp Quest"},{"Cost":25.0,"Name":"Reef Runner"},{"Cost":30.0,"Name":"Lava Lab"}]
curl -i http://127.0.0.1:5000/games/4
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 33

<h1>Star Sailor</h1><p>$45.00</p>
curl -i http://127.0.0.1:5000/games/99 (status line only: 404 NOT FOUND, with an HTML error page)
HTTP/1.1 404 NOT FOUND
curl -i "http://127.0.0.1:5000/games?max_cost=abc" (status line and message: 400 BAD REQUEST)
HTTP/1.1 400 BAD REQUEST
...
<p>max_cost must be a number</p>

Flask's JSON output sorts the keys, which is why Cost comes before Name; the client should never depend on key order. The terminal running flask run also logs one line per request, such as "GET /games?max_cost=30 HTTP/1.1" 200 -, which is the first place to look when a request misbehaves.

Observing the process

Matching the code to the dot point:

ElementIn the code aboveRole in the request
Web server softwareNot visible: flask run starts a simple development serverIn production, Nginx or Apache receives the request, handles HTTPS and static files, and passes dynamic requests to the application through a gateway such as Gunicorn (using the WSGI standard)
Web frameworkFlask, @app.route, abortMatches the URL and method to a function, parses the request, builds the response, converts errors to status codes
Objectsrequest, and the response made by jsonify; the row objects from the databaseWrap raw HTTP and database results as objects with properties and methods
Librariessqlite3, render_template_string (which uses Jinja2)Reusable code for database access and page templating
Databasegameshelf.db and the SQL stringStores and returns the data the handler needs

The ? placeholder deserves attention. If the code built the query by joining strings, a value such as 0 OR 1=1 could change what the query means (SQL injection). With a placeholder the database receives the SQL and the value separately, so the value is always treated as data.

Hands-on: watch a request being handled

  1. Save the program, create the database and start it with flask run.
  2. Open http://127.0.0.1:5000/games?max_cost=30 in a browser with the Network panel open. Confirm the status 200, the application/json content type and the body.
  3. Change 30 to abc, then change the path to /games/99. Note each status code and match it to the line in the code that produced it.
  4. Add print(request.headers) at the top of list_games, reload, and read the headers your browser sent (such as Host and User-Agent) in the terminal.
Exam tip: "Observe and describe" the back-end process Describe the path in order and name each Including item at its step: the web server software receives the request, the framework routes it to a handler, the handler uses the request object, calls libraries and queries the database, and builds a response object that returns a status code and body. Say what goes wrong at each step (404 if no route matches, 400 for bad input, 500 for a fault in the code). A sequence of numbered steps with the right vocabulary earns more than a paragraph of general description.
▶ Watch & Learn

A clear walkthrough of how a client sends a request and a server sends back data — exactly the back-end request/response process described above.


Web Applications and Shell Scripts

🎯 (SE-12-02)

📌 Develop a web application using an appropriate scripting language with shell scripts to make files and directories, and searching for text in a text file

A scripting language such as Python runs the web application, while shell scripts automate jobs around it: setting up folders, creating files and searching logs. Together they save time and make set-up repeatable on any computer.

Worked example: a set-up script

Before writing any Flask code, the team needs the same folder layout on every computer. A shell script records the steps once, so anyone can repeat them exactly. This one is saved as setup.sh and run with bash setup.sh:

Shell (Bash): setup.sh
#!/usr/bin/env bash
set -e                                        # stop at the first error
mkdir -p gameshelf/{static/{css,js,images},templates,logs}   # nested directories in one command
touch gameshelf/app.py gameshelf/requirements.txt            # create empty files
echo "Flask" > gameshelf/requirements.txt                    # > writes text into a file
echo "Created:"
find gameshelf | sort                                        # list everything created
Output
Created:
gameshelf
gameshelf/app.py
gameshelf/logs
gameshelf/requirements.txt
gameshelf/static
gameshelf/static/css
gameshelf/static/images
gameshelf/static/js
gameshelf/templates

The -p flag creates parent folders as needed and does not complain if a folder already exists, so the script is safe to run twice. > replaces a file's contents and >> adds to the end. The braces expand into every combination, which saves typing. On Windows PowerShell, the equivalents are New-Item -ItemType Directory, New-Item for files and Select-String for searching; the ideas are the same.

Worked example: searching a text file

The web app writes a log file. Suppose logs/app.log contains these six lines:

logs/app.log
2026-09-29 09:01:12 INFO  GET /games 200
2026-09-29 09:01:15 ERROR GET /games/99 404 game not found
2026-09-29 09:02:03 INFO  POST /login 200
2026-09-29 09:02:41 ERROR POST /login 401 bad password
2026-09-29 09:03:09 WARN  GET /games?max_cost=abc 400
2026-09-29 09:04:30 ERROR GET /games/7 500 database timeout
grep commands and their output
$ grep "ERROR" logs/app.log
2026-09-29 09:01:15 ERROR GET /games/99 404 game not found
2026-09-29 09:02:41 ERROR POST /login 401 bad password
2026-09-29 09:04:30 ERROR GET /games/7 500 database timeout

$ grep -n "POST" logs/app.log
3:2026-09-29 09:02:03 INFO  POST /login 200
4:2026-09-29 09:02:41 ERROR POST /login 401 bad password

$ grep -c "ERROR" logs/app.log
3

$ grep -i "bad password" logs/app.log
2026-09-29 09:02:41 ERROR POST /login 401 bad password

$ grep -v INFO logs/app.log
2026-09-29 09:01:15 ERROR GET /games/99 404 game not found
2026-09-29 09:02:41 ERROR POST /login 401 bad password
2026-09-29 09:03:09 WARN  GET /games?max_cost=abc 400
2026-09-29 09:04:30 ERROR GET /games/7 500 database timeout
OptionMeaning
-nShow line numbers
-cCount matching lines instead of showing them
-iIgnore upper and lower case
-vInvert: show the lines that do not match
-rSearch every file in a folder and its sub-folders
--include="*.log"Only search files whose names match

Worked example: the same search from Python, inside the web app

Shell scripts run around the application; Python runs inside it. This script makes a directory and a file (as the shell commands did) and searches the log. Then a route makes the result available to an administrator.

Python: find_errors.py
from pathlib import Path

def find_errors(log="logs/app.log"):
    """Return every line of the log that contains ERROR."""
    return [line.rstrip() for line in open(log) if "ERROR" in line]

uploads = Path("static/uploads")
uploads.mkdir(parents=True, exist_ok=True)         # like mkdir -p
(uploads / "README.txt").write_text("User uploads")   # like echo ... > file

errors = find_errors()
print(len(errors), "errors found")
for line in errors:
    print(line)
print(sorted(p.name for p in Path("static").iterdir()))
Output (run from inside the gameshelf folder)
3 errors found
2026-09-29 09:01:15 ERROR GET /games/99 404 game not found
2026-09-29 09:02:41 ERROR POST /login 401 bad password
2026-09-29 09:04:30 ERROR GET /games/7 500 database timeout
['css', 'images', 'js', 'uploads']
Python (Flask): expose the count to an administrator (illustrative)
@app.route("/admin/errors")
def error_count():
    # a real application would check the user's role first (authorisation)
    return jsonify(count=len(find_errors()))         # {"count": 3} for the log above

Two more commands from the same family: find . -name "*.py" -mtime -1 lists Python files changed in the last day, and find templates/ -name "*.html" | xargs wc -l counts the lines in every template (the | pipe passes one command's output to the next).

Choose the tool by the job: shell commands are quick for one-off tasks, file searching and set-up scripts that need no program logic; Python is better when the result must feed into the application, needs conditions and loops, or must run on any operating system.

Exam tip: "Develop" with scripts Code you write must run: NESA's 2025 marker feedback for programming questions asked for code that could be executed in the exam's coding environment, with correct indentation and functions called from the main program (one published exam). For shell questions, know mkdir (with -p), touch, cd, ls and grep (with -n, -i, -c). Explain each command in a line and predict the output for a small sample file; a common slip is forgetting that grep is case-sensitive unless -i is used.
Tip Keep a setup.sh script in your project. Anyone who clones the repository can run one command to create the folders, install packages and start the server.

Web-Based Databases and SQL

🎯 (SE-12-06, SE-12-08)

📌 Apply a web-based database and construct script that executes SQL

  • selecting fields
  • incorporating ‘group by’
  • common SQL queries
  • constraints using WHERE keyword
  • table joins

Web applications keep their data in a relational database and read and change it with Structured Query Language (SQL). The Course Specifications set the syntax used in the HSC: SELECT the fields or calculated values to display, FROM the tables to use, WHERE the search criteria, GROUP BY the fields that group the rows and ORDER BY the fields that set the sequence. AS renames a field for display. Every query on this page runs on one running database, the GameShelf catalogue, whose three tables and keys are the ones in the Specifications' example.

The running database

TableFields (P = primary key, F = foreign key)Relationship
PublishersPublisher_ID (P), NameOne publisher has many games
DevelopersDeveloper_ID (P), First_name, Last_nameOne developer has many games
GamesID (P), Name, Release_date, Cost, Publisher_ID (F), Developer_ID (F)Each game has one publisher and one developer, held as foreign keys

The tables contain this data (dates are written year-month-day, which databases such as SQLite compare correctly as text; the Specifications' examples write them as '01/03/2022', so follow the format your exam environment gives):

Publishers table
Publisher_ID  Name
------------  -------------
1             Games Inc
2             Pixel Harbour
3             Coral Studios
(3 rows)
Developers table
Developer_ID  First_name  Last_name
------------  ----------  ---------
1             Ava         Nguyen
2             Ben         Clarke
3             Chloe       Zhang
4             Dev         Patel
(4 rows)
Games table
ID  Name             Release_date  Cost   Publisher_ID  Developer_ID
--  ---------------  ------------  -----  ------------  ------------
1   Reef Runner      2022-03-15    25.00  1             1
2   Lava Lab         2022-11-02    30.00  1             2
3   Kelp Quest       2021-08-09    20.00  1             1
4   Star Sailor      2023-03-20    45.00  2             1
5   Bush Tracker     2023-06-01    35.00  2             3
6   Quest for Coral  2023-01-12    40.00  3             3
7   Outback Odyssey  2022-03-31    50.00  1             2
(7 rows)

Notice that Dev Patel has no games and that the same developer appears many times in Games. That is why developers are in their own table: each fact is stored once and linked by a key. To recreate the database on your own computer, run this once (it also creates the gameshelf.db file that the Flask examples use):

Python: setup_db.py creates gameshelf.db
import sqlite3

db = sqlite3.connect("gameshelf.db")
db.executescript("""
DROP TABLE IF EXISTS Games;
DROP TABLE IF EXISTS Publishers;
DROP TABLE IF EXISTS Developers;
CREATE TABLE Publishers (Publisher_ID INTEGER PRIMARY KEY, Name TEXT);
CREATE TABLE Developers (Developer_ID INTEGER PRIMARY KEY, First_name TEXT, Last_name TEXT);
CREATE TABLE Games (
    ID INTEGER PRIMARY KEY, Name TEXT, Release_date TEXT, Cost REAL,
    Publisher_ID INTEGER REFERENCES Publishers(Publisher_ID),
    Developer_ID INTEGER REFERENCES Developers(Developer_ID));
INSERT INTO Publishers VALUES (1,'Games Inc'), (2,'Pixel Harbour'), (3,'Coral Studios');
INSERT INTO Developers VALUES (1,'Ava','Nguyen'), (2,'Ben','Clarke'), (3,'Chloe','Zhang'), (4,'Dev','Patel');
INSERT INTO Games VALUES
 (1,'Reef Runner','2022-03-15',25.00,1,1), (2,'Lava Lab','2022-11-02',30.00,1,2),
 (3,'Kelp Quest','2021-08-09',20.00,1,1),  (4,'Star Sailor','2023-03-20',45.00,2,1),
 (5,'Bush Tracker','2023-06-01',35.00,2,3), (6,'Quest for Coral','2023-01-12',40.00,3,3),
 (7,'Outback Odyssey','2022-03-31',50.00,1,2);
""")
db.commit()
db.close()
print("gameshelf.db created")

Selecting fields

List the fields you want after SELECT. Use * only for exploring; naming fields sends less data and survives a new column being added. A calculated value can be given a display name with AS:

SQL: selecting fields and a calculated field
SELECT Name, Cost
FROM Games

SELECT Name, Cost - 5 AS Sale_price
FROM Games
Result
Name             Sale_price
---------------  ----------
Reef Runner      20.00
Lava Lab         25.00
Kelp Quest       15.00
Star Sailor      40.00
Bush Tracker     30.00
Quest for Coral  35.00
Outback Odyssey  45.00
(7 rows)

Rows come back in no guaranteed order unless you use ORDER BY, so never rely on the order shown by a query without it.

Constraints using WHERE

WHERE keeps only the rows for which the condition is true. The Specifications list relational operators by name; in an SQL engine you write the symbol, and CONTAINS becomes a LIKE pattern where % stands for "any characters".

Specifications' operatorWritten in SQLExample
EQUALS=Publishers.Name = 'Games Inc'
NOT EQUAL TO<> (or !=)Publisher_ID <> 1
GREATER THAN / GREATER THAN OR EQUAL TO> / >=Cost > 30
LESS THAN / LESS THAN OR EQUAL TO< / <=Release_date <= '2023-03-31'
CONTAINSLIKE '%text%'Name LIKE '%Quest%'
DOES NOT CONTAINNOT LIKE '%text%'Name NOT LIKE '%Quest%'
AND, OR, NOTthe same wordsCost < 25 OR Cost > 45

Text and date values go in single quotes; numbers do not. The first query below is the Specifications' date-range example, using this database:

SQL: games released from 1 March 2022 to 31 March 2023, in name order
SELECT Name, Release_date
FROM Games
WHERE Release_date >= '2022-03-01' AND Release_date <= '2023-03-31'
ORDER BY Name ASC
Result
Name             Release_date
---------------  ------------
Lava Lab         2022-11-02
Outback Odyssey  2022-03-31
Quest for Coral  2023-01-12
Reef Runner      2022-03-15
Star Sailor      2023-03-20
(5 rows)
SQL: CONTAINS, NOT and OR
SELECT Name, Cost FROM Games WHERE Name LIKE '%Quest%'

SELECT Name, Cost FROM Games
WHERE Cost >= 30 AND NOT Publisher_ID = 1
ORDER BY Cost DESC

SELECT Name, Cost FROM Games
WHERE Cost < 25 OR Cost > 45
ORDER BY Cost ASC
Result
Name             Cost
---------------  -----
Kelp Quest       20.00
Quest for Coral  40.00
(2 rows)
Result
Name             Cost
---------------  -----
Star Sailor      45.00
Quest for Coral  40.00
Bush Tracker     35.00
(3 rows)
Result
Name             Cost
---------------  -----
Kelp Quest       20.00
Outback Odyssey  50.00
(2 rows)

ORDER BY takes the field and a method: ASC (A to Z, 0 to 9) or DESC (Z to A, 9 to 0). Both Kelp Quest and Quest for Coral match '%Quest%' because the pattern can appear anywhere in the name.

Incorporating GROUP BY

GROUP BY collapses the rows that share a value into one row per group, and functions such as SUM, AVG, COUNT, MAX and MIN calculate one value for each group. Reading the query below: find each game's publisher, group the games by publisher, then for each group count the games, average the cost and find the dearest.

SQL: a summary for each publisher
SELECT Publishers.Name AS Publisher, COUNT(Games.ID) AS Titles,
       AVG(Games.Cost) AS AvgCost, MAX(Games.Cost) AS Dearest
FROM Games, Publishers
WHERE Publishers.Publisher_ID = Games.Publisher_ID
GROUP BY Publishers.Publisher_ID
ORDER BY Titles DESC
Result
Publisher      Titles  AvgCost  Dearest
-------------  ------  -------  -------
Games Inc      4       31.25    50.00
Pixel Harbour  2       40.00    45.00
Coral Studios  1       40.00    40.00
(3 rows)

Check it by hand: Games Inc published Reef Runner (25), Lava Lab (30), Kelp Quest (20) and Outback Odyssey (50), so the count is 4, the total is 125 and the average is 125 ÷ 4 = 31.25. Every field in SELECT that is not a calculation should be one of the fields you group by (or be determined by them); some database engines reject a query that breaks this rule.

Table joins

A game holds only a publisher's and developer's ID numbers. To show names, the query must link the tables by matching each foreign key to its primary key. The Specifications join by listing the tables in FROM and adding the matching conditions to WHERE. This is their second example on the running database:

SQL: total cost of games each developer made for the publisher 'Games Inc'
SELECT Developers.First_name, Developers.Last_name, SUM (Games.cost) AS Totalcost
FROM Games, Publishers, Developers
WHERE Publishers.Name = 'Games Inc'
AND Publishers.Publisher_ID = Games.Publisher_ID
AND Developers.Developer_ID = Games.Developer_ID
GROUP BY Developers.Developer_ID
ORDER BY Developers.Last_name DESC
Result
First_name  Last_name  Totalcost
----------  ---------  ---------
Ava         Nguyen     45.00
Ben         Clarke     80.00
(2 rows)

Only Games Inc's four games count. Ava's two (Reef Runner and Kelp Quest) total 45 and Ben's two (Lava Lab and Outback Odyssey) total 80; Chloe made no game for Games Inc, so she does not appear. The same idea is written with an explicit JOIN keyword in most SQL you will meet outside the exam:

SQL: INNER JOIN, then LEFT JOIN
SELECT Games.Name, Developers.Last_name
FROM Games INNER JOIN Developers ON Games.Developer_ID = Developers.Developer_ID
WHERE Games.Cost > 30
ORDER BY Games.Name ASC

SELECT Developers.First_name, Developers.Last_name, COUNT(Games.ID) AS Titles
FROM Developers LEFT JOIN Games ON Developers.Developer_ID = Games.Developer_ID
GROUP BY Developers.Developer_ID
ORDER BY Developers.Last_name ASC
Result
Name             Last_name
---------------  ---------
Bush Tracker     Zhang
Outback Odyssey  Clarke
Quest for Coral  Zhang
Star Sailor      Nguyen
(4 rows)
Result
First_name  Last_name  Titles
----------  ---------  ------
Ben         Clarke     2
Ava         Nguyen     3
Dev         Patel      0
Chloe       Zhang      2
(4 rows)

An inner join keeps only rows with a match on both sides. A left join keeps every row from the left table, filling the missing side with NULL, which is how Dev Patel appears with 0 titles. COUNT(Games.ID) counts only non-null values, so it correctly gives 0. Leave out the matching condition and the database pairs every row with every row (a Cartesian product):

SQL: a join with the condition forgotten
SELECT COUNT(*) AS Rows_returned
FROM Games, Publishers, Developers
Result
Rows_returned
-------------
84
(1 row)

That is 7 × 3 × 4 = 84 rows of nonsense, and a good sign to look for when a result has far too many rows.

Try it

SQL practice: games, publishers and developers

Write SELECT queries with joins, GROUP BY and aggregate functions on the running database. Enable JavaScript to use this tool. In short: SELECT chooses the fields, JOIN links tables on matching keys, WHERE filters rows, GROUP BY makes one row per group for COUNT, SUM, AVG, MAX and MIN, and ORDER BY sorts.

Common SQL queries

StatementPurposeExample on the running database
SELECTRetrieve recordsSELECT * FROM Games;
INSERT INTOAdd a recordINSERT INTO Games (Name, Release_date, Cost, Publisher_ID, Developer_ID) VALUES ('Tide Pool', '2024-02-10', 18.00, 3, 4);
UPDATEChange recordsUPDATE Games SET Cost = 22.00 WHERE ID = 3;
DELETERemove recordsDELETE FROM Games WHERE ID = 3;
WHEREFilter which rows are affectedWHERE Cost < 25 AND Publisher_ID = 1
GROUP BYOne row per group, with SUM, COUNT, AVG, MAX, MINGROUP BY Publisher_ID
INNER JOINRows with matching keys in both tablesJOIN Developers ON Games.Developer_ID = Developers.Developer_ID
LEFT JOINAll rows from the left table, NULL where no matchDevelopers who have made no games

Always give UPDATE and DELETE a WHERE. DELETE FROM Games; removes every game.

A script that executes SQL

From a web application, SQL is sent as text by a script. This program (run in the folder containing gameshelf.db) executes two queries, passing the user's value as a parameter rather than joining it into the SQL string.

Python: running SQL from a script with a parameter
import sqlite3

db = sqlite3.connect("gameshelf.db")

publisher = "Games Inc"                     # in a web app this would come from the request
rows = db.execute(
    "SELECT Games.Name, Games.Cost "
    "FROM Games, Publishers "
    "WHERE Publishers.Name = ? AND Publishers.Publisher_ID = Games.Publisher_ID "
    "ORDER BY Games.Cost DESC",
    (publisher,),                           # the value is sent separately from the SQL
).fetchall()

for name, cost in rows:
    print(f"{name}: ${cost:.2f}")

total = db.execute("SELECT SUM(Cost) FROM Games").fetchone()[0]
print("Catalogue value:", total)
db.close()
Output
Outback Odyssey: $50.00
Lava Lab: $30.00
Reef Runner: $25.00
Kelp Quest: $20.00
Catalogue value: 245.0

If the script had built the query as "... WHERE Publishers.Name = '" + publisher + "' ..." and a user supplied x' OR '1'='1, the condition would become true for every row and the query would return the whole table. That is SQL injection. The ? placeholder prevents it because the database never treats the value as SQL.

Common mistakes

SymptomLikely cause
Far too many rowsA missing join condition (Cartesian product)
An error about a field not being groupedA non-calculated field in SELECT that is not in GROUP BY
No rows returnedText compared with the wrong case or spelling, or quotes missing ('Games Inc' needs them)
Rows in a surprising orderNo ORDER BY, so the order is not guaranteed
Everything changed or deletedUPDATE or DELETE without WHERE
Two different tables' Name fields confusedField names not qualified: write Publishers.Name and Games.Name
Exam tip: "Apply" SQL The 2025 online paper had a 4-mark SQL item using SELECT, WHERE with OR, COUNT, GROUP BY and ORDER BY ... DESC, and NESA's marker feedback asked for a query that ran in the exam's SQL environment (one published exam). Work in the clause order SELECT, FROM, WHERE, GROUP BY, ORDER BY. Qualify field names with the table name when tables share a field name, join tables by matching each foreign key to its primary key, put text in single quotes, and check your result against the sample data by hand. Marks are lost for a missing join condition, for GROUP BY without an aggregate function, and for confusing AND with OR in a range test.

Comparing ORM to SQL

🎯 (SE-12-06, SE-12-08)

📌 Compare Object-Relational Mapping (ORM) to SQL

Method Description Pros & Cons
🛠️ Raw SQL Queries written directly in SQL and sent to the database engine. Provides fine-grained control over every aspect of the query. Pros: Maximum performance; expressive power for complex JOINs, subqueries, and aggregations; universal — works with any SQL database.
Cons: Vulnerable to SQL injection if inputs aren't parameterised; verbose for simple CRUD operations; query strings are hard to refactor.
🛠️ ORM Maps database tables to Python/JavaScript classes. Developers interact with objects; the ORM generates SQL automatically (e.g., Django ORM, SQLAlchemy, Sequelize). Pros: Faster development for standard operations; built-in input sanitisation prevents injection; database-agnostic (swap PostgreSQL for MySQL with minimal changes); code is more readable and object-oriented.
Cons: Can generate inefficient SQL for complex queries; adds an abstraction layer that obscures what is actually executed; requires learning the ORM API on top of SQL.

What an ORM maps

The Course Specifications describe an ORM as a layer of abstraction between the database and the programming language: in an object-oriented language, database rows are represented as objects and their columns as properties of the object. Students are expected to interpret code fragments used in an ORM framework, so the goal here is to read ORM code fluently, not to memorise one library.

Database ideaORM ideaIn the GameShelf example
TableClassGames table, Game class
Row (record)Object (instance)One Game object for Kelp Quest
Column (field)Attribute (property)game.Cost
Foreign keyRelationship between objectsgame.developer gives the Developer object
SQL queryMethod calls that build the queryselect(Game).where(Game.Cost > 30)

Worked example: the running database as classes

This code uses SQLAlchemy, a widely used Python ORM (installed with pip install sqlalchemy). It describes the same three tables that setup_db.py created. Each class is one table; each attribute line is one column; ForeignKey and relationship describe the links.

Python (SQLAlchemy): models.py
from sqlalchemy import create_engine, ForeignKey, select, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session

class Base(DeclarativeBase):
    pass

class Publisher(Base):
    __tablename__ = "Publishers"                     # the class maps to this table
    Publisher_ID: Mapped[int] = mapped_column(primary_key=True)
    Name: Mapped[str]
    games: Mapped[list["Game"]] = relationship(back_populates="publisher")

class Developer(Base):
    __tablename__ = "Developers"
    Developer_ID: Mapped[int] = mapped_column(primary_key=True)
    First_name: Mapped[str]
    Last_name: Mapped[str]
    games: Mapped[list["Game"]] = relationship(back_populates="developer")

class Game(Base):
    __tablename__ = "Games"
    ID: Mapped[int] = mapped_column(primary_key=True)
    Name: Mapped[str]                                # each attribute maps to a column
    Release_date: Mapped[str]
    Cost: Mapped[float]
    Publisher_ID: Mapped[int] = mapped_column(ForeignKey("Publishers.Publisher_ID"))
    Developer_ID: Mapped[int] = mapped_column(ForeignKey("Developers.Developer_ID"))
    publisher: Mapped[Publisher] = relationship(back_populates="games")
    developer: Mapped[Developer] = relationship(back_populates="games")

engine = create_engine("sqlite:///gameshelf.db")

Now the same queries as the SQL section, first as SQL and then as ORM code. The first query lists games costing more than 30 in name order, and shows each one's developer:

Python (SQLAlchemy): a filtered, ordered query with a relationship
from models import *

with Session(engine) as session:
    query = select(Game).where(Game.Cost > 30).order_by(Game.Name)
    for game in session.scalars(query):
        print(game.Name, game.Cost, game.developer.Last_name)   # follows the foreign key
Output
Bush Tracker 35.0 Zhang
Outback Odyssey 50.0 Clarke
Quest for Coral 40.0 Zhang
Star Sailor 45.0 Nguyen

The query object is built in Python, but the ORM turns it into SQL for you. It sends something equivalent to SELECT ... FROM Games WHERE Games.Cost > ? ORDER BY Games.Name, with the value 30 passed separately, which is also why an ORM protects against SQL injection by default. Reading a game's developer makes a further query for that developer.

The Specifications' grouping example (each developer's total for 'Games Inc') is written like this. The join calls replace the matching conditions in WHERE, and group_by, order_by and func.sum correspond directly to GROUP BY, ORDER BY and SUM:

Python (SQLAlchemy): the GROUP BY query
from models import *

with Session(engine) as session:
    query = (
        select(Developer.First_name, Developer.Last_name, func.sum(Game.Cost).label("Totalcost"))
        .join(Game).join(Publisher)
        .where(Publisher.Name == "Games Inc")
        .group_by(Developer.Developer_ID)
        .order_by(Developer.Last_name.desc())
    )
    for row in session.execute(query):
        print(row.First_name, row.Last_name, row.Totalcost)
Output
Ava Nguyen 45.0
Ben Clarke 80.0

Changing data is where an ORM feels most like ordinary object-oriented code: you load an object, change its attribute and commit. Add a new object and commit to insert a row.

Python (SQLAlchemy): update and insert without writing SQL
from models import *

with Session(engine) as session:
    game = session.get(Game, 3)          # SELECT ... WHERE ID = 3
    print(game.Name, game.Cost, game.publisher.Name)
    game.Cost = 22.00                    # change the attribute
    session.commit()                     # the ORM runs UPDATE Games SET Cost = 22.0 WHERE ID = 3
    session.add(Game(Name="Tide Pool", Release_date="2024-02-10", Cost=18.00, Publisher_ID=3, Developer_ID=4))
    session.commit()                     # the ORM runs INSERT INTO Games ...
Output
Kelp Quest 20.0 Games Inc

This changes the database, so run setup_db.py again to restore the original data before trying other examples. Other frameworks express the same ideas differently: Django writes the first query as Game.objects.filter(cost__gt=30).order_by("name"), but the mapping of tables to classes, rows to objects and columns to attributes is identical, which is what the Course Specifications ask you to recognise.

Comparing the two approaches

CriterionRaw SQLORM
Speed of writing routine codeSlower: text queries and manual conversion of rowsFaster: create, read, update and delete through objects
Readability for programmersGood for people who know SQL; strings hide errors until run timeReads like the rest of the program, with editor help and checks
Control over the queryComplete; can use every database featureLess direct; the generated SQL may be inefficient (for example one query per object in a loop)
Performance tuningDirect: you write the exact queryNeeds knowledge of how the ORM builds queries, or a raw SQL escape hatch
Switching database engineSome SQL differs between enginesThe ORM hides most differences
SecuritySafe only if every query is parameterisedParameterises by default, though raw strings passed to it are still risky
Learning costLearn SQLLearn SQL and the ORM

In practice teams often use both: the ORM for everyday operations on single records, and raw SQL for a reporting query that joins several tables and must be fast.

Exam tip: "Compare" and "interpret" ORM Two question types are likely. A compare or matching item gives characteristics and asks which belong to ORM and which to raw SQL (NESA's 2025 online paper included a matching item of this kind, one published exam). For a code fragment, translate it line by line into the database idea it stands for: a class is a table, an object is a row, an attribute is a column, a method call such as filter or where is a WHERE clause. A full-mark comparison gives a similarity and a difference, and a benefit and a drawback of each, not a list of one-word labels. A common mistake is claiming an ORM is always slower or that raw SQL is always insecure.
HSC writing sample "An ORM such as SQLAlchemy speeds up development because tables are used as Python classes, and it parameterises queries automatically, which reduces the risk of SQL injection. Raw SQL gives more control and can be faster for complex reports that join several tables, so many applications use an ORM for everyday queries and raw SQL for a few performance-critical ones."

Front-End and Back-End Collaboration

🎯 (SE-12-05, SE-12-09)

📌 Describe how collaborative work practices between front-end and back-end developers improve the development of a web solution

API Contracts and Documentation

The primary collaboration mechanism between front-end and back-end developers is the API contract — an agreed-upon specification of every endpoint's URL, HTTP method, request parameters, and response format. Tools like OpenAPI/Swagger allow teams to document and even mock APIs before the back end is fully built, enabling front-end and back-end development to proceed simultaneously. This prevents integration errors at the end of the project and enables both teams to work independently with confidence.

Worked example: an API contract

Before either team writes code, they agree on the contract. This fragment (in the OpenAPI format, which is widely used to document web APIs) says that GET /games needs a numeric max_cost and returns a list of objects with a name and a cost:

OpenAPI (YAML): the agreed contract for one endpoint
openapi: 3.0.3
info: { title: GameShelf API, version: "1.0" }
paths:
  /games:
    get:
      summary: List games costing no more than max_cost
      parameters:
        - { name: max_cost, in: query, required: true, schema: { type: number } }
      responses:
        "200":
          description: A list of games
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    Name: { type: string }
                    Cost: { type: number }
        "400":
          description: max_cost is missing or not a number

With the contract written down, the front-end developer can build against fake data that has the agreed shape (a small mock that returns [{ Name: 'Kelp Quest', Cost: 20 }] is enough), and swap in the real API later.

Meanwhile the back-end developer writes a contract test that fails if the real API drifts from the agreement:

Python: a contract test (runs with pytest or as a plain function)
def test_games_contract(client):
    response = client.get("/games?max_cost=30")
    assert response.status_code == 200
    for game in response.get_json():
        assert set(game) == {"Name", "Cost"}                 # exactly the agreed fields
        assert isinstance(game["Cost"], (int, float))

Run against the GameShelf route, the test passes silently. Now suppose the back-end developer renames Cost to Price without telling anyone. Without the test, the front end fails in users' browsers with a message like Cannot read properties of undefined (reading 'toFixed'). With it, the change fails the test in seconds, before it is merged. The fix is a conversation: either revert the rename, or agree on a new version of the contract (for example /v2/games) and change both sides together.

Version Control and Branching Strategies

Teams use Git branching workflows to manage parallel development. In the Git Flow model: the main branch always contains production-ready code; develop is the integration branch for completed features; and individual feature/ branches are created for each task. Pull Requests (PRs) are submitted when a feature is complete — requiring at least one peer code review before merging. This enforces code quality and knowledge sharing across the team, and ensures no one developer is a single point of failure for any part of the system.

Open-Source and Ethical Collaboration

Collaborative web development carries ethical responsibilities. Teams must fairly attribute contributions, respect the licences of all third-party code used, ensure equitable division of workload, and maintain transparent communication with stakeholders about progress and setbacks. When using open-source code libraries, teams must monitor for security vulnerabilities and update dependencies promptly to protect users.

Practices and what they prevent

PracticeHow it helps the two teamsProblem it prevents
API contract and mock dataBoth sides build in parallel against an agreed shapeWaiting for the other team; "it worked on my side" integration failures
Shared repository, branches and pull requestsEvery change is reviewed by a second person, often from the other teamUnreviewed changes breaking the live site; overwriting each other's files
Agreed error handlingThe back end returns consistent status codes and messages; the front end shows friendly text for eachBlank screens and unexplained failures
Issue tracker and regular stand-upsVisible tasks, owners and blockersDuplicated work and forgotten tasks
Exam tip: "Describe" collaboration Name two or three practices and link each to a benefit in a specific web project: "an agreed API contract lets the front-end team build against mock data while the back end is written, so neither waits for the other, and integration errors are found early". Weak answers say "communicate more" or "use GitHub" without saying what is shared, agreed or reviewed.

Designing a Progressive Web App

🎯 (SE-12-02, SE-12-03, SE-12-06)

📌 Design, develop and implement a progressive web app (PWA)

  • the application of design and user interface (UI) and user experience (UX) principles of font, colour, audio, video and navigation
  • a UI that considers accessibility and inclusivity

UI and UX Principles

Typography

Select legible, web-safe or Google Fonts typefaces. Establish a clear typographic hierarchy (H1 → H2 → body → caption). Minimum body font size of 16px for readability. Sufficient line-height (1.5–1.7) for comfortable reading. Limit typeface count to 2 maximum.

Colour

Use a defined colour palette: primary, secondary, and accent colours plus semantic colours (danger red, success green). Ensure a minimum WCAG AA contrast ratio of 4.5:1 between text and background. Avoid conveying information by colour alone (for colour-blind users).

Audio and Video

Provide controls for all auto-playing media. Supply captions/subtitles for video content (WCAG requirement). Compress media for fast loading. Avoid content that flashes more than 3 times per second (seizure risk).

Navigation

Consistent navigation structure across all views. Clear visual indication of the current page (active state). Breadcrumbs for deep navigation hierarchies. Touch-friendly tap targets (minimum 44×44px). Logical tab order for keyboard navigation.

5-Point Syllabus Compliance Checklist for PWA UX

Ensure your software engineering project meets these core NESA user experience standards:

  1. Responsiveness: Adapts cleanly to mobile, tablet, and desktop viewports.
  2. Accessibility: Achieves WCAG AA contrast and full keyboard operability.
  3. Offline Grace: Displays a custom UI (via Service Worker) when the network drops, rather than the browser's default error.
  4. Installability: Provides a valid manifest.json so the app can be pinned to the home screen.
  5. Consistency: Uses a predictable interaction model and uniform colour palette across all screens.

Accessibility and Inclusivity

PWA interfaces must be designed to be usable by everyone, including people with visual, auditory, cognitive, or motor disabilities:

Semantic HTMLUse correct elements for their purpose (<button> for buttons, <nav> for navigation, <main> for content). Screen readers rely on semantic structure to describe pages to blind users.
Alt TextEvery non-decorative image must have descriptive alt attribute text. Decorative images use alt="" so screen readers skip them.
⌨️ Keyboard NavigationAll interactive elements must be reachable and operable using only a keyboard (Tab to focus, Enter to activate). Required for users who cannot use a mouse.
ARIA AttributesARIA (Accessible Rich Internet Applications) attributes (e.g., aria-label, aria-expanded, role) describe the state and purpose of dynamic UI components to assistive technologies.
Focus ManagementWhen a modal or dialog opens, keyboard focus must move into it. When it closes, focus returns to the triggering element. This ensures keyboard users don't lose their place in the interface.

Core PWA Technical Requirements

Web App Manifest

A JSON file (manifest.json) that defines the app's name, icons, theme colour, and display mode (fullscreen/standalone). Enables the "Add to Home Screen" install prompt in mobile browsers.

Service Worker

A JavaScript file that runs in the background as a network proxy. Intercepts fetch requests to serve cached assets when offline, enable push notifications, and implement background sync for reliable data submission.

HTTPS Required

PWAs must be served over HTTPS — service workers are restricted to secure origins. HTTPS also builds user trust and is a requirement for browser installability.

JavaScript — Service Worker: Offline Cache Strategy
const CACHE_NAME = 'pwa-cache-v1';
const ASSETS_TO_CACHE = ['/', '/index.html', '/css/styles.css', '/js/app.js'];

// Install event: cache core app shell assets
self.addEventListener('install', event => {
    event.waitUntil(
        caches.open(CACHE_NAME).then(cache => cache.addAll(ASSETS_TO_CACHE))
    );
});

// Fetch event: serve from cache, fallback to network
self.addEventListener('fetch', event => {
    event.respondWith(
        caches.match(event.request).then(cached => cached || fetch(event.request))
    );
});

Flowchart

A service worker serving cached files

A cache-first strategy answers from the cache and only uses the network when it must.

What to notice
  • Cached files load instantly and work offline.
  • The offline page means the app never shows a blank error.

Try thisNews headlines change often. Would cache-first suit them? What would you change?

Web App Manifest

The manifest.json file tells the browser how to install and present the PWA. Link it in your HTML <head>: <link rel="manifest" href="/manifest.json">

JSON — manifest.json (complete example)
{
  "name": "HSC Study Planner",
  "short_name": "StudyApp",
  "description": "A PWA for organising HSC study sessions",
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#4f46e5",
  "orientation": "portrait",
  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}

PWA vs Native App

FeaturePWANative App
InstallationAdd to home screen via browserApp Store / Play Store download
Offline capabilityYes — via service worker cacheYes — full local storage
PerformanceGood — limited by browserExcellent — direct hardware access
UpdatesInstant — update the web filesUser must download update
Device featuresLimited (camera, GPS, notifications)Full access to all device APIs
Development costLow — one codebase for all platformsHigh — separate iOS and Android teams

The service worker lifecycle

A service worker is a script the browser runs separately from the page. It has its own lifecycle, and most PWA bugs come from misunderstanding it.

  1. Register. The page asks the browser to install the worker (code below). The browser downloads sw.js.
  2. Install. The worker's install event runs once for each new version. It normally caches the app shell here.
  3. Activate. When the new worker takes over, the activate event runs. This is the place to delete old caches.
  4. Fetch. From now on, every request from pages the worker controls goes through its fetch event, where it decides whether to answer from the cache or the network.
JavaScript (in the page): register the worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then(reg => console.log('Service worker registered, scope:', reg.scope))
      .catch(err => console.log('Registration failed:', err));
  });
}

On a first visit the console shows Service worker registered, scope: https://…/. The worker controls pages only inside its scope (the folder it is served from), so sw.js is placed at the site root. A page loaded before the worker was installed is not controlled until it is reloaded.

Caching strategies

StrategyHow it worksBest forDrawback
Cache firstAnswer from the cache; use the network only if it is not cachedThe app shell, fonts, images and other files that rarely changeUsers may see stale files until the cache version changes
Network firstTry the network; use the cache if the network failsData that changes often, such as headlines or pricesSlower when the connection is poor
Stale while revalidateAnswer from the cache immediately, fetch a fresh copy in the background for next timeContent where a slightly old copy is acceptable, such as avatarsThe first view after a change is one version behind

The earlier example is purely cache first. A more realistic worker uses network first for the API, cache first for the shell, cleans old caches when the version changes, and shows a friendly offline page instead of the browser's error:

JavaScript: sw.js with versioned cache, two strategies and an offline page
const CACHE = 'gameshelf-v2';                                  // change this to release new files
const SHELL = ['/', '/index.html', '/css/site.css', '/js/app.js', '/offline.html'];

self.addEventListener('install', event => {
  event.waitUntil(caches.open(CACHE).then(cache => cache.addAll(SHELL)));
});

self.addEventListener('activate', event => {                   // delete caches from old versions
  event.waitUntil(
    caches.keys().then(names => Promise.all(
      names.filter(name => name !== CACHE).map(name => caches.delete(name))
    ))
  );
});

self.addEventListener('fetch', event => {
  const url = new URL(event.request.url);
  if (url.pathname.startsWith('/games')) {                     // API data: network first
    event.respondWith(
      fetch(event.request).catch(() => caches.match(event.request))
    );
  } else {                                                     // everything else: cache first
    event.respondWith(
      caches.match(event.request).then(cached =>
        cached || fetch(event.request).catch(() => caches.match('/offline.html')))
    );
  }
});

Testing that it really works offline

  1. Serve the app over HTTPS (or from localhost, which browsers treat as secure for development) and load it once.
  2. Open the dev tools, Application panel, Service Workers. You should see sw.js listed as activated and running. Under Cache Storage you should see gameshelf-v2 containing the shell files.
  3. Tick Offline (or switch off the computer's Wi-Fi) and reload. Expected result: the app shell still loads instantly; the list of games comes from the network-first fallback if you have loaded it before, or the offline page appears if you have not.
  4. Change CACHE to gameshelf-v3, reload twice, and check Cache Storage: the old gameshelf-v2 cache is deleted after the new worker activates.
  5. Run Lighthouse and read the installability and PWA checks it reports; fix anything it lists, such as a missing icon size or a manifest without a start_url.

Browsers decide for themselves when to offer installation. The usual conditions are a secure connection, a linked manifest with a name, icons and a start URL, and (in some browsers) a registered service worker, so check the current requirements for the browsers you support rather than assuming them.

Worked example: accessible, inclusive markup

Accessibility is built into the markup, not added afterwards. This fragment gives a form a visible label, a status message announced by screen readers and a landmark structure for navigation:

HTML: labelled form, announced status and landmarks
<header>
  <nav aria-label="Main">
    <a href="/" aria-current="page">Home</a>
    <a href="/games">Games</a>
  </nav>
</header>
<main>
  <h1>Find a game</h1>
  <form>
    <label for="max">Maximum price (AUD)</label>
    <input id="max" name="max" type="number" min="0" inputmode="decimal" required>
    <button type="submit">Search</button>
  </form>
  <p id="status" role="status">3 games found.</p>   <!-- read out when its text changes -->
</main>
  • The label is tied to the input with for and id, so a screen reader announces "Maximum price" and a tap on the label focuses the box.
  • Real button, nav and main elements give keyboard focus, key handling and navigation landmarks for free; a clickable div would need all of that added by hand.
  • role="status" makes a screen reader announce a change to the message without moving focus.
Quick accessibility testHowPass looks like
Keyboard onlyPut the mouse aside; use Tab, Shift+Tab, Enter and SpaceEvery control is reachable, the focus outline is visible and the order makes sense
Zoom and text sizeZoom the browser to 200% and narrow the windowNothing is cut off and no horizontal scrolling for text content
ContrastCheck text against its background (see the W3C section)At least 4.5:1 for normal text
Not by colour aloneView the page in greyscaleErrors and states are still clear from icons or words
MediaPlay any video without sound; check for controlsCaptions are available and nothing autoplays with sound
MotionTurn on "reduce motion" in the operating systemLarge animations stop or shrink (prefers-reduced-motion)
Exam tip: "Design, develop and implement" a PWA NESA's 2025 online paper compared a PWA with an interactive website for a weather-app scenario (one published exam). A strong answer names what makes an app progressive (a manifest for installation, a service worker for offline use, HTTPS), says which strategy the worker uses for which files and why, and ties the choice to the scenario: weather data should use network first, because a cached forecast can mislead, while the app shell can be cache first. For UI and UX questions, tie each principle to a user need: contrast for low-vision users, captions for deaf users, keyboard navigation for people who cannot use a mouse, and consistent navigation for everyone. Explain trade-offs rather than only stating rules: a PWA has one code base, but fewer device features than a native app.