Skip to content

← Grade 11

Clients, Servers and the Web

Unit 6 · Networks and the internet

Hide the stories, facts, photos, activities and interactives, for revising before an exam
In this unit 15 sections
  1. Clients and servers
  2. The layers, in depth
  3. Ports: which program?
  4. How TCP makes delivery reliable
  5. How the internet differs from other networks
  6. HTTP in depth
  7. HTTPS and certificates
  8. JSON in depth
  9. Web APIs and their documentation
  10. Building both sides
  11. What runs where
  12. Designing the database behind it
  13. Project: a lost-and-found board
  14. In short
  15. Exercise

You tap “Check balance” in a mobile wallet. Half a second later, a number appears. It wasn’t on your phone. Your phone asked a computer you will never see, perhaps in a building in Kathmandu, perhaps in another country, and that computer looked it up and answered. Almost every app works like this: the part on your phone asks, and a program far away answers.

In Grade 9 you followed a web page on its way to your phone, and in Grade 10 your programs asked a weather API for data. This unit goes further in both directions. Underneath: how the layers of the internet really wrap and pass on a message, how a program finds the right program on another computer, and how TCP gets every byte there over a network that loses packets and flips bits, how phones share the air of one Wi-Fi network, and how routers learn their way. On top: how HTTP requests and answers are built, how a certificate proves who you are talking to, how a web app is made of a page, a server program and a database, and how to design that database so its data can never contradict itself. By the end you will have built both sides of a small web app yourself.

Clients and servers

When two programs talk over a network, one usually starts the conversation and the other waits for it. The program that asks is the client. The program that waits, listening for requests and answering them, is the server. Your browser is a client; so is the wallet app on your phone. The program at the bank that answers “What is this account’s balance?” is a server.

Notice that “server” names a program, and only by habit the computer it runs on. One computer can run several servers at once: a web server, an email server and a database server, each waiting for its own kind of request. And a phone can be a server too, when it shares its internet as a hotspot and hands out addresses (Grade 9’s DHCP).

This way of working, clients asking and servers answering, is called the client–server model. Every exchange is a request followed by a response. The client always starts the conversation, and the server answers it.

Rows of black server computers in tall metal racks in a data center, with small blue and white lights
Figure 6.1 Servers in a data center, here some of those that answer requests for Wikipedia. Each flat box is a computer with no screen or keyboard, built to run server programs all day and night. Photo: Victor Grigas, CC BY-SA 3.0

Which of these is acting as a server?

Pick an answer to see why.

The layers, in depth

In Grade 9 you met the four layers of the TCP/IP model: application, transport, internet and link. Each layer does one job and uses the layer below it. Now look at exactly what each layer adds, and what happens to it on the way.

Wrapping and unwrapping

Say your browser wants the list from a school’s lost-and-found board. The request it writes is a few lines of text, about 120 bytes. Before it leaves your phone, each layer wraps it, adding a header of its own in front: the information that layer needs to do its job.

Layer What its header holds Size of the header
Application (HTTP) the request itself: method, address, headers (it is the data)
Transport (TCP) the two port numbers (which program at each end), a sequence number (which bytes these are), an acknowledgment number (what has arrived from the other side), and a checksum at least 20 bytes
Internet (IP) the two IP addresses (the two ends), the TTL (Grade 9’s time to live: how many more routers may pass it on), and which transport protocol is inside at least 20 bytes (IPv4)
Link (Wi-Fi, Ethernet) the two MAC addresses of this one link, and a check at the end Ethernet: 14 bytes in front, 4 at the end

Putting each layer’s package inside the next layer’s is called encapsulation. The receiving computer does the opposite: each layer reads its own header, takes it off, and hands what is inside up to the layer above. A piece of data at the transport layer is called a segment; with its IP header, a packet; with its link header, a frame.

There is a limit to how much one frame can carry: on Ethernet, 1,500 bytes inside the frame. Take away 20 bytes for IP and 20 for TCP, and each packet carries at most 1,460 bytes of your data (a little less when the headers carry extra options, or a link allows less). So a 2 MB photo travels as about 1,400 packets, each wrapped on its own.

One request, hop by hop

Here is the important part. The two ends’ IP addresses and port numbers stay the same all the way, but the link layer’s wrapping is only for one hop. Each router takes it off, reads the IP address, chooses the next hop (Grade 9’s routing table), takes 1 off the TTL, and wraps the packet again for the next link, with new MAC addresses. Follow it:

Try it

Follow one request

Press Next to follow a request from a phone to a server, and the answer back. The yellow values are the ones that changed at this hop. Watch which change at every hop, which change only once, and what nobody in the middle can read.

PhoneHomerouterInternetcompany9 moreroutersServerthe request

1. Phone → home router, by Wi-Fi

Link (Wi-Fi) from …53:0ato …53:01
Internet (IP) from 192.168.1.23to 198.51.100.20TTL 64
Transport (TCP) from port 52114to port 443
Application (HTTP) 7f 3a c1 9e 04 b2 5d … (scrambled)

The browser’s request, encrypted by TLS, is wrapped three times: TCP adds the ports, IP the addresses of the two ends, Wi-Fi the addresses of this one link.

Step 1 of 7
What you should see

A packet leaves a phone with a TTL of 64 and passes through 12 routers before it reaches the server. What is its TTL when it arrives?

Pick an answer to see why.

On its way from a home router to a server in another country, which part of a packet is replaced at every hop?

Pick an answer to see why.

Ports: which program?

An IP address gets a packet to the right computer. But a computer runs many programs that use the network at once: a browser with five tabs, a chat app, an email app, a music app. Which one is this packet for? That is the job of the port number, a number from 0 to 65,535 (16 bits) in the TCP or UDP header.

A server program listens on a fixed port that everyone knows, so clients know where to knock:

Port Who listens there
80 a web server, for HTTP
443 a web server, for HTTPS
53 a DNS server
22 an SSH server, for remote login
25 an email server, receiving mail from other servers

A client doesn’t need a well-known port. When your browser opens a connection, your phone gives it a temporary port, usually a high number such as 52114, used only for this connection and given to another one later.

A connection is four numbers

Ten students in a lab open the same school website at once. The web server gets ten connections on port 443. How does it keep their answers apart? Each TCP connection is known by four numbers: the client’s IP address and port, and the server’s IP address and port.

Client address Client port Server address Server port
203.0.113.7 40001 198.51.100.20 443
203.0.113.7 40002 198.51.100.20 443
192.0.2.88 51530 198.51.100.20 443

The first two are two tabs in the same home (same public address, different ports); the third is someone else. No two connections have the same four numbers, so every segment that arrives belongs to exactly one of them. One end of a connection, an IP address with a port, is called a socket, and it is also the name of what a program asks its operating system for when it wants to use the network.

This is also how a home router’s NAT (Grade 9) shares one public address among a whole family. It gives each connection going out a port of its own on the public side and writes it in a table: “public port 40001 means 192.168.1.23, port 52114”. When an answer comes back to port 40001, the table says whose it is.

Two browser tabs on the same laptop are both open on the same website. How does the laptop know which tab each arriving segment is for?

Pick an answer to see why.

How TCP makes delivery reliable

What IP doesn’t promise

IP does its best to deliver each packet, and promises nothing more. This is called best-effort delivery. On the way a packet can be:

  • lost: a busy router whose queue is full has no room for it and throws it away; radio noise on Wi-Fi or mobile data spoils it;
  • damaged: a bit flips on the way; the checks at each layer notice, and the packet is thrown away, so damaged becomes lost;
  • out of order: two packets take different routes, and the second arrives first;
  • duplicated: a copy is sent twice, and both arrive.

