Adding Records
Giving users a form to submit new snail measurements
Concepts
- A POST request sends data in the request body rather than the URL
- appropriate for operations that create or modify server state
- GET requests should only retrieve data
- The Post/Redirect/Get (PRG) pattern:
after a successful POST, the server redirects the browser to a GET URL
- if the user refreshes, the browser re-issues the GET rather than re-submitting the POST, preventing duplicate insertions
hx-postsubmits a form via an HTMX request- the server returns only a fragment (e.g., a confirmation message) rather than a full page redirect, which is faster and less disruptive
- CSRF (Cross-Site Request Forgery): a malicious page on another
domain can cause the user's browser to submit a form to your server
using their active session
- CSRF tokens prevent this by requiring a secret value only your page knows
- HTML5 input types (
type="date",type="number",min,max,required) provide client-side validation that catches obvious mistakes before submission- cannot replace server-side validation
A POST route in Litestar
- Declaring a
@posthandler - Reading form data from the request body
- Litestar's
BodyandFormannotations
A data-entry form
- An HTML form with fields for each column
- Labels, input types, and a submit button in htpy
Inserting the new record
- Passing form data to a parameterized
INSERTstatement - Returning a redirect to the table page so refreshing does not re-submit
Partial page updates with HTMX
- Using
hx-postto submit the form - Swapping a confirmation message into the page instead of a full redirect
What the LLM missed
- CSRF tokens, duplicate-submission prevention, and audit logging
- For each: what it is, when it matters, and whether it matters here
Check for Understanding
What is the Post/Redirect/Get pattern and why does it prevent duplicate submissions on refresh?
After a successful POST, the server sends a redirect response (303 or 302) pointing to a GET URL (typically the updated table page). The browser follows the redirect and makes a GET request. If the user presses Refresh, the browser re-issues the GET, not the original POST. Without the redirect, refreshing would re-send the form data and insert a duplicate record.
If a form has method="post" and action="/add", what does the browser send to the server when the user clicks Submit?
An HTTP POST request to /add with a Content-Type: application/x-www-form-urlencoded
header. The request body contains the form fields as URL-encoded key-value pairs
(e.g., site=north-beach&diameter=14.2&mutated=true). The server must decode these
values from the request body rather than from the URL.
What is a CSRF attack, and why does it only affect state-modifying requests?
A CSRF attack tricks the user's browser into sending a request to your server using
the user's active session cookie. An attacker embeds a hidden form on their site that
auto-submits to your /add route; the browser sends the request with the victim's
cookies attached. It only affects POST/PUT/DELETE because GET requests should not
modify state; CSRF tokens work by including a secret in the form that the attacker
cannot know.
How does Litestar map HTML form fields to Python function parameters?
Litestar reads the Content-Type header to determine the encoding (form data vs.
JSON), then parses the body and maps field names to the corresponding function
parameters declared with Body(media_type=RequestEncodingType.URL_ENCODED) or a
similar annotation. Field names in the HTML form must match the parameter names
(or the fields in the annotated dataclass) exactly.
Exercises
Add client-side validation
Add HTML5 attributes to the form inputs to prevent obviously wrong values before
submission: required on all fields, min="0.1" and max="50" on the diameter
field, and type="date" on the date field. Test what happens when you submit the
form with an empty required field or a diameter of -1. Note what the browser does
versus what the server would have done.
Duplicate detection
Modify the server to check whether a measurement from the same site on the same date already exists before inserting. Decide whether to reject the duplicate with a 409 status, silently ignore it, or update the existing record. Implement your chosen behavior and add a brief comment explaining the rationale.
Multi-record upload
Add a route and form that accepts a CSV file upload and inserts all valid records
from it. Ask the LLM for the HTML <input type="file"> markup and the Litestar
handler code. Test with a file that contains a mix of valid and invalid rows and
decide what to do with each.
Audit the LLM's output
Ask the LLM to add CSRF protection to the data-entry form. Read the code it produces carefully: does it actually prevent the attack described in this lesson? What would a test that verifies CSRF protection look like? Note any gaps between what the LLM claimed and what the code actually does.