Skip to content

FTM401 · Examination paper

90 minutes · written · open book (notes) · no internet · 40 marks · pass 20


Section A — surface, service, file (16 marks, 8 × 2)

Section titled “Section A — surface, service, file (16 marks, 8 × 2)”

For each URL, name the surface, the service that answers, and the file you would open first.

  1. https://www.fixthemusic.com/browse/london
  2. https://musicians.staging.fixthemusic.com/pandemoniums/messages/
  3. https://admin.staging.fixthemusic.com/admin/booking/enquiry/
  4. https://www.fixthemusic.com/blog/some-post
  5. https://staging.fixthemusic.com/api/routing/
  6. https://musicians.staging.fixthemusic.com/musician-api/v0/messages/
  7. https://help.fixthemusic.com/ (or frontend-help)
  8. https://staging.fixthemusic.com/enquiries/u63n1ym6d1

Section B — cacheability (10 marks, 2 × 5)

Section titled “Section B — cacheability (10 marks, 2 × 5)”

For each, say whether the response can be cached by the CDN, what makes it cacheable or not, and the header you would set.

B1. A public band profile page. B2. The dashboard thread page above, as a logged-in musician.


"use client";
export default function Thread({ id }) {
const [msgs, setMsgs] = useState([]);
useEffect(() => { fetch(`/api/messages/${id}`).then(r => setMsgs(r.json())); }, []);
return msgs.map((m, i) => <div key={i}>{m.body}</div>);
}

Name three defects, the class of each, and the fix. One defect is about error handling, one about React keys, one about a promise.


Section D — supervise the change (6 marks)

Section titled “Section D — supervise the change (6 marks)”

You are asked to add a “mark all read” button to the dashboard thread list.

State: which surface and file, whether it needs a client boundary, how the request should be issued (Unpoly or fetch, and why), and what must be true for the action to be authorised.


SectionMarks
A2 each: 1 surface/service, 1 file
B5 each: 2 verdict, 2 reason, 1 header
C8: ~2.5 per defect, 0.5 for the fix
D6: 1 surface/file, 2 boundary, 2 request shape, 1 authorisation

Pass 20/40. Distinction 28.

Expected answers:

  • 1 public site, Next.js, frontend/src/app/browse/[[...slug]]/page.js
  • 2 dashboard, Django SSR, bands/dashboard/message_views.py + bands/templates/dashboard/
  • 3 admin, Django admin, booking/admin*.py
  • 4 Astro blog, Cloudflare Pages, frontend-blog/
  • 5 Django, routing.urls, ftm/urls.py:117
  • 6 Ninja API, bands/api_v2/routers/messages.py
  • 7 Astro (help), frontend-help/
  • 8 public site, Next.js, frontend/src/app/enquiries/
  • B1 cacheable; no session read; public, s-maxage=300, stale-while-revalidate=600
  • B2 not cacheable; session-dependent; private, no-store
  • C r.json() returns a promise — needs .then(r => r.json()).then(setMsgs) or an async fn; .then() with no .catch swallows failures (a 500 renders as empty); index keys break when the list reorders; and r.ok is never checked, so an error body is treated as data.
  • D dashboard, bands/dashboard/message_views.py + template; no client boundary needed (Unpoly form/link); up-target to the list fragment so only the list updates; authorisation must check band membership server-side per request, and the POST needs CSRF.