Noticing a flipped bit

How can a receiver tell that a bit flipped on the way? The sender adds a little extra, worked out from the data, and the receiver works it out again from what arrived. If the two don’t agree, the data was damaged.

The simplest extra is one bit. Take the letter C in 7-bit ASCII (Grade 7): 1000011. It has three 1s. With even parity, the sender adds an eighth bit, the parity bit, chosen to make the number of 1s even: here a 1, so the byte sent is 11000011. The receiver counts the 1s again. An even count: accept. An odd count: something changed, so refuse the byte and ask for it again. (With odd parity, both ends agree to make the count odd instead; it works the same way.)

Try it

A parity bit on the way

The byte below was sent with even parity. Be the noise on the line: tap a bit of the received byte to flip it. Flip one bit, then a second, then a third. Try the parity bit itself.

Sentthe letter C
11000011
Receivedreads as C
Letter sent:
Bits flipped
0
1s received
4 (even)
Receiver
accepts “C”

Nothing changed on the way: an even number of 1s, so the receiver accepts the byte. Tap a bit of the received byte to flip it.

What you should see

Send several bytes as a parity block and the receiver can find the bit too. Each byte has its own parity bit, and after the last byte comes a parity byte, whose bits make each column’s count of 1s even. One flipped bit makes its byte’s count odd and its column’s count odd: the bit where that row and that column cross is the one that flipped, and the receiver can flip it back.

TCP doesn’t use parity. Its checksum is a 16-bit number in the TCP header: the sender adds up the whole segment as 16-bit numbers and sends the total, and the receiver adds it up again. It is stronger than a parity bit: it catches every single flipped bit and most bigger damage, including most pairs of flipped bits. But not all: if a 0 becomes 1 in one 16-bit piece and a 1 becomes 0 in the same place in another, or two pieces swap places, the total doesn’t change. So the link layer adds a stronger check of its own, the 4 bytes at the end of every Ethernet or Wi-Fi frame, a cyclic redundancy check (CRC), which catches almost everything a noisy link does. A segment that fails any check is thrown away, and TCP sends it again.

A byte is sent with even parity as 01101001 and arrives as 01100001. What does the receiver do?

Pick an answer to see why.

Three ideas that make TCP reliable

Yet when you download a PDF of your textbook, every byte arrives, in order. TCP, running only on the two ends, turns IP’s best effort into a reliable stream. It does it with three ideas.

  1. Number the bytes. Each segment carries a sequence number: the number of its first byte. Each side starts counting from a random number, chosen when the connection is set up.
  2. Say what arrived. The receiver sends back an acknowledgment (ACK) with the number of the next byte it wants. “ACK 1009” means “I have everything up to byte 1008; send 1009 next.”
  3. Send again what wasn’t acknowledged. The sender keeps a copy of everything it has sent until it is acknowledged, and runs a timer. If no acknowledgment comes in time, it sends the oldest unacknowledged segment again. This is called retransmission. (Real TCP doesn’t always wait for the timer: when the same acknowledgment comes back three extra times, it guesses that a segment was lost and sends it at once. The widget below keeps only the timer.)

The receiver uses the sequence numbers to put segments back in order, keeps early ones until the gap before them is filled, and throws away any duplicate. Only then does it hand the bytes to the program, in order.

Try it

Lose a piece on the way

A message of 24 bytes goes in six segments of 4 bytes, numbered from byte 1001. One piece is set to be lost: press Send and watch what TCP does. Then lose two pieces, or none, and try UDP.

SenderReceiverpiecesACKs1001BLUE10011005␣BAG10051009␣FOU✕10091013ND,␣10131017LIBR10171021ARY.1021The app gets:
Lose on the way:
Protocol:
Pieces sent
0
Lost on the way
0
Arrived
0 of 6

Choose which pieces are lost on the way (tap a number again to stop losing it). Then press Send.

What you should see

A connection from start to end

Before any data, the two ends set up the connection with the three-way handshake you met in Grade 9. Now you can see what it is for: each side tells the other its random starting number, and each confirms the other’s. A segment with the SYN flag says “here is my starting number”; ACK says “I got yours”. At the end, each side sends FIN to say it has nothing more to send.

Phone(the client)Serverport 4431. Setting up: the three-way handshakeSYNseq 7000SYN-ACKseq 3000, ack 7001ACKack 30012. The request and the answerGET /items (120 bytes)seq 7001200 OK and the list (900 bytes)seq 3001, ack 7121ACKack 39013. ClosingFINseq 7121ACK and FINseq 3901, ack 7122ACKack 3902time ↓
Figure 6.2 A whole TCP connection, time going down. SYN and FIN each count as one byte, so the handshake’s acknowledgments are 7001 and 3001. The request is 120 bytes (7001 to 7120), so the server acknowledges 7121; the answer is 900 bytes (3001 to 3900), so the phone acknowledges 3901.

Why random starting numbers? If every connection started at 0, a delayed segment from an old connection between the same two sockets could arrive and be taken for part of a new one. A random start makes that almost impossible, and makes it much harder for an attacker to slip in a fake segment by guessing the numbers.

Why does the receiver send an ACK with the number of the next byte it wants, rather than a list of every segment it got?

Pick an answer to see why.

Not too fast: flow and congestion

Sending one segment and waiting for its ACK would be slow: on a link to a server abroad, the round trip can take a tenth of a second or more. So TCP keeps many segments in flight at once, as many as a window allows. The window is limited twice:

  • by the receiver: each ACK also says how much room it has left, so a fast server can’t drown a slow phone. This is flow control.
  • by the network: TCP treats a lost segment as a sign that some router on the way is overloaded, and slows down; while nothing is lost, it speeds up again, a little at a time. This is congestion control, and it is what lets millions of connections share the same cables politely.

UDP: when late is as bad as lost

On a video call with your cousin in Qatar, a piece of the picture that arrives a second late is useless: the call has moved on. TCP would hold up everything behind the missing piece until it was sent again, and the call would freeze. So video calls, online games and live streams usually send their sound and pictures over UDP (Grade 9): no handshake, no acknowledgments, no retransmission, no waiting. The app copes with a lost piece itself, by blurring a moment of picture or guessing a few milliseconds of sound.

UDP still has port numbers and a checksum; it just doesn’t promise delivery. An app that needs some reliability on top of UDP builds its own, as QUIC does: a newer transport protocol, standardized in 2021, which carries HTTP/3. It runs over UDP but numbers, acknowledges and resends its data itself, and sets up an encrypted connection in fewer round trips than TCP followed by TLS.

TCP UDP
Connection set up first (handshake) none: just send
Delivery every byte, in order, or the program is told it failed best effort: some may be lost or out of order
Acknowledgments and resending yes no
Slows down when the network is busy yes only if the app does it
Header at least 20 bytes 8 bytes
Used for web pages, email, files, payments, SSH video and voice calls, games, live streams, DNS questions, QUIC

A file is being downloaded by TCP over a poor mobile signal, and some packets are lost. What happens?

Pick an answer to see why.

How the internet differs from other networks

The internet isn’t the only big network. The old telephone network joined every telephone in the world too. But it worked in a completely different way.

When your grandparents rang Biratnagar from a landline in Bhaktapur, the exchanges set up a circuit: a path through the network reserved for that call alone, from end to end, for as long as the call lasted. Nobody else could use that capacity, even during the silences. This is circuit switching. It suits a voice call perfectly: once the circuit is set up, the sound flows with no delays and nothing lost. But it is wasteful for computers, which send in bursts and are silent most of the time, and if any exchange on the path failed, the call was cut.

