Weekend Challenges
Challenge 1 — POST Endpoint
Add a POST /api/languages endpoint to your Day 5 server in your preferred language. The endpoint should:
- Accept a JSON request body with
name,display,typing, andparadigmfields - Return
400 Bad Requestwith an error message if any required field is missing - Append the new language to the in-memory list and return it with status
201 Created
Test it with curl:
curl -X POST \
-H "Content-Type: application/json" \
-d '{"name":"rust","display":"Rust","typing":"static","paradigm":"systems"}' \
http://localhost:8000/api/languages
Then verify the new entry appears in the list:
curl http://localhost:8000/api/languages
After adding the POST endpoint, reload both / (SSR) and /app (CSR) in the browser. Both should show the new language — because both ultimately read from the same server-side list.
Test the validation: send a body with a missing field and confirm you get a 400 back, not a 500.
Challenge 2 — Template Engines
Building HTML with string concatenation works, but it becomes hard to maintain as pages grow. Template engines let you write HTML files with placeholder variables, and the server fills them in at request time.
Refactor your SSR route to use a template engine instead of an f-string or template literal:
- Python: Jinja2 (
uv add jinja2). Create atemplates/directory with anindex.htmlfile. UseJinja2Templatesfromfastapi.templatingto render it, passinglanguagesas a context variable. - Node.js: EJS (
npm install ejs). Configure Express withapp.set('view engine', 'ejs')and create aviews/index.ejsfile. Useres.render('index', { languages }). - C#: Razor Pages (built into ASP.NET Core). Create a
.cshtmlfile and use the Razor view engine to pass the language list to the template. - Go:
html/template(standard library, no extra dependency). Parse a template string or file withtemplate.Must(template.ParseFiles("index.html"))and execute it with the language data.
The goal is to separate the HTML structure from the Go/Python/JS/C# code. The server code handles data; the template handles presentation.
Challenge 3 — Language Detail View
Add a detail view to the CSR page from Day 4. When a user clicks "View details" on a language card, the page should:
- Fetch
GET /api/languages/{name}for that specific language - Render the detail in the same page — without a full page reload
- Show a "Back to list" link that restores the list view
This is the foundation of a single-page application: one HTML file, multiple views, no page reloads.
To get started, add a data-name attribute to each card button:
<button class="details-btn" data-name="${lang.name}">View details</button>
Then attach a click handler:
document.getElementById('list').addEventListener('click', async e => {
if (!e.target.matches('.details-btn')) return
const name = e.target.dataset.name
const response = await fetch(`${API}/api/languages/${name}`)
const lang = await response.json()
// render the detail view
})
Challenge 4 — Language-Agnostic Frontend
Build a single static HTML page (no server required — just open the file in a browser) with a dropdown that switches between two backends:
<select id="backend">
<option value="http://localhost:8000">Python (port 8000)</option>
<option value="http://localhost:3000">Node.js (port 3000)</option>
<option value="http://localhost:5000">C# (port 5000)</option>
<option value="http://localhost:8080">Go (port 8080)</option>
</select>
When the dropdown changes, re-fetch /api/languages from the selected backend and re-render the list. Confirm that switching backends returns identical data regardless of which language is serving it.
This exercise makes the language-agnostic nature of HTTP concrete: the frontend code is unchanged; only the origin changes.
Challenge 5 — Reflection Questions
Think through these questions and write short answers — a few sentences each is enough:
-
What are two situations where you would choose SSR over CSR? What are two where you would choose CSR? Give a concrete example for each.
-
When would you use both SSR and CSR in the same application? (Hint: think about which parts of a page need to be immediately visible vs which parts are highly interactive.)
-
How does disabling JavaScript in the browser affect SSR pages vs CSR pages? What does this reveal about their respective dependencies?
-
What does CORS protect, and why does it only apply to browser-initiated requests? Why can
curlcall any API regardless of CORS headers? -
In Day 5, the SSR page reads the
languageslist directly from server memory, while the CSR page fetches it from/api/languages. If you added a database later, which approach requires fewer changes and why?
Recommended Reading
- web.dev: Rendering on the Web — if you have not read it yet, this is the best single article on SSR vs CSR vs SSG and when to use each
- MDN: Progressive Enhancement — the philosophy behind building pages that work without JavaScript first
- The Twelve-Factor App — focus on factors III (Config), VI (Processes), and VII (Port Binding) as they relate to the servers you built this week
- MDN: HTTP caching — once your APIs are working, caching is the next lever for performance