Request In, Response Out
Hoplink has three jobs: answer GET /cat with a redirect, answer POST /links by creating a new short link, and say "not found" to everything else. All three are the same shape of problem — read the incoming request, decide, and hand back a response.
The rule
Read the Request, decide, return exactly one Response.
A Request object carries everything the visitor sent:
request.method—"GET","POST"and so on.request.url— the full URL as a string. Wrap it innew URL(...)to getpathnameandsearchParams.request.headers— read one withrequest.headers.get("user-agent").- The body — read it with
await request.json()orawait request.text(). A body is a stream, so it can be read once; userequest.clone()first if two pieces of code need it.
A Response is what you give back: a body plus a status and headers. Three helpers cover most cases:
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
if (request.method === "GET" && url.pathname === "/cat") {
return Response.redirect("https://example.com/all-about-cats", 302);
}
if (request.method === "POST" && url.pathname === "/links") {
const body = await request.json();
return Response.json({ created: body.code }, { status: 201 });
}
return new Response("No such link", { status: 404 });
},
};Response.redirect sets the Location header for you, Response.json sets content-type: application/json, and new Response(body, { status, headers }) handles everything else.
The status code is the first thing a browser, a crawler or another program reads, so choose it with care. 2xx means it worked, 3xx means "look elsewhere", 4xx means the caller asked for something wrong, and 5xx means your Worker failed. Headers carry the rest of the instructions: cache-control says how long a copy may be kept, and content-type says how to read the body. For Hoplink, a 302 keeps the door open to change a link's target later, while a 301 tells browsers the move is permanent and they may remember it for a long time.
The shortcut
Before writing any branch, fill in a three-line card: method + path → status + body. If you can fill the card, the code writes itself; if you cannot, the requirement is not clear yet.
Method and path
Deleting a link:
DELETE /links/cat. The code is the last piece ofurl.pathname.split("/").
Two traps: forgetting the final fallback return (a path that returns nothing makes the Worker throw an error), and reading the body twice, which fails because the stream is already used up.
Drills
Answer fast — trust the rule.
How do you get just the path, such as /cat, from a request?
Which call sends a visitor on to the long URL with a temporary redirect?
A logging helper calls await request.text(), then the handler calls await request.json(). What happens?
Which status fits a successful POST /links that created a new short link?
Remember
- Read the Request, decide, return exactly one Response.
- Shortcut: method + path → status + body, written down before the code.
- Traps: a missing fallback response, and reading a body twice.