The internet uses packet switching (Grade 7): nothing is reserved. Every packet finds its own way, and every link is shared by everyone’s packets, one after another. Links are used far more fully, a broken router is simply routed round, and the cost is that packets can be delayed or lost when links are busy, which TCP then repairs.

Telephone network (circuit switching) Internet (packet switching)
Before talking a path is set up and reserved nothing: just send packets
Sharing a link one call owns its share of a link everyone’s packets take turns
When it is busy a new call can’t get through (“all lines are busy”) everyone’s packets slow down, and some are lost
If a switch or router fails the call is cut packets find another route
Where the cleverness is in the network: the exchanges at the ends: the computers, TCP and the apps
New uses need the network to change anyone can invent one on top (the web, video calls)

The last two rows are the internet’s great idea, often called the end-to-end principle: keep the middle of the network simple, just passing packets on, and put everything else, reliability, encryption, the meaning of the data, in the computers at the ends. That is why no one had to ask permission from the network to invent the web, a payment app or a video call. Phone calls are moving to packets too: on a 4G network and a phone that both support it (VoLTE, voice over LTE), a call travels as IP packets like everything else; otherwise the phone drops back to the older 2G or 3G network for the call.

Why did a phone call on the old telephone network waste capacity that the internet doesn’t?

Pick an answer to see why.

Sharing the air: how Wi-Fi takes turns

A cable from a switch to a computer carries only those two devices’ signals. Radio is different: every phone and laptop on a Wi-Fi network sends on the same channel, and if two send at the same moment, their signals mix at the access point and neither can be read. That is a collision. Wi-Fi’s rule for taking turns is called CSMA/CA (carrier sense, multiple access, with collision avoidance):

  1. Listen first. If anyone is sending, wait until the air is quiet.
  2. Wait a random time. Even when the air is quiet, wait a random number of tiny time slots before sending. Two phones that were both waiting for the same quiet moment almost always pick different waits; the one with the shorter wait goes first, and the other hears it and waits again.
  3. Send, and wait for an acknowledgment. The receiver answers every frame with a short ACK. No ACK means the frame was lost, most likely in a collision: the sender waits again, picking from a range of waits twice as long each time, and sends again.

Why avoid collisions rather than notice them? A radio can’t hear anyone else while it is sending, because its own signal drowns everything, so it learns of a collision only from the missing ACK. (Early Ethernet, with many computers on one shared cable, could listen while it sent and stop at once: CSMA/CD, collision detection. Today each computer has its own cable to a switch, so Ethernet collisions don’t happen.)

There is one more trap. Two laptops at the two ends of a hostel corridor may both reach the access point in the middle but not hear each other: each thinks the air is quiet, and they collide at the access point. This is the hidden station problem. Wi-Fi can avoid it with RTS/CTS: before a long frame, a laptop sends a short request to send (RTS); the access point answers with a clear to send (CTS), which every device in range of the access point hears, and which says how long the air is booked for. Everyone else stays quiet until then.

Now picture a hostel on the night before an exam, forty phones on one access point. On a channel, for the most part, only one device sends at a time. The more devices, the more often two pick the same random wait and collide, the more waits double, and the more airtime goes on frames sent twice. A phone with a weak signal at the end of the corridor sends at a slower rate, so each of its frames holds the air longer while everyone waits; and a neighbor’s network on the same channel shares the same air. The internet connection behind the access point may be fast; the air in front of it is the bottleneck.

How routers learn their routes

In Grade 9, a router chose the next hop from its routing table. Who writes that table? A home router needs only one useful line, typed in once: “send everything else to the ISP”. That is a static route, set by hand. The routers of an ISP, and between ISPs, can’t work like that: links break, new ones are added, and no one could keep thousands of tables right by hand. So they use dynamic routing: each router talks to its neighbors with a routing protocol, telling them which addresses it can reach and how far away they are, and builds its own table from what they tell it. When a link breaks, the routers at its ends notice, tell their neighbors, and within seconds or minutes the tables everywhere show a way round it; that is how “a broken router is simply routed round”. Inside one organization’s network, routers use protocols such as OSPF; between networks, an ISP in Nepal and one in India, the protocol is BGP. Routers believe what their neighbors tell them: on 24 February 2008, Pakistan Telecom, told to block YouTube inside Pakistan, announced a route to YouTube’s addresses that leaked to the rest of the world, and for about two hours much of the internet sent YouTube’s traffic to Pakistan, where it was thrown away.

In CSMA/CA, why does a phone wait a random time before sending, even when the air is quiet?

Pick an answer to see why.

A cable between two cities breaks. How do the internet’s routers find a way round it?

Pick an answer to see why.

HTTP in depth

In Grade 9 you saw that HTTP is the web’s protocol: a browser asks, a server answers, in short lines of text. Now read the messages closely, the way a web developer reads them every day.

Reading a request

A request

POST /items HTTP/1.1Host: lostfound.example.comUser-Agent: Mozilla/5.0 (Linux; Android …) …Content-Type: application/jsonContent-Length: 81Cookie: lang=en {"what": "a blue water bottle", "place": "the library", "found_on": "2026-10-20"}
  • The request line: the method (POST), the path (/items) and the version of HTTP.
  • Headers, one per line, a name and a value: Host, the website wanted (one server often holds many); User-Agent, which browser and system is asking; Content-Type, what kind of data the body is (here JSON); Content-Length, how many bytes the body is; Cookie, values this website asked the browser to keep (below).
  • A blank line, which ends the headers.
  • The body: the data being sent. A GET request usually has none.

Reading a response

The response

HTTP/1.1 201 CreatedContent-Type: application/jsonContent-Length: 9Date: Sat, 24 Oct 2026 06:22:19 GMT {"id": 7}

The status line says how it went, in a status code and a few words. The first digit gives the kind of answer:

Code Means Example
2xx success 200 OK: here it is. 201 Created: the new thing was made. 204 No Content: done, nothing to send back.
3xx go elsewhere 301 Moved Permanently: it lives at the address in the Location header now. 304 Not Modified: the copy you already have is still right.
4xx the client’s mistake 400 Bad Request: the request is wrong (say why). 401 Unauthorized: sign in first. 403 Forbidden: signed in, but not allowed. 404 Not Found: nothing at that address. 429 Too Many Requests: slow down.
5xx the server’s mistake 500 Internal Server Error: the server’s program failed. 503 Service Unavailable: overloaded or down for now; try later.

The first digit tells a program what to do even with a code it has never seen: a 4xx means fix the request before trying again; a 5xx means the same request may work later.

Methods: what the request wants done

Method Means Changes anything?
GET send me this no
POST here is something new: add it yes
PUT put this at that address, replacing what is there yes
PATCH change part of what is at that address yes
DELETE remove what is at that address yes

A GET must never change anything. Browsers, search engines and link previews in chat apps send GET requests by themselves, to fetch a page ahead of time or to show a preview. If “GET /items/2/delete” deleted item 2, a chat app showing a preview of that link would delete it.

A program sends a request and gets 503 Service Unavailable. What should it do?

Pick an answer to see why.

Which request should never change any data on the server?

Pick an answer to see why.

HTTP remembers nothing: cookies

HTTP is stateless: each request stands alone, and the server remembers nothing about you between one request and the next. Yet a website keeps you signed in for days. How? With a cookie: a small value the server asks the browser to keep, which the browser then sends back with every request to that website.

Try it

Staying signed in

Step through how a member of staff signs in and stays signed in. Notice what travels in each message, and what the server has to look up every time.

Your browserServerDatabase
Step 0 of 8

Mr. Shrestha, the school’s office assistant, signs in to the lost-and-found board to remove things that have been collected. HTTP remembers nothing between requests, so how does the server know it is still him? Press Next step.

The value that keeps you signed in is a session ID: a long random number that means nothing by itself, and that the server looks up in its own list of sessions. Two settings on a cookie protect it: Secure (send it only over HTTPS) and HttpOnly (the page’s JavaScript can’t read it). Unit 7 shows what an attacker can do with a stolen one.

HTTPS and certificates

In Grade 9 you saw the steps of HTTPS: the browser connects, the server sends a certificate, the browser checks it, and the two agree on a key that encrypts everything after it. Grade 12 shows the mathematics. Here, look at the certificate itself, and at how your browser decides to believe it.

What a certificate says

A certificate is a small file that says, in effect: “This public key belongs to the owner of these names, from this date to that date. Signed, a certificate authority.” It holds:

  • the names it is for, such as bhakaripay.com.np and www.bhakaripay.com.np (Bhakari Pay is the made-up wallet of Grades 7 and 9);
  • the site’s public key (Grade 9), which only the owner’s secret key matches;
  • the dates it is valid from and to;
  • who issued it, and that issuer’s digital signature over all of the above.

Before signing, a certificate authority (CA) checks that whoever asks really controls the name, for example by asking them to put a given value on the website or in its DNS. A thief can’t get a certificate for bhakaripay.com.np without controlling bhakaripay.com.np.

A chain of trust

Your browser, or your phone’s operating system, comes with a list of well over a hundred root certificates: the public keys of the certificate authorities it trusts. A root’s secret key is so precious that it is kept locked away, offline, and used rarely. So a root signs an intermediate certificate, and the intermediate signs the websites’ certificates, day after day. When you connect, the server sends its own certificate and the intermediate’s; the browser follows the signatures up the chain of trust until it reaches a root in its own list.

The browser connects only if three things are true:

  1. The chain of signatures leads to a root in its list.
  2. The certificate is for the very name in the address bar.
  3. Today is between the certificate’s dates (and it hasn’t been withdrawn).

Try it

Would your browser trust it?

Try each site. Read the certificate chain, see which check fails, and what the browser does. Then look hard at the last one.

https://bhakaripay.com.npExample Root Authorityin your browser’s list ✓signsExample TLS Authority 2signed by the rootsignsCertificate for bhakaripay.com.npvalid 14 Sep 2026 to 13 Dec 2026signed by Example TLS Authority 2
  • ✓Signed, step by step, by an authority already in your browser’s list
  • ✓Made for the name in the address bar
  • ✓Today is between its dates

The browser connects.

Every check passes, so the browser connects and shows the site. This really is the owner of bhakaripay.com.np, and nobody on the way can read or change what you send.

What you should see

A certificate has the right name and dates, but was signed by an authority that isn’t in the browser’s list (and doesn’t lead to one). What does the browser do?

Pick an answer to see why.

JSON in depth

In Grade 10 you read JSON answers from an API into Python’s dictionaries and lists. Now you will write JSON too, because a client sends data to a server as JSON, and a server answers in it.

Python’s json module goes both ways. json.dumps() (“dump to a string”) turns a Python value into JSON text; json.loads() turns JSON text back into a Python value.

import json item = {"what": "पानीको बोतल", "place": "पुस्तकालय", "collected": False,        "photo": None, "size_cm": (7, 24)} text = json.dumps(item)print(text)print(json.dumps(item, ensure_ascii=False)) back = json.loads(text)print(back["size_cm"], back == item)

Output

{"what": "\u092a\u093e\u0928\u0940\u0915\u094b \u092c\u094b\u0924\u0932", "place": "\u092a\u0941\u0938\u094d\u0924\u0915\u093e\u0932\u092f", "collected": false, "photo": null, "size_cm": [7, 24]}{"what": "पानीको बोतल", "place": "पुस्तकालय", "collected": false, "photo": null, "size_cm": [7, 24]}[7, 24] False

Three things to notice:

  • False and None became JSON’s false and null.
  • By default, json.dumps() writes every character that isn’t in ASCII as a \u code (Grade 7’s Unicode code points, in hexadecimal). It is still correct JSON, and reads back the same, but it is six bytes per letter instead of three, and no person can read it. ensure_ascii=False keeps the Devanagari as it is.
  • JSON has no tuples, only arrays, so (7, 24) went out as [7, 24] and came back as a list. The value that came back isn’t equal to the one that went out. JSON has only six kinds of value: objects, arrays, strings, numbers, true/false and null; anything else must be turned into one of them before it is sent (a date into a string such as "2026-10-20").

JSON is stricter than it looks. Its rules, written in one short standard: strings and names in double quotes only; no comma after the last item; no comments; true, false and null in small letters. A program that reads JSON refuses anything else, with a JSONDecodeError that says where it gave up:

import json text = "{'what': 'a black umbrella', 'place': 'the school gate'}"try:    item = json.loads(text)except json.JSONDecodeError as error:    print("Not JSON:", error)

Output

Not JSON: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

That text is how Python prints a dictionary, with single quotes, and it isn’t JSON. Never build JSON by gluing strings together; let json.dumps() write it, and it is always right.

Which of these is valid JSON?

Pick an answer to see why.

Web APIs and their documentation

A web app’s server answers two kinds of client: browsers asking for pages, and programs (including the page’s own JavaScript) asking for data. The second kind of answer, data for a program, is a web API (Grade 10). A well-designed API is predictable: each kind of thing has an address, a resource (/items, /items/2), and the HTTP method says what to do with it. This style, using HTTP’s own methods and status codes for what they mean, is often called REST, from a description of the web’s design written by Roy Fielding in 2000.

Reading an API’s documentation

A programmer learns an API from its documentation, which must say, for every request: the method and address, what to send, what comes back, and every error. Here is the documentation for the lost-and-found board’s API, which you will build in this unit:

Request Send Answer
GET /items nothing 200, a JSON array of every item, newest first: [{"id": 3, "what": …, "place": …, "found_on": …}, …]
POST /items a JSON object with what (up to 60 characters), place (up to 40) and found_on (a date, YYYY-MM-DD, not in the future) 201 and {"id": <the new item’s id>}; or 400 and {"error": "<what is wrong>"}
DELETE /items/<id> a staff member’s session cookie 200; 401 if not signed in as staff; 404 if there is no such item
any other method on /items 405
any other address 404

Every error answer is JSON with an "error" that says what is wrong in words a person can act on. Good documentation is half of a good API: if it isn’t written down, a programmer can only guess, and their program breaks the day the guess turns out wrong.

Building both sides

Now build the app: first the server part, then the page that talks to it.

The server’s part

A server program is, at heart, a function: a request goes in, a response comes out. Here is the lost-and-found board’s, as a module, lostfound.py. It keeps its items in an SQLite database (Grade 10 Unit 3) through Python’s sqlite3 module. On this page the database lives in memory, inside your browser; on a computer, one changed line keeps it in a file.

lostfound.py

"""The lost-and-found board's server part: a request in, a response out."""import jsonimport sqlite3from datetime import date db = sqlite3.connect(":memory:")   # on a computer: "lostfound.db", kept on the diskdb.execute("""CREATE TABLE IF NOT EXISTS items (    id INTEGER PRIMARY KEY,    what TEXT NOT NULL,    place TEXT NOT NULL,    found_on TEXT NOT NULL)""") LONGEST = {"what": 60, "place": 40, "found_on": 10}  def answer(status, data):    """A response: the status code, and the body as JSON text."""    return status, json.dumps(data, ensure_ascii=False)  def problem(item):    """What is wrong with an item sent to us, or None if nothing is."""    if not isinstance(item, dict):        return "Send one item as a JSON object."    for key, longest in LONGEST.items():        value = item.get(key)        if not isinstance(value, str) or not value.strip():            return key + " must be some text."        if len(value) > longest:            return key + " is too long."    try:        found = date.fromisoformat(item["found_on"])    except ValueError:        return "found_on must be a date, such as 2026-10-20."    if found > date.today():        return "found_on is in the future."    return None  def handle(method, path, body=""):    """The server: one request in, one response out."""    if path == "/items" and method == "GET":        rows = db.execute("SELECT id, what, place, found_on FROM items ORDER BY id DESC")        return answer(200, [{"id": i, "what": w, "place": p, "found_on": f}                            for i, w, p, f in rows])    if path == "/items" and method == "POST":        try:            item = json.loads(body)        except json.JSONDecodeError:            return answer(400, {"error": "The body must be JSON."})        wrong = problem(item)        if wrong:            return answer(400, {"error": wrong})        new = db.execute("INSERT INTO items (what, place, found_on) VALUES (?, ?, ?)",                         (item["what"].strip(), item["place"].strip(), item["found_on"]))        db.commit()        return answer(201, {"id": new.lastrowid})    if path == "/items":        return answer(405, {"error": method + " is not allowed here."})    return answer(404, {"error": "Nothing at " + path})

Read it as the documentation’s table, in code:

  • Lines 42 to 45: GET /items reads every row, newest first, and turns the rows into a list of dictionaries, which answer() turns into JSON.
  • Lines 46 to 57: POST /items reads the body as JSON, refusing anything that isn’t (400). Then problem() checks every value: present, text, not empty, not too long, a real date, not in the future. Only a clean item reaches the database.
  • Lines 54 and 55: the values go into the SQL as ? placeholders, never glued into the SQL text. Unit 7 shows the attack that this one habit stops.
  • Lines 58 to 60: anything else gets 405 or 404, with a reason.

(The DELETE request with its staff sign-in, in the documentation, is left out to keep the program short; the widget below has it.)

Now be a client. This program imports the module and sends it requests directly, as the HTTP layer will later:

from lostfound import handle print(handle("POST", "/items", '{"what": "a blue water bottle", "place": "the library", "found_on": "2026-10-20"}'))print(handle("POST", "/items", '{"what": "a calculator", "found_on": "2026-10-21"}'))print(handle("POST", "/items", '{"what": "a pen", "place": "Room 4", "found_on": "2099-01-01"}'))print(handle("GET", "/items"))print(handle("DELETE", "/items"))print(handle("GET", "/teachers"))

Output

(201, '{"id": 1}')(400, '{"error": "place must be some text."}')(400, '{"error": "found_on is in the future."}')(200, '[{"id": 1, "what": "a blue water bottle", "place": "the library", "found_on": "2026-10-20"}]')(405, '{"error": "DELETE is not allowed here."}')(404, '{"error": "Nothing at /teachers"}')

Only the first request changed the database. The others were refused before reaching it, each with a status code and a reason.

A client that expects failure

A client must look at the status code before it trusts the body, and treat each kind of answer differently, as the status code table said: success, the client’s own mistake (tell the user what to fix), or the server’s (try again later). Here is a client written that way:

import jsonfrom lostfound import handle  def report(what, place, found_on):    """Send a report, as the page's JavaScript will, and say how it went."""    body = json.dumps({"what": what, "place": place, "found_on": found_on})    status, text = handle("POST", "/items", body)    data = json.loads(text)    if status == 201:        print("Saved as item", data["id"])    elif 400 <= status < 500:        print("Not saved:", data["error"])    else:        print("The server failed. Try again later.")  def show_list():    status, text = handle("GET", "/items")    if status != 200:        print("Couldn't get the list.")        return    for item in json.loads(text):        print(item["id"], item["what"], "-", item["place"], "-", item["found_on"])  report("कालो छाता", "the school gate", "2026-10-19")report("a red pencil case", "", "2026-10-22")report("a math guide", "Room 4", "22 October")show_list()

Output

Saved as item 1Not saved: place must be some text.Not saved: found_on must be a date, such as 2026-10-20.1 कालो छाता - the school gate - 2026-10-19

Over a real network, a client must also expect a third kind of failure: no answer at all. The internet is down, the server is off, or it is too slow. On a computer, Python’s standard library sends real requests with urllib.request, which raises HTTPError when the server answers 4xx or 5xx, and URLError when there is no answer. A function that handles all three:

ask.py

"""Sending a request to a real server, on a computer, and handling every failure."""import jsonfrom urllib.error import HTTPError, URLErrorfrom urllib.request import Request, urlopen  def send(method, address, data=None):    """Returns (status, data), or (None, None) if no answer came."""    body = json.dumps(data).encode("utf-8") if data is not None else None    request = Request(address, data=body, method=method,                      headers={"Content-Type": "application/json"})    try:        with urlopen(request, timeout=10) as response:            return response.status, json.loads(response.read())    except HTTPError as error:              # an answer, but 4xx or 5xx        return error.code, json.loads(error.read())    except (URLError, TimeoutError, ConnectionError):  # no answer at all        return None, None

The timeout=10 matters: without it, a program waiting for a server that never answers waits for ever. (This file needs a real network connection, so it can’t run on this page; you will use it in the project.)

The browser’s part

The page is HTML and JavaScript (Grade 9). The new part is fetch(), the browser’s way for a page’s JavaScript to send an HTTP request and get the response, without loading a new page:

part of index.html

async function showList() {  const response = await fetch("/items");  const items = await response.json();  const list = document.querySelector("#list");  list.innerHTML = "";  for (const item of items) {    const li = document.createElement("li");    li.textContent = item.what + ", " + item.place + ", " + item.found_on;    list.append(li);  }} form.addEventListener("submit", async (event) => {  event.preventDefault();  const item = {    what: document.querySelector("#what").value,    place: document.querySelector("#place").value,    found_on: document.querySelector("#found_on").value,  };  try {    const response = await fetch("/items", {      method: "POST",      headers: { "Content-Type": "application/json" },      body: JSON.stringify(item),    });    const answer = await response.json();    if (response.ok) {      message.textContent = "Thank you! Saved as item " + answer.id + ".";      form.reset();      showList();    } else {      message.textContent = "Not saved: " + answer.error;    }  } catch (error) {    message.textContent = "Couldn't reach the server. Is it running?";  }});
  • A request over the network takes time, and the page mustn’t freeze while it waits. A function marked async can await something slow: it pauses there, the page carries on, and the function continues when the answer arrives.
  • JSON.stringify() and response.json() are JavaScript’s json.dumps() and json.loads().
  • response.ok is true for a 2xx status. The page shows the server’s own error message otherwise, and a message of its own if no answer came at all (the catch).
  • Line 8 puts each item on the page with textContent, never innerHTML, so text someone typed can only ever show as text. Unit 7 shows why that matters.

Try the whole app

Here are all three parts working together: the page in a phone, the server, and its database, all simulated inside this page. Use the page as anyone would, then go round it.

Try it

Report something you found

First use the page: report something you found, and watch the request, the server’s steps, the database and the response. Try reporting with an empty box. Then choose Write the request and send the ready-made requests, first with the server’s checks On, then Off. Then edit the session cookie in the last one.

Browserthe pageServerthe programDatabasethe table
lostfound.example.com

Lost and found

Server checks:

The request

The response

What the server did

The database: table items

idwhatplacefound_on
1a math guide, Grade 11Room 42026-10-29
2a black umbrellathe school gate2026-10-31
3a red pencil casethe canteen2026-11-03

The page has just asked the server for the list (GET), and the server read it from the database. Fill in the form and report something, or write a request yourself.

What you should see

The parts, drawn

A diagram of a web app shows its hardware and its software, and how they talk:

Figure 6.3 The lost-and-found board’s parts. The phone is hardware; the browser and the page are software on it. HTTPS requests on port 443 cross the internet to a server computer in a data center, which runs an operating system, a web server program that handles HTTPS, and the app’s own program. Only the app’s program talks to the database, in SQL, and the data lives on the server’s disk.

Notice that the page and the database never talk directly. Everything the page wants, it asks the server for, and the server decides. That is the next section’s point.

In the diagram, why doesn’t the page in the browser connect straight to the database?

Pick an answer to see why.

What runs where

When you design a web app, every piece of work goes in one of two places: in the browser (the client), or on the server. The rule that decides most of them:

The browser belongs to the user. The server belongs to you.

Anything in the browser, the user can see and change. The page’s JavaScript, its hidden form fields, anything it is sent, can be read in the developer tools; and anyone can send your server any request at all, from a program of their own, as you did in the widget. So:

  • Work that is only for the user’s comfort can run in the browser: showing and hiding things, a message as they type, remembering their choice of tab.
  • Anything that must be true for everyone runs on the server: who may do what, prices and totals, the data itself, every secret (passwords, keys, the database’s password).
  • Checks run in both: in the browser to help an honest user at once, and on the server because a request can come from anywhere.

Try it

Browser, server or both?

For each job in a web app, tap where it should run.

Checking a password against the stored hash

Where should this run?

Tap a group above.

Card
1 of 12
Right the first time
0

Designing the database behind it

The lost-and-found board keeps one table. A real one soon wants more: which student claimed which item, which member of staff handed it over, who is signed in. Before writing any CREATE TABLE, a designer draws the data, then tests the design for the faults that let copies of a fact drift apart. In Grade 10 you linked tables by their keys and wrote each fact once; now you will draw a design the way database designers and exam papers do, and test a table against exact rules.

Entities, attributes and relationships

An entity is a kind of thing the app keeps data about: an item, a student, a member of staff, a claim. Each entity becomes a table, and each single thing (one blue water bottle) a row. An attribute is a fact about an entity, such as what, place and found_on; each becomes a column. Every entity has a primary key, and a foreign key holds the primary key of a row in another table (Grade 10). A relationship says how two entities are linked, and how many of one can go with one of the other:

  • one-to-one: one school has one principal (rare in a database: the two often become one table);
  • one-to-many: one member of staff has many sessions, and each session belongs to one member of staff;
  • many-to-many: a student can claim many items over the years, and an item can be claimed by more than one student (two students both say the blue bottle is theirs).

An entity–relationship diagram (ER diagram) draws all of this. In crow’s-foot notation, each entity is a box with its name and attributes, each relationship a line, and the marks at each end of a line say how many: a bar means one, a fork (the crow’s foot) means many, a ring means none is allowed. Each end has two marks: the one next to the box is the most there can be, the other the fewest.

A many-to-many relationship can’t be stored as it is: which single item_id would a student’s row hold? So it is resolved into a new entity between the two, with a one-to-many relationship to each. Here that is claim: one row for one student claiming one item, with facts of its own (when, its status, who handed it over). Grade 10’s marks table was the same kind of thing, between students and subjects.

hasmakeshands overhasitemitem_idPKwhatplacefound_onclaimitem_idPK, FKstudent_idPK, FKhanded_byFKclaimed_onstatusstudentstudent_idPKnameclassstaffstaff_idPKnamephonesessionsession_idPKstaff_idFKexpiresexactly onezero or oneone or manyzero or many hasmakeshands overhasitemitem_idPKwhatplacefound_onclaimitem_idPK, FKstudent_idPK, FKhanded_byFKclaimed_onstatusstudentstudent_idPKnameclassstaffstaff_idPKnamephonesessionsession_idPKstaff_idFKexpiresexactly onezero or oneone or manyzero or many
Figure 6.4 The lost-and-found board’s database as an ER diagram. PK marks a primary key, FK a foreign key; claim’s primary key is the pair (item_id, student_id). Read each line from both ends: one item has zero or many claims, and each claim is for exactly one item; a member of staff hands over zero or many claims, and a claim is handed over by zero or one member of staff (none until it is collected).

In an ER diagram, the line between class and student has two bars at the class end and a ring and a fork at the student end. What does it say?

Pick an answer to see why.

Normal forms: one fact, one place

Is a design good? Edgar Codd, who invented relational databases (Grade 10), gave the test in 1970 and 1971: a set of rules called the normal forms. A table that breaks them keeps some fact in several rows, and that leads to three kinds of fault, called anomalies:

  • update anomaly: a fact is written in several rows, and a change made to some of them and not others leaves the table saying two things at once;
  • insertion anomaly: one fact can’t be recorded without another, unrelated fact;
  • deletion anomaly: deleting one fact deletes another that was still wanted.

Here is a school office’s fee register, kept the way it grew, as one table:

Receipt Paid on Student ID Name Class Class teacher Fee a month Months paid Amount
1001 2026-04-20 101 Aarati Shrestha 9A Pratibha Shakya 3000 Baisakh, Jestha 6000
1002 2026-04-22 102 Bibek Thapa 9A Pratibha Shakya 3000 Baisakh 3000
1003 2026-04-23 131 Sujan Dahal 10A Ramesh Acharya 3400 Baisakh, Jestha, Asar 10200
1004 2026-04-27 145 Rojina Rai 10B Sushila Manandhar 3400 Baisakh 1700
1005 2026-05-18 102 Bibek Thapa 9A Pratibha Shakya 3000 Jestha 3000

(Rojina has a half scholarship, so she pays 1,700 a month.) Now take it through the normal forms, one step at a time.

First normal form

The Months paid field holds a list. A program can’t easily ask “who has paid for Jestha?” or add up a month’s fees, and the Amount covers all the months together. A table is in first normal form (1NF) when every field holds one single value (no lists, no repeating groups such as Month1, Month2, Month3) and a primary key picks out each row. So write one row for each receipt and each month paid. No single field is unique now (receipt 1003 has three rows), but the pair (receipt_no, month) is: a composite key. That is the table in the playground below.

1NF cures the lists, but look at what each row repeats: the date, the student, the class, the teacher and the fee, once for every month on the receipt.

Try it

An update anomaly

This is the fee register in 1NF, named fee_register, in a real database in this page. The office finds that receipt 1003 was paid on 24 April, not 23 April, and corrects one row. The query has already run: read its answer. Then press Reset, and write an UPDATE that corrects the date properly.

Loading the tables…

underlined = primary key; → a foreign key, and the table it points to

Rows
–

Starting the database in this page…

What you should see

Second normal form

Why does the date repeat? A field depends on another when knowing the one tells you the other. Knowing the receipt number, 1003, tells you the date, the student and the class; knowing the month alone tells you none of them. So paid_on depends on only part of the key, receipt_no: a partial dependency. A table is in second normal form (2NF) when it is in 1NF and has no partial dependencies: every non-key attribute depends on the whole key. (A table whose key is a single column can’t break this rule.)

The cure: move the attributes that depend on part of the key into a table of their own, keyed by that part. The date, student and class go into receipts, one row per receipt; the month and the amount paid for it, which depend on the whole key, stay in receipt_months. In SQL, make the two tables, then copy the data into them from the old register; SELECT DISTINCT gives each receipt once:

CREATE TABLE receipts (  receipt_no INTEGER PRIMARY KEY,  paid_on TEXT NOT NULL,  student_id INTEGER NOT NULL,  student_name TEXT NOT NULL,  class_id TEXT NOT NULL,  class_teacher TEXT NOT NULL,  monthly_fee INTEGER NOT NULL);INSERT INTO receiptsSELECT DISTINCT receipt_no, paid_on, student_id, student_name,  class_id, class_teacher, monthly_feeFROM fee_register;CREATE TABLE receipt_months (  receipt_no INTEGER REFERENCES receipts (receipt_no),  month TEXT,  amount INTEGER NOT NULL,  PRIMARY KEY (receipt_no, month));INSERT INTO receipt_monthsSELECT receipt_no, month, amount FROM fee_register;

The playground below has done this step. But receipts still repeats things. Try these in it, one at a time:

  1. A new class, 10C, starts next month, with Nisha Silwal as class teacher and a fee of 3,400. Record it: INSERT INTO receipts (class_id, class_teacher, monthly_fee) VALUES ('10C', 'Nisha Silwal', 3400);
  2. Receipt 1004 was entered by mistake. Delete it, then look for class 10B: DELETE FROM receipt_months WHERE receipt_no = 1004; DELETE FROM receipts WHERE receipt_no = 1004; SELECT * FROM receipts WHERE class_id = '10B';

Try it

Take it to 2NF

This database has run the SQL above on the register, and its query shows the new receipts table: each receipt’s date is now written once. Try the two changes above in it, one at a time.

Loading the tables…

underlined = primary key; → a foreign key, and the table it points to

Rows
–

Starting the database in this page…

What you should see

Third normal form

In receipts, the key is receipt_no. But student_name depends on student_id, and class_teacher and monthly_fee depend on class_id, and neither of those is the key. Receipt 1002 tells you student 102, and student 102 tells you Bibek Thapa: the name depends on the key only through another field. That is a transitive dependency (some exam boards say a non-key dependency). A table is in third normal form (3NF) when it is in 2NF and has no transitive dependencies: no non-key attribute depends on another non-key attribute. William Kent put all three rules in one sentence in 1983: every non-key field must give “a fact about the key, the whole key, and nothing but the key”.

The cure is the same move again: each group of facts goes to the table of the thing it is about. The class’s teacher and fee go to classes, the student’s name and class to students, and receipts keeps only what belongs to the receipt, with a foreign key to the student. The four tables of the finished design, each with its primary key and foreign keys, filled from the old register:

CREATE TABLE classes (  class_id TEXT PRIMARY KEY,  class_teacher TEXT NOT NULL,  monthly_fee INTEGER NOT NULL);CREATE TABLE students (  student_id INTEGER PRIMARY KEY,  name TEXT NOT NULL,  class_id TEXT NOT NULL REFERENCES classes (class_id));CREATE TABLE receipts (  receipt_no INTEGER PRIMARY KEY,  paid_on TEXT NOT NULL,  student_id INTEGER NOT NULL REFERENCES students (student_id));CREATE TABLE receipt_months (  receipt_no INTEGER REFERENCES receipts (receipt_no),  month TEXT,  amount INTEGER NOT NULL,  PRIMARY KEY (receipt_no, month));INSERT INTO classesSELECT DISTINCT class_id, class_teacher, monthly_fee FROM fee_register;INSERT INTO studentsSELECT DISTINCT student_id, student_name, class_id FROM fee_register;INSERT INTO receiptsSELECT DISTINCT receipt_no, paid_on, student_id FROM fee_register;INSERT INTO receipt_monthsSELECT receipt_no, month, amount FROM fee_register;

The playground below has done this step. Try these in it, one at a time:

  1. INSERT INTO classes VALUES ('10C', 'Nisha Silwal', 3400);
  2. UPDATE classes SET class_teacher = 'Kamal Neupane' WHERE class_id = '9A';
  3. DELETE FROM receipt_months WHERE receipt_no = 1004; DELETE FROM receipts WHERE receipt_no = 1004; SELECT * FROM students;
  4. DELETE FROM students WHERE student_id = 101;
  5. SELECT r.receipt_no, m.month, r.paid_on, s.name, c.class_id, c.class_teacher, m.amount FROM receipt_months m JOIN receipts r ON m.receipt_no = r.receipt_no JOIN students s ON r.student_id = s.student_id JOIN classes c ON s.class_id = c.class_id ORDER BY r.receipt_no;

Try it

Take it to 3NF

This database has run the SQL above on the register, and its query shows the classes. Try the five queries above in it, one at a time (you can copy and paste them).

Loading the tables…

underlined = primary key; → a foreign key, and the table it points to

Rows
–

Starting the database in this page…

What you should see

A table marks has the key (student_id, subject_id) and the fields mark and student_name. Which normal form does it break, and why?

Pick an answer to see why.

When to break the rules on purpose

The 3NF design is right for the data the office changes: each fact in one place, every change one change. But each question now needs joins. Suppose the principal’s screen shows how much each class paid for each month, and is opened hundreds of times a day, while receipts are added a few times a day. Adding up every receipt, with two joins, every time the screen opens is wasted work.

So a designer may denormalize on purpose: keep a copy of the answer, ready-made, in a report table:

DROP TABLE IF EXISTS month_report;CREATE TABLE month_report ASSELECT m.month, s.class_id, COUNT(*) AS paid, SUM(m.amount) AS collectedFROM receipt_months mJOIN receipts r ON m.receipt_no = r.receipt_noJOIN students s ON r.student_id = s.student_idGROUP BY m.month, s.class_id;SELECT * FROM month_report;

Run it in the 3NF playground above (before step 3, or after Reset), and month_report has six rows; Baisakh’s 9A row, for example, says 2 receipts paid 6,000. Reading it needs no joins and no adding up. The price: the copy can go stale. After step 3 deletes receipt 1004, month_report still counts Rojina’s 1,700 until it is made again. So the rules for a report table are: the 3NF tables stay the truth; the report table is only a copy, rebuilt from them (every night, or each time a receipt is added); and nothing is ever changed in the copy itself. Grade 12 shows the same trade made on a far bigger scale, when services keep copies of their data on many computers.

When does it make sense to keep a denormalized report table?

Pick an answer to see why.

Project: a lost-and-found board

You need a computer with Python 3 (Grade 8 Unit 1 showed how to install it, and Grade 10 Unit 6 how to use the command line). Work in pairs. Everything here runs on your own computer; nothing goes online.

With only a phone: a server needs a program that keeps running and listens on a port, which the Python on this page can’t do. On an Android phone, a terminal app with Python can (the box below says how; install it only with a teacher’s help). Then every step works on the phone except step 5, which needs a computer’s developer tools: read the lines the server prints instead. With no terminal app, do what a phone allows: for step 7, press Edit on the client program above (“A client that expects failure”), put the whole of lostfound.py in place of its line from lostfound import handle, make your change and Run it; send step 6’s requests to the widget’s server (Write the request); and draw step 8’s diagram on paper.

  1. Make a folder called lostfound. Save lostfound.py (above) in it, and change its ":memory:" to "lostfound.db", so the items are kept on the disk.
  2. Save the server. This file turns real HTTP requests into calls to handle(), and sends back its answers. It uses a class, as in Unit 1, from Python’s standard library module http.server:

server.py

"""The lost-and-found board's server: run it, then open http://localhost:8000"""from http.server import BaseHTTPRequestHandler, HTTPServerfrom lostfound import handle  class Board(BaseHTTPRequestHandler):    """Turns real HTTP requests into calls to handle(), and back."""     def send(self, status, text, kind="application/json"):        data = text.encode("utf-8")        self.send_response(status)        self.send_header("Content-Type", kind + "; charset=utf-8")        self.send_header("Content-Length", str(len(data)))        self.end_headers()        self.wfile.write(data)     def do_GET(self):        if self.path == "/":            with open("index.html", encoding="utf-8") as file:                self.send(200, file.read(), "text/html")        else:            self.send(*handle("GET", self.path))     def do_POST(self):        length = int(self.headers.get("Content-Length", 0))        body = self.rfile.read(length).decode("utf-8")        self.send(*handle("POST", self.path, body))  print("Open http://localhost:8000 in a browser. Press Ctrl+C to stop.")HTTPServer(("localhost", 8000), Board).serve_forever()
  1. Save the page as index.html. Put the JavaScript above where the comment says:

index.html

<!DOCTYPE html><html lang="en"><head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width, initial-scale=1">  <title>Lost and found</title></head><body>  <h1>Lost and found</h1>  <form id="report">    <p><label>What did you find? <input id="what" required maxlength="60"></label></p>    <p><label>Where? <input id="place" required maxlength="40"></label></p>    <p><label>When? <input id="found_on" type="date" required></label></p>    <button>Report it</button>  </form>  <p id="message"></p>  <ul id="list"></ul>  <script>    const form = document.querySelector("#report");    const message = document.querySelector("#message");     // the JavaScript above goes here: showList() and form.addEventListener(…)     showList();  </script></body></html>
  1. Run it: in the folder, python3 server.py (on Windows, python server.py). Open http://localhost:8000. Report three things. Stop the server with Ctrl+C, start it again: are they still there?
  2. Watch it: open the developer tools’ Network tab. Report something and find the POST and the GET the page sent. Read their headers, bodies and status codes. Look at the server’s window: it prints a line for every request.
  3. Be the other client: with the server running, open a second terminal and send it requests with ask.py: send("GET", "http://localhost:8000/items"), then a POST with an item that has no place, then one with a date in the future. Do you get the answers the documentation promises? Stop the server, send again: what does send() return?
  4. Improve it (choose one): add GET /items/<id>; add a collected field and a request that marks an item collected; or let a member of staff delete an item, with a password checked on the server. Update the documentation table first, then the code, then test every row of the table.
  5. Draw your app’s diagrams: one like Figure 6.3, its hardware, its software, the protocols and ports between them, marking where each check runs; and an ER diagram of its database, like Figure 6.4, with any table you added in step 7. Check each table against 1NF, 2NF and 3NF.
  6. Present to the class: show the app working, one request you read in the developer tools, one bad request your server refuses, and your diagrams.

In short

Exercise

What is a socket?

Pick an answer to see why.

A receiver has all the bytes up to 4,800 and is missing 4,801 to 5,300, but has received 5,301 to 5,800. What acknowledgment does it send?

Pick an answer to see why.

Which header tells a server which of the websites it holds is wanted?

Pick an answer to see why.

A browser shows a full-page warning: the certificate is for www.lalupatebank.com.np, but you typed lalupatebank-login.com. What is the right thing to do?

Pick an answer to see why.

A server program receives a POST whose JSON has no “place”, and it crashes. What status code does the browser most likely get?

Pick an answer to see why.

With odd parity, which byte has a parity bit (the first bit) that is wrong?

Pick an answer to see why.

In a hostel, a laptop at each end of a long corridor can reach the access point but can’t hear the other laptop. What helps them stop sending at the same time?

Pick an answer to see why.

A table bookings has the key booking_id and the fields bus_no, travel_date, seat, driver_name, where each bus has one driver. Which normal form does it break?

Pick an answer to see why.

a.

Name the four layers of the TCP/IP model. For each, say what its header holds, and whether a router on the way reads it.

Answer
b.

Explain, with an example, why a server needs port numbers as well as an IP address. Why does a browser get a temporary port, while a web server uses 443?

Answer
c.

A message of 3,000 bytes is sent by TCP in segments of 1,000 bytes, starting at sequence number 801. The second segment is lost the first time. Write down, in order, each segment sent and each acknowledgment, until everything has arrived.

Hint
Answer
d.

Compare TCP and UDP on four points. Which would you use for (i) sending a scanned marksheet, (ii) a live video lesson, (iii) a DNS question? Give reasons.

Answer
e.

Explain how the internet differs from the old telephone network. Give two advantages of the internet’s way and one disadvantage.

Answer
f.

Read this request and answer the questions.

PUT /notices/12 HTTP/1.1Host: board.example.comContent-Type: application/jsonCookie: session=7Hq2… {"title": "Football match moved to Friday"}

a. What does the client want done? b. What is the body, and what kind of data is it? c. Why is there a Cookie header? d. What status code would you expect if it works, if there is no notice 12, and if the session belongs to a student who may not edit notices?

Answer
g.

What does a website’s certificate contain? Describe the three checks a browser makes before it trusts one, and give one thing a valid certificate does not prove.

Answer
h.

Write the Python dictionary below as JSON, then say what json.loads() would give back for each value: {"district": "Ilam", "tea": True, "elevation_m": (500, 3600), "notes": None}.

Answer
i.

A shop’s website works out the total in the page’s JavaScript and sends {"item": "school bag", "price": 1800} to the server, which charges that price. Explain the flaw, how someone could use it, and how to fix it.

Answer
j.

Draw a diagram of a web app that lets students of your school check the day’s notices on their phones. Show the hardware and the software at each part, the protocols and port numbers between them, and where the notices are stored. Mark where a check that “only teachers may post notices” must run, and explain why there.

Answer
k.

A program asks an API for data and gets no answer for a minute. Explain what a well-written client should do, and why it needs a timeout. Then give the right reaction to each of these answers: 200, 400, 401, 404, 429, 503.

Answer
l.

a. Write the letter K (1001011 in 7-bit ASCII) as a byte with an even parity bit in front. b. It arrives as 11001001. What does the receiver find, and what does it do? c. Give an example of damage that the parity bit would miss, and say why TCP’s checksum and the link layer’s CRC are still needed.

Answer
m.

A school library lends books. Each member (student_id, name, class) may borrow many books, and a book (book_id, title, author) is borrowed by many members over the years; each loan has a date out and a date back. a. Name the entities, with their attributes and primary keys. b. Describe the relationships, and resolve any many-to-many relationship. c. Describe the ER diagram you would draw in crow’s-foot notation, saying the marks at each end of each line.

Answer
n.

A bus company keeps this table of tickets: tickets (ticket_no, seat, travel_date, bus_no, route, driver, driver_phone, passenger_name, passenger_phone). Each bus always runs one route with one driver.

  • a.Identify one update anomaly, one insertion anomaly and one deletion anomaly this table allows.
  • b.Normalize it to third normal form. Write each table with its primary key, its foreign keys, and the normal form each step reaches.
  • c.Explain how referential integrity protects the new design.
Answer
o.

a. Explain how CSMA/CA lets many phones share one Wi-Fi channel, and why a hostel’s Wi-Fi is slow when everyone is online in the evening, even if its internet connection is fast. b. Explain the difference between a static route and dynamic routing, and why the internet’s core can’t work with static routes alone.

Answer