# Full Stack on Cloudflare — complete course study summary

Course: Matthew Sessions / Backpine. Prepared 13 September 2026.

Coverage: 59 videos, 11:29:33 of original video; all 58 supplied English transcripts read in full. Lesson 30 uses sampled video frames and has no narration transcript. Other lessons follow subtitles and may omit silent on-screen edits.

[Open the searchable HTML guide](cloudflare-course-guide.html) · [Concept reference and quizzes](cloudflare-concepts.html)

## How to study

Read one lesson, explain its recall answer from memory, then attempt its exercise. Reveal the answer only after trying. Rewatch the source timestamps where your explanation fails. Revisit the next day and a week later as a suggested review routine. Use your answers to decide which concepts need more practice.

## The connected system

Smart Links combines a management application with a public redirect/data service. The React/TanStack interface calls tRPC procedures in the user-application Worker, which uses shared Data Ops/Drizzle queries against D1. The data-service Worker uses Hono to look up link configuration through KV or D1, select the visitor-country override or default destination, and return a redirect.

A background helper sends clicks to Queues for persistence and scheduling, and directly to an account-scoped Durable Object for live-map updates. A separate scheduler object, keyed by link and destination, uses an alarm to trigger an evaluation workflow. Browser Rendering collects evidence, Workers AI classifies availability, R2 stores artifacts, and D1 stores the evaluation. The user-application Worker later authenticates API/socket requests; Stripe webhooks synchronize sandbox subscription activity. Paid-plan enforcement remains additional work.

Architecture synthesis: course lessons 01, 18, 22–24, 34–44, and 49–59.

## Study modules

### 01–03: Get a small app running

Establish the local-development and deployment loop before adding more services. The tangible result is a project you can run, change, and deploy.

Checkpoint: Explain the path from source files to a deployed Worker.

### 04–06: Understand the runtime and bindings

Framework code still runs inside an execution environment. Learn the Worker entrypoint, request lifecycle, environment, and resource bindings so configuration errors become understandable.

Checkpoint: Point to the entrypoint and explain how it reaches a database.

### 07–11: Build the application and data layer

The monorepo shares database operations and types across applications. React and TanStack provide the UI; tRPC connects it to server procedures; Drizzle queries D1.

Checkpoint: Trace a UI action through validation, a query, and the rendered result.

### 12–18: Route visitors to the right destination

A separate Hono service handles public smart-link traffic. It looks up link configuration, chooses a country-specific or default destination, and uses KV to speed up repeat reads.

Checkpoint: Predict the redirect for a country match, a missing match, and a missing link.

### 19–24: Move click processing into a queue

Click processing should not dominate the visitor's redirect latency. The producer sends an event and consumers validate, process, retry, or divert failed messages.

Checkpoint: Explain what happens if processing fails after a message is delivered.

### 25–34: Build and repair an AI evaluation workflow

Render a destination page, classify its availability, preserve evidence in R2, and save a structured result in D1. Lesson 34 repairs the earlier ordering to avoid oversized step results.

Checkpoint: Draw the final workflow with small values crossing step boundaries.

### 35–38: Schedule evaluations with Durable Objects

Repeated clicks should not launch an expensive evaluation every time. An object keyed by link and destination coordinates whether an alarm has already been scheduled.

Checkpoint: Distinguish a click-triggered delayed check from a perpetual daily schedule.

### 39–44: Stream a bounded view of live clicks

A SQL-backed Durable Object stores recent click events and broadcasts updates over WebSockets. Service bindings let the application Worker reach the data service internally.

Checkpoint: Describe what a new subscriber sees and where durable history differs from live delivery.

### 45–48: Separate stage and production

Environment-specific Workers, bindings, resources, builds, and routes turn a local experiment into repeatable deployments. Follow one consistent public URL design.

Checkpoint: Trace which database and services a stage build can access.

### 49–52: Add identity and server authorization

Better Auth introduces users and sessions. The UI checks session state, while the API must independently authenticate callers and restrict records to the current account.

Checkpoint: Explain why a hidden page or a disabled button is insufficient access control.

### 53–56: Integrate subscription billing

Stripe products and recurring prices feed a checkout flow, while webhooks synchronize subscription changes back to the application. The recorded deployment uses Stripe sandbox credentials on stage.

Checkpoint: Separate checkout completion, webhook synchronization, and feature entitlement enforcement.

### 57–59: Finish the dashboard and identify gaps

The final UI ties together the earlier features. Mocked unit tests cover selected business logic; the closing lesson assigns production work such as enforcing plan limits and extending tests.

Checkpoint: Name the behavior already demonstrated and the production work still left to implement.

## Corrections and course boundaries

### Use the patched workflow ordering

Course correction: lesson 34 supersedes the earlier workflow sequence. Generate the evaluation ID, collect and store large artifacts inside the first step, return text plus ID, classify with AI, then save the D1 row. A later recording may still display the older sequence. Current docs retain a 1 MiB limit for non-stream step results and also support streamed binary results; R2 references remain useful for large or long-lived artifacts.

Course lesson 34.
[Workflows limits](https://developers.cloudflare.com/workflows/reference/limits/)

### Queues are no longer a paid-only prerequisite

Current-doc update, checked 13 September 2026: the Queues pricing page lists a Workers Free allowance. Lesson 20's paid-only requirement reflects the recording. Learn how writes, reads, deletes, payload size, and retries affect cost; consult the linked plan pages before estimating a real workload.

Course lesson 20.
[Queues pricing](https://developers.cloudflare.com/queues/platform/pricing/)

### Delivery can repeat

Added implementation guidance: Queues provides at-least-once delivery. A consumer must tolerate the same message arriving again. Use a stable event ID and a duplicate-safe database operation or downstream idempotency key when repeating an action would be wrong. A TypeScript type or successful parse does not solve duplicate processing.

Course lesson 24.
[Queues delivery guarantees](https://developers.cloudflare.com/queues/reference/delivery-guarantees/)

### A fast cache can hold old routing data

Course caveat: lesson 17 uses a 24-hour TTL without demonstrating write-side invalidation. Current docs describe KV as eventually consistent, so an update may not become visible everywhere immediately. Decide how destination edits update or invalidate cached records and what stale behavior is acceptable.

Course lesson 17.
[How Workers KV works](https://developers.cloudflare.com/kv/concepts/how-kv-works/)

### Visitor location and Worker location differ

Terminology correction: request.cf is Cloudflare request metadata, not simply a collection of HTTP headers. Country and latitude/longitude describe the client's inferred location; colo identifies the Cloudflare data center serving the request. Use the former for country-based destination selection.

Course lesson 14.
[Cloudflare Request API](https://developers.cloudflare.com/workers/runtime-apis/request/)

### An alarm is not automatically a daily job

Course implementation: lessons 35–38 schedule a check after a click when no alarm exists, using link ID plus destination URL as the object identity. The demonstration delay becomes 24 hours. Continued daily checks without clicks require explicit rescheduling, which this demand-triggered path does not establish.

Course lesson 38.

### Live visualization is not complete event history

Course limitation: lessons 39–44 build a bounded recent-click feed. Initial-history replay is proposed but the later demonstration shows an empty new tab until another click. Timestamp offsets and a capped set of events should not be treated as a lossless accounting system.

Course lesson 44.

### A JWT is not inherently encrypted

Terminology correction: JWT claims can be carried in a signed/MACed token or an encrypted token. A signed token alone does not hide its payload. Cookies describe a storage/transport mechanism, so cookie-based sessions and JWTs are not mutually exclusive categories.

Course lesson 49.
[RFC 7519: JSON Web Token](https://www.rfc-editor.org/info/rfc7519/)

### Billing integration still needs entitlements

Course boundary: lesson 56 demonstrates publicly reachable webhooks on stage using Stripe sandbox, not real-money live mode. Lesson 59 explicitly leaves plan-limit enforcement as further work. Checkout and synchronized subscription records must be connected to server-side feature and quota checks before they enforce a paid product.

Course lesson 59.
[Better Auth Stripe plugin](https://better-auth.com/docs/plugins/stripe)

### Mocked tests leave runtime behavior unverified

Course boundary: lesson 58 mocks key dependencies to test selected business logic. Those tests do not by themselves verify real D1 migrations, bindings, queue retries, WebSocket authorization, or webhook delivery. Cloudflare's Vitest integration is a primary source for a separate runtime-testing layer.

Course lesson 58.
[Cloudflare Workers Vitest integration](https://developers.cloudflare.com/workers/testing/vitest-integration/)

### Match the build and URL design to the final code

Course progression: lesson 47 demonstrates Cloudflare Workers Builds with Git integration; configuring the data service is a follow-up exercise. Lesson 48 demonstrates /r/* routing, but 57 returns to go-stage/go subdomains. Choose one URL scheme and apply it consistently to generated links, bindings, and environment variables.

Course lesson 57.

### Check pagination instead of trusting a variable name

Observed caveat: lessons 10 and 30 describe a createdBefore cursor, but greater-than comparison appears in the narration or screen code. For descending history, verify older-page behavior with fixed timestamps and a tie-breaking key. The summary does not treat the demonstrated pagination as proven correct.

Course lesson 30.

### Set the authentication secret before exposure

Course sequencing caveat: lesson 56 fixes a Better Auth default-secret warning only late in the recording. When building your own application, configure a suitable secret in each deployed environment before exposing authentication. The successful final demo should not be read as endorsing the earlier temporary setup.

Course lesson 56.
[Better Auth basic usage](https://better-auth.com/docs/basic-usage)

### Verify every path to account data

Added implementation review: lesson 52 authenticates the application Worker’s tRPC and socket routes and forwards a verified account identity. It does not prove every detail/update query checks ownership or that a public data-service socket cannot bypass that middleware. Check direct backend reachability and record-level authorization; a trusted header must only come from a trusted caller.

Course lesson 52.
[Better Auth basic usage](https://better-auth.com/docs/basic-usage)

## Lesson-by-lesson notes

## 01. Intro & Overview

Getting Started · Original video 00:20:15 · Evidence: transcript

The course builds Smart Links, a short-link SaaS application whose redirects can vary by country and whose dashboard reports clicks and destination health. Its central lesson is how to divide a full-stack product into services with different responsibilities, then connect Cloudflare compute and storage products. React and the other libraries provide the working example; this is a systems course rather than a beginner framework tutorial.

Original video (course video 01) · [Focused study page](../lessons/0002-cloudflare-01-intro-overview.html)

### Understand the idea

- The user application contains both the browser interface and a thin backend for authentication, routing, and ordinary create/read/update operations.
- The data service owns public redirects, data processing, and longer background work, keeping these responsibilities separate from dashboard requests.
- Data Ops is shared source code: database schemas, reusable queries, Zod validation schemas, and types; it is not a third deployed service.
- HTTP requests, scheduled events, and queue messages can trigger backend work; Durable Objects, Workflows, browser rendering, and AI provide different capabilities.

### What the course does

1. Inspect the finished dashboard: create links, set a default destination, and configure country overrides.
2. Identify the two apps and shared Data Ops package in the pnpm workspace.
3. Trace dashboard data through its backend to the database, and distinguish that path from public link redirects.
4. Use the starter repository for following along and the completed repository or supplied snippets to compare work when stuck.

### Watch for

- The instructor explicitly assumes existing development knowledge and moves quickly through framework code.
- Added advice: treat the broad cost and platform superiority comparisons as the instructor’s opinions at recording time, not current pricing guidance.

### Recall and practice

**Question:** Why does this project have two applications and a shared package?

**Answer:** The apps separate user-facing CRUD/authentication from redirects and background processing; the package lets both reuse the same data definitions and queries.

**Try it:** Draw three boxes for the user application, data service, and Data Ops. Place link editing, public redirecting, and database query definitions in their appropriate boxes.

**Success check:** You can explain each responsibility and show that shared code is imported into deployable apps rather than called as another network service.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:07:00 (course video 01) — Course scope and pace
- 00:09:00 (course video 01) — Smart Links product demonstration
- 00:13:02 (course video 01) — User application versus data service responsibilities
- 00:15:02 (course video 01) — Shared package and request flow

Companion documentation: [Cloudflare Workers runtime APIs](https://developers.cloudflare.com/workers/runtime-apis/). Check installed versions before copying recorded commands.

## 02. Setting up the project

Getting Started · Original video 00:07:31 · Evidence: transcript

The starter is a pnpm monorepo containing two applications and one reusable package. The lesson gets the existing interface running, establishing the build order and the relationship between root convenience scripts and scripts inside each workspace package. The visible dashboard is still mostly a scaffold with dummy data, so a working page does not yet mean the backend features exist.

Original video (course video 02) · [Focused study page](../lessons/0003-cloudflare-02-setting-up-the-project.html)

### Understand the idea

- An apps directory groups independently deployable services; a packages directory groups code those services reuse.
- Each application has its own source and package.json, while the root provides workspace-wide commands.
- The Data Ops package must be built into its dist output before applications can consume the exported JavaScript and types.
- pnpm filtering selects a workspace by its package name; entering its directory and running its local script is another way to work on the same app.

### What the course does

1. Fork the starter into your GitHub account and clone that fork so later changes and deployment integration belong to your repository.
2. Inspect apps/user-application, apps/data-service, and the Data Ops package before changing code.
3. Install workspace dependencies with pnpm, then run the root helper that builds the shared package.
4. Start the frontend using the root filtered helper or run its dev script from the application directory.
5. Open the local interface on port 3000 and inspect the marketing page, dashboard, link editor, and placeholder analytics.

### Watch for

- A number of dashboard values, maps, and editor behaviors are dummy implementations at this point.
- Added advice: read the actual package.json script names rather than reconstructing hyphens from spoken subtitles.

### Recall and practice

**Question:** Why build Data Ops before starting an application that imports it?

**Answer:** The application consumes the package’s exported build output; the build creates the JavaScript and type files at those export paths.

**Try it:** In a separate course checkout, identify the workspace package names, build the shared package, and start the user application.

**Success check:** The dashboard opens locally, and you can point to the scripts and package that produced it.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:00 (course video 02) — Fork and clone the starter
- 00:01:00 (course video 02) — Monorepo structure
- 00:03:00 (course video 02) — Build the Data Ops package
- 00:04:01 (course video 02) — Root scripts versus app-local scripts

Companion documentation: [Cloudflare Workers runtime APIs](https://developers.cloudflare.com/workers/runtime-apis/). Check installed versions before copying recorded commands.

## 03. Deploy the project

Getting Started · Original video 00:05:50 · Evidence: transcript

The first deployment turns the working starter into a live Cloudflare Worker application. Wrangler reads the application configuration, builds the project through its deploy script, and uploads both static browser assets and server code. Deploying early makes the hosting process a familiar development step before the product becomes complicated.

Original video (course video 03) · [Focused study page](../lessons/0004-cloudflare-03-deploy-the-project.html)

### Understand the idea

- wrangler.jsonc describes the Worker name, server entrypoint, assets, and additional runtime configuration.
- Wrangler is the Cloudflare CLI used by the project’s deployment script; the script also performs the framework build.
- A full-stack deployment includes browser files such as JavaScript and CSS plus a server bundle that handles application requests.
- The workers.dev address identifies the deployed application, and the Cloudflare dashboard exposes deployment history, metrics, bindings, and settings.
- Observability must be enabled in the demonstrated configuration to surface the application’s logs.

### What the course does

1. Create or use a Cloudflare account and find the Workers area of its dashboard.
2. Inspect the user application’s Wrangler configuration and deployment script.
3. Run its deploy script from the application directory; complete the CLI’s browser authentication flow when prompted.
4. Wait for the build and upload to complete, then open the returned workers.dev URL.
5. Visit the deployed dashboard and inspect its Cloudflare deployment record, asset binding, and observability settings.

### Watch for

- The exact dashboard organization is the recording’s interface and can move over time.
- Added advice: deployment verifies the current starter state; dummy features remain dummy until their later backend lessons are implemented.

### Recall and practice

**Question:** What two kinds of output are uploaded when this application is deployed?

**Answer:** Static browser assets and the server-side Worker bundle; they serve different parts of the application.

**Try it:** Deploy the starter from your course checkout and trace its deployed name and entrypoint back to the Wrangler file.

**Success check:** The returned URL loads the same starter interface and the matching Worker appears in your account.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:39 (course video 03) — Locate the Wrangler configuration
- 00:01:59 (course video 03) — Build and Wrangler deploy script
- 00:03:04 (course video 03) — Authorize Wrangler access
- 00:04:28 (course video 03) — Inspect the deployed Worker dashboard

Companion documentation: [Cloudflare Workers runtime APIs](https://developers.cloudflare.com/workers/runtime-apis/). Check installed versions before copying recorded commands.

## 04. Understand the Worker Runtime

Building on Cloudflare Workers · Original video 00:11:45 · Evidence: transcript

This conceptual lesson explains why Workers use V8 isolates and how that differs from the instructor’s traditional-server and Lambda examples. The provider handles routing requests and making an execution environment available, leaving the application to implement handlers. A second distinction is active CPU work versus elapsed request time: waiting for another service is different from executing code.

Original video (course video 04) · [Focused study page](../lessons/0005-cloudflare-04-understand-the-worker-runtime.html)

### Understand the idea

- A single server can become a bottleneck or fail; distributing an application across servers adds resilience while increasing operational and idle-capacity costs.
- Serverless still uses servers, but their allocation and request routing are managed by the provider.
- A cold request may need an execution environment prepared before application code runs; a warm environment can often be reused.
- The course models Workers as request routing, isolate selection or creation, request-object construction, then application-handler execution.
- CPU time measures active computation. An API call that waits for a response can have much longer wall-clock duration than CPU duration.

### What the course does

1. Compare a single-server application with several servers behind a load balancer.
2. Follow the instructor’s serverless scheduling model, including cold and warm request paths.
3. Identify isolate creation as the lightweight environment step in the Workers model.
4. Use the example of an external AI request to separate application computation from network waiting.
5. Relate that distinction to the instructor’s explanation of Workers billing for requests and active CPU time.

### Watch for

- The detailed startup timings, Lambda internals, prices, and predictions are recording-time explanations, not measurements or guarantees for your application.
- Added advice: do not treat the instructor’s three-server production rule as universal; reliability depends on requirements and architecture.

### Recall and practice

**Question:** Why can a one-second request consume much less than one second of CPU time?

**Answer:** It may spend most of that second waiting on database or API I/O rather than actively executing application instructions.

**Try it:** Sketch a request that validates input, waits for an API, parses the response, and returns JSON. Mark which spans represent CPU work.

**Success check:** You distinguish elapsed time from active computation and can explain why isolate startup and application execution are separate concerns.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:00 (course video 04) — V8 isolates and the traditional server model
- 00:02:01 (course video 04) — Serverless allocation concept
- 00:07:00 (course video 04) — Workers request execution flow
- 00:10:00 (course video 04) — CPU work versus time waiting on APIs

Companion documentation: [Cloudflare Workers runtime APIs](https://developers.cloudflare.com/workers/runtime-apis/). Check installed versions before copying recorded commands.

## 05. Framework bundling, Entrypoints, & Env

Building on Cloudflare Workers · Original video 00:22:39 · Evidence: transcript

A framework becomes runnable on Workers when its build produces static assets and a compatible server entrypoint. The lesson inspects this boundary using a basic fetch handler, Hono, and a Next.js/OpenNext example, then explains how runtime environment bindings differ from copied Node.js configuration patterns. Understanding the build output helps debug both deployment problems and missing secrets.

Original video (course video 05) · [Focused study page](../lessons/0006-cloudflare-05-framework-bundling-entrypoints-env.html)

### Understand the idea

- A Worker fetch handler is the server interface; frameworks and adapters package their routing and server logic behind that interface.
- Client JavaScript, CSS, and other static assets form a different build output from code that executes on the server.
- Browser network requests for assets do not necessarily correspond to separate Worker invocations in the demonstrated asset configuration.
- Runtime bindings provide application variables, secrets, and resources; their access path depends on the framework integration used.
- Local .dev.vars values, generated environment types, deployed Worker secrets, and shared Secrets Store bindings solve related but different configuration needs.

### What the course does

1. Inspect a minimal fetch handler and see how Hono exposes a compatible handler.
2. Compare static chunks and the generated server Worker in the framework’s build output.
3. Use browser network inspection and Worker logs to distinguish asset delivery from server execution.
4. Define a local development secret, regenerate Cloudflare environment types, and access it through the demonstrated runtime context.
5. Configure the matching deployed secret through the Worker dashboard or CLI; inspect the shared Secrets Store alternative for several Workers.

### Watch for

- The discussion of Nitro, TanStack Start migration, process.env compatibility, Vite support, and Secrets Store maturity is version-specific.
- Keep .dev.vars out of Git as the instructor specifies; a local secret does not automatically create the deployed secret.
- Added advice: check current adapter documentation instead of treating the recording’s environment access syntax or asset billing examples as universal.

### Recall and practice

**Question:** Why can copying a database-client example from Node documentation fail in a Worker project?

**Answer:** The example may assume process.env or initialization timing that differs from the project’s Worker runtime and framework adapter.

**Try it:** Find your course app’s static output, server entrypoint, local secret configuration, and deployed secret configuration.

**Success check:** You can explain when each value or file is used and identify how server-only configuration reaches the request handler.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:00 (course video 05) — The fetch handler as the server contract
- 00:07:02 (course video 05) — Framework chunks and OpenNext output
- 00:13:00 (course video 05) — Local development variables and generated types
- 00:19:00 (course video 05) — Deployed secrets and shared secret storage

Companion documentation: [Cloudflare Workers runtime APIs](https://developers.cloudflare.com/workers/runtime-apis/). Check installed versions before copying recorded commands.

## 06. Binding resources to your Worker

Building on Cloudflare Workers · Original video 00:06:59 · Evidence: transcript

Bindings connect a Worker to a Cloudflare resource through its runtime environment. The lesson demonstrates the recurring pattern with KV: create a namespace, declare it in Wrangler, regenerate types, then read and write through the binding. The same discovery process extends to other Cloudflare products, reducing the amount of connection setup repeated in application code.

Original video (course video 06) · [Focused study page](../lessons/0007-cloudflare-06-binding-resources-to-your-worker.html)

### Understand the idea

- The resource exists independently of the Worker; a named binding gives that Worker access to it.
- The binding name is the property your code uses, whereas the resource ID identifies the configured Cloudflare resource.
- Wrangler’s configuration schema and generated environment types help discover and check the available fields and methods.
- Local development normally uses simulated resource state under .wrangler; the course’s experimental remote option selects the hosted resource instead.
- KV, D1, R2, Queues, AI, and other products provide distinct capabilities while following a similar resource-binding workflow.

### What the course does

1. Create the demonstration KV namespace in the Cloudflare dashboard.
2. Add its namespace ID and an application-facing binding name to the Worker’s Wrangler configuration.
3. Run the script that invokes Wrangler type generation so the binding becomes visible to TypeScript.
4. Add one route that stores a supplied ID under a fixed key and another route that reads that key.
5. Run locally, write two different IDs in sequence, and confirm the read route reflects the persisted state in the demonstration environment.

### Watch for

- The fixed-key save/read routes are explicitly a teaching example, not the final Smart Links cache design.
- Experimental remote configuration and product/free-tier details are recording-specific.
- Added advice: know whether a development command targets local state or a hosted resource before using it with meaningful data.

### Recall and practice

**Question:** What is the difference between a KV resource ID and its binding name?

**Answer:** The ID selects the namespace in Cloudflare; the binding name is the environment property through which this Worker accesses it.

**Try it:** Reproduce the demonstration with an isolated test KV namespace and explain where the saved ID lives during your chosen development mode.

**Success check:** The read route retrieves the saved value, and you can identify the configuration, generated type, and method call involved.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:00 (course video 06) — Resources and bindings across products
- 00:02:00 (course video 06) — Create and bind a KV namespace
- 00:03:00 (course video 06) — Local state versus remote bindings
- 00:04:00 (course video 06) — Generate types and implement KV routes

Companion documentation: [Cloudflare resource bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/). Check installed versions before copying recorded commands.

## 07. React + Tanstack + TRPC on Workers

Building in a Mono Repo · Original video 00:14:21 · Evidence: transcript

The user application pairs React with TanStack Router, TanStack Query, and a tRPC backend deployed as a Worker. The lesson traces a dashboard table from its route loader to a query hook and then to the server procedure, showing how these tools divide navigation, server-data state, and typed communication. At this stage, those procedures still return placeholder data.

Original video (course video 07) · [Focused study page](../lessons/0008-cloudflare-07-react-tanstack-trpc-on-workers.html)

### Understand the idea

- TanStack Router maps route files to pages and runs loaders that can prefetch the data a page needs.
- TanStack Query manages server-data loading, errors, caching, and configured refetch behavior instead of duplicating those concerns in each component.
- A query key identifies cached data, while a query function performs the actual asynchronous data retrieval.
- tRPC supplies a typed client interface and query options, including query keys and functions, for the server procedures.
- The source directory contains the browser application; the worker directory contains the thin backend in the same deployable project.

### What the course does

1. Run the user application and follow navigation from the landing page to the dashboard.
2. Find the dashboard route loader and the top-countries table component that consumes its data.
3. Compare manual useEffect-based fetching and state variables with a TanStack Query hook.
4. Follow the typed tRPC procedure from the client into the Worker router using editor navigation.
5. Identify the dummy result that later database queries will replace, and distinguish ordinary query hooks from the demonstrated suspense-based hook.

### Watch for

- Authentication is scheduled for later; the route naming and UI grouping do not prove access is protected yet.
- Added advice: verify retry, caching, and refetch defaults for the installed TanStack version; the instructor’s spoken default retry count is tentative.
- Added advice: compile-time types complement runtime validation; typed client code alone cannot make incoming network data trustworthy.

### Recall and practice

**Question:** Which tool handles navigation, which handles server-data state, and which supplies the typed backend interface?

**Answer:** TanStack Router handles navigation, TanStack Query handles server-data state, and tRPC connects typed client calls to server procedures.

**Try it:** Trace one dashboard value through its component, query options, route loader, and server procedure without changing the code.

**Success check:** You can point to where the current dummy data originates and where a real database query would be inserted.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:02:01 (course video 07) — File routes and dashboard loaders
- 00:06:00 (course video 07) — Problems in manual fetch state management
- 00:09:00 (course video 07) — tRPC adds typed communication
- 00:11:01 (course video 07) — Trace the table to the Worker backend

Companion documentation: [tRPC introduction](https://trpc.io/docs/). Check installed versions before copying recorded commands.

## 08. Setting up D1 Database & Drizzle ORM

Building in a Mono Repo · Original video 00:17:49 · Evidence: transcript

The course creates a D1 database and uses Drizzle to represent its tables as TypeScript schemas. Schema and query code lives in Data Ops so both applications can share it, and database initialization is centralized to reduce provider-specific setup scattered through the apps. This lesson uses a database-first workflow: create tables remotely, then introspect them into code.

Original video (course video 08) · [Focused study page](../lessons/0009-cloudflare-08-setting-up-d1-database-drizzle-orm.html)

### Understand the idea

- D1 supplies the SQL database used for this project; Drizzle adds typed schema and query-building support.
- The links table stores configuration, link clicks stores event data for analytics, and destination evaluations records background health-check results.
- Indexes support the anticipated lookup/filter patterns, with the instructor noting the read-versus-write tradeoff.
- Drizzle Kit’s pull command reads the database structure and produces schema files; it does not create meaningful application rows.
- Drizzle tooling uses an account ID, database ID, and API token, which is a different connection path from a Worker’s D1 binding.

### What the course does

1. Create a Smart Links database with a stage suffix to prepare for later environment separation.
2. Put the account and database identifiers and a D1-capable API token in the Data Ops tooling environment file.
3. Configure Drizzle output paths and run an initial pull to confirm connectivity.
4. Use the database query interface and supplied course SQL to create links, link clicks, indexes, and destination evaluations.
5. Pull again and inspect the generated TypeScript schemas in the shared package.

### Watch for

- The D1 size limit, allowances, Workers for Platforms claims, and database suitability recommendations describe the recording’s products and opinions.
- The instructor says to exclude the environment file from Git and save the token securely.
- Added advice: centralizing an ORM connection reduces migration work, but changing database providers can still require schema, SQL, and operational changes.

### Recall and practice

**Question:** Why is the first schema pull empty, and why does the second contain tables?

**Answer:** The first runs before any tables exist; the second introspects the tables just created in D1.

**Try it:** Create the course’s three tables in a dedicated study database and explain what event or action writes to each one.

**Success check:** The generated schemas match the database tables, and you can distinguish link configuration, click history, and evaluation results.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:03:01 (course video 08) — Why introduce Drizzle and shared queries
- 00:08:01 (course video 08) — Drizzle configuration and introspection
- 00:12:00 (course video 08) — Create the D1 tooling token
- 00:14:02 (course video 08) — Create application tables and indexes
- 00:17:00 (course video 08) — Pull the resulting schemas

Companion documentation: [Drizzle with Cloudflare D1](https://orm.drizzle.team/docs/sqlite/connect-cloudflare-d1). Check installed versions before copying recorded commands.

## 09. Creating SQL Queries in our Mono Repo

Building in a Mono Repo · Original video 00:22:23 · Evidence: transcript

This lesson implements the first real end-to-end write: submitting a link form creates a database row and returns its generated short-link ID. A validated tRPC mutation calls a reusable Data Ops query, which gets the initialized Drizzle database and inserts the row. The debugging sequence makes the difference between package exports, runtime initialization, resource bindings, and local versus remote data concrete.

Original video (course video 09) · [Focused study page](../lessons/0010-cloudflare-09-creating-sql-queries-in-our-mono-repo.html)

### Understand the idea

- Zod describes the accepted create-link input, and its inferred TypeScript type is reused by the query function.
- The account ID is supplied through server context rather than being part of the browser’s create-link input; authentication remains a placeholder here.
- The query generates a short ID, stores destination configuration as serialized JSON, and returns the new ID.
- Package exports and workspace dependencies make the built query available to the user application.
- The course initializes a shared database accessor when the Worker receives a request, before tRPC invokes database operations.

### What the course does

1. Trace the Create button to its client mutation and matching server procedure.
2. Implement the create-link query in Data Ops using the generated links schema, then rebuild the package.
3. Replace the dummy mutation result with the query call and supply the account information from context.
4. Diagnose the missing-initialization error, add the D1 binding, regenerate types, and initialize the database at the Worker entrypoint.
5. Distinguish an empty local SQLite database from the hosted D1 database; enable the course’s remote development integration.
6. Verify the new row in D1 and return its actual generated ID so navigation opens the correct link page.

### Watch for

- The Vite and Wrangler experimental remote flags are version-specific.
- Added advice: the module-level database accessor is the course pattern; review concurrent requests and environment boundaries before extending it to multiple database tenants.
- Forgetting to return the created ID leaves the frontend navigating to the dummy identifier.

### Recall and practice

**Question:** Why can a D1 query still fail after its binding has been declared?

**Answer:** The shared Drizzle accessor may not be initialized, or development may be using a separate local database whose tables were never created.

**Try it:** Create a link and follow its ID from form submission to the database row and the page URL.

**Success check:** The stored destination and name match the form, and the final page URL contains the ID actually returned by the database operation.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:03:00 (course video 09) — Shared input schema and query creation
- 00:06:00 (course video 09) — Database initialization and accessor
- 00:14:01 (course video 09) — Diagnose database not initialized
- 00:18:01 (course video 09) — Remote database integration
- 00:21:00 (course video 09) — Return the actual generated link ID

Companion documentation: [Drizzle with Cloudflare D1](https://orm.drizzle.team/docs/sqlite/connect-cloudflare-d1). Check installed versions before copying recorded commands.

## 10. Create all TRPC CRUD operations

Building in a Mono Repo · Original video 00:24:54 · Evidence: transcript

The remaining demonstrated link-management operations replace placeholder results with real shared queries. The application lists an account’s links, loads one link, updates its name, and changes its default and country-specific destinations. TanStack Query invalidation refreshes the affected server data after a successful mutation, keeping the UI synchronized with the database.

Original video (course video 10) · [Focused study page](../lessons/0011-cloudflare-10-create-all-trpc-crud-operations.html)

### Understand the idea

- A reusable list query accepts an account and an optional time cursor, selects link fields, orders results, and limits the returned page.
- The link-detail query parses stored destinations into the shared link shape used by the client.
- Destination configuration always includes a default URL and can additionally map country codes to alternate URLs.
- Shared Zod schemas validate sensitive updates before the query writes them, while TypeScript supports callers during development.
- tRPC mutations are network requests despite their function-call-like interface; successful mutation callbacks can invalidate query-cache entries.

### What the course does

1. Add the get-links query to Data Ops, rebuild it, and replace the list procedure’s dummy array.
2. Connect the link-detail page to a real get-link query and return a not-found error for missing links.
3. Wire the name editor to the update-name mutation and confirm changes persist after reloading.
4. Wire default and country destination edits to the shared update-destinations query.
5. Add two country overrides, verify their persistence, then inspect how disabling geo-routing writes only the default destination.
6. Observe the follow-up read request triggered by query invalidation after an update succeeds.

### Watch for

- Despite its title, the lesson demonstrates create/read/update and deletion of country overrides; it does not implement a separate delete-link operation.
- The narration calls the cursor created-before while describing a greater-than condition. Added advice: verify comparator, ordering, and timestamp boundaries against the actual code before relying on pagination.
- Added advice: account ownership checks are essential when completing authentication; knowing a link ID must not itself authorize edits.

### Recall and practice

**Question:** Why does a new get-link request appear immediately after the destination update succeeds?

**Answer:** The success handler invalidates that query’s cached data, causing the displayed server data to be fetched again.

**Try it:** Change a name, add a country override, and disable geo-routing while inspecting network requests and database state.

**Success check:** The name persists, the override appears then is removed, the default remains, and you can explain the refresh request after each affected mutation.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:02:00 (course video 10) — List query and time-based pagination
- 00:09:01 (course video 10) — Link-detail and update queries
- 00:11:00 (course video 10) — Destination shape and runtime parsing
- 00:21:01 (course video 10) — Persist and remove country overrides
- 00:23:01 (course video 10) — Invalidate queries after mutation success

Companion documentation: [tRPC introduction](https://trpc.io/docs/). Check installed versions before copying recorded commands.

## 11. Deploy your changes!

Building in a Mono Repo · Original video 00:04:28 · Evidence: transcript

This short checkpoint deploys the now data-backed user application and verifies its link operations against D1. The instructor demonstrates reading a build error rather than postponing deployment until the entire course is finished. Continuous deployment checkpoints help isolate problems to a small set of recent changes.

Original video (course video 11) · [Focused study page](../lessons/0012-cloudflare-11-deploy-your-changes.html)

### Understand the idea

- The existing deploy script still handles building assets and publishing the application; new functionality does not require a new deployment mechanism.
- The project’s build checks can block deployment before upload when source issues such as unused variables are found.
- The deployment output lists the D1 binding, connecting configuration review to the running app.
- The hosted application can behave differently from the experimental remote local-development setup, so both need practical verification.

### What the course does

1. Stop the local user application and run its deployment script.
2. Read the reported unused state-setter error in the unfinished socket component, and correct the intentionally unused destructuring slot.
3. Run deployment again and inspect the uploaded assets and listed D1 binding.
4. Open the deployed URL and test the links list, link editor, and a country destination update.
5. Refresh the page to confirm those changes persist in the database-backed application before moving to the data service.

### Watch for

- The unused-variable example depends on the exact starter and local code state; it may not appear in your checkout.
- Added advice: fix the reported source problem rather than removing useful build checks simply to make deployment pass.
- Authentication is still implemented later in the course, so this checkpoint should not be mistaken for a completed production application.

### Recall and practice

**Question:** What does deploying at this checkpoint validate beyond seeing the local page work?

**Answer:** It validates the application build, deployment configuration, hosted D1 binding, and actual link operations in the Cloudflare environment.

**Try it:** Deploy your current course application and perform one persistent edit through the hosted interface.

**Success check:** The deployment succeeds, the expected D1 binding appears, and the edit survives a full reload.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:00 (course video 11) — Why deploy incrementally
- 00:01:00 (course video 11) — Run deploy and inspect its failure
- 00:02:00 (course video 11) — Fix the unused placeholder variable
- 00:03:00 (course video 11) — Verify the deployed database-backed UI

Companion documentation: [Drizzle with Cloudflare D1](https://orm.drizzle.team/docs/sqlite/connect-cloudflare-d1). Check installed versions before copying recorded commands.

## 12. Setting up the Data Service

Smart Routing Service · Original video 00:07:20 · Evidence: transcript

The course now moves from the dashboard’s lightweight backend to the separate data service. Its first responsibility is receiving a short-link ID and redirecting the visitor to a default or country-specific destination. The lesson starts with a minimal WorkerEntrypoint class, explaining event handlers and deploying a hello-world response before adding routing complexity.

Original video (course video 12) · [Focused study page](../lessons/0013-cloudflare-12-setting-up-the-data-service.html)

### Understand the idea

- The public redirect service is separate from the dashboard service, even though both use the same link configuration.
- Country-based routing requires a link’s destination map and location information attached to the incoming request.
- A WorkerEntrypoint class and a module exporting a fetch handler can both handle HTTP requests in the patterns shown.
- Fetch, queue, and scheduled handlers correspond to different trigger types that invoke application code.
- The class constructor provides a common initialization location before the class handles its event, which later lessons use for shared dependencies.

### What the course does

1. Inspect the dashboard’s default destination and optional country overrides to define the required routing behavior.
2. Open the data-service application and locate its source entrypoint and minimal fetch response.
3. Compare the class-based entrypoint with the user application’s module-style Worker entrypoint.
4. Inspect the available fetch, queue, and scheduled methods and discuss their trigger roles.
5. Run the data service locally on the demonstrated port 8787, then deploy it using its simple Wrangler script.
6. Open the deployed data-service URL and confirm hello world before introducing Hono.

### Watch for

- Generated short URLs currently include an undefined hostname because their frontend Vite variable has not been configured yet; this is expected at this stage.
- The class-based style is the instructor’s organizational preference rather than a requirement for all Workers.
- Added advice: request-location information is an input to routing, not proof of someone’s identity or exact physical position.

### Recall and practice

**Question:** Why establish a working hello-world deployment before adding the redirect logic?

**Answer:** It confirms the data service’s entrypoint and deployment setup independently, making later failures easier to attribute to the new functionality.

**Try it:** Run and deploy the data service, then name one responsibility that belongs here and one that stays in the user application.

**Success check:** Both local and hosted URLs respond, and you assign public redirecting to the data service and link editing to the user application.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:00 (course video 12) — Default and country-specific redirect requirements
- 00:02:00 (course video 12) — Move into the separate data service
- 00:04:01 (course video 12) — Worker triggers
- 00:05:01 (course video 12) — Constructor and local startup
- 00:06:01 (course video 12) — Deploy the minimal service

Companion documentation: [Cloudflare resource bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/). Check installed versions before copying recorded commands.

## 13. Setting up Hono

Smart Routing Service · Original video 00:08:26 · Evidence: transcript

Hono adds a small routing layer inside the data-service Worker so the application can define paths without manually inspecting each URL. The Worker remains the entrypoint and forwards the request, environment, and execution context to the Hono app. A dynamic ID route initially returns JSON, establishing the seam where real short-link logic will be added.

Original video (course video 13) · [Focused study page](../lessons/0014-cloudflare-13-setting-up-hono.html)

### Understand the idea

- The Worker entrypoint receives the event; Hono decides which application route handles its HTTP path.
- A dynamic /:id route captures the short-link identifier while leaving the homepage unimplemented.
- Hono’s context exposes request helpers, response helpers, and environment bindings to each route.
- Typing the app’s bindings with the generated environment type provides completion and checking when accessing Cloudflare resources.
- The execution context includes waitUntil, introduced as a way to continue limited asynchronous work after returning a response; later lessons apply it.

### What the course does

1. Keep the existing WorkerEntrypoint class and create a separate Hono app module in the data service.
2. Ensure Hono is installed, export the app, and specify the application’s environment bindings type.
3. Define a GET route with a dynamic ID parameter and return a small JSON response.
4. Replace the Worker’s hello-world response with forwarding to the Hono app’s fetch handler.
5. Forward all three pieces of context: the incoming request, Worker environment, and execution context.
6. Run locally and compare the unmatched homepage with an arbitrary ID path that matches the new route.

### Watch for

- Omitting the environment when forwarding prevents the route from accessing its bindings, even if Wrangler configuration is correct.
- A not-found response at the homepage is expected because only the ID route has been defined.
- Added advice: waitUntil is not a substitute for the durable queue and workflow mechanisms that the course introduces for more substantial background processing.

### Recall and practice

**Question:** Why must the Worker forward more than the request to Hono?

**Answer:** The routes also need the runtime environment for bindings and the execution context for Cloudflare-specific request-lifecycle capabilities.

**Try it:** Add the Hono ID route and inspect responses for / and /sample-link in your local data service.

**Success check:** The ID route returns JSON, the homepage is unmatched, and you can trace the same request through the Worker and Hono layers.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:00 (course video 13) — Why manual path conditions become cumbersome
- 00:03:00 (course video 13) — Create the typed Hono app and dynamic route
- 00:05:01 (course video 13) — Forward the Worker request and context
- 00:07:00 (course video 13) — Test matched and unmatched paths

Companion documentation: [Hono on Cloudflare Workers](https://hono.dev/docs/getting-started/cloudflare-workers). Check installed versions before copying recorded commands.

## 14. Cloudflare Worker location headers

Smart Routing Service · Original video 00:05:50 · Evidence: transcript

The lesson inspects the request information available for geo-routing. Hono wraps the original Request with convenience methods but preserves it under its raw property; the Cloudflare-specific cf object is accessed there. The implementation extracts country, latitude, and longitude so subsequent lessons can use country for routing and coordinates for click analytics.

Original video (course video 14) · [Focused study page](../lessons/0015-cloudflare-14-cloudflare-worker-location-headers.html)

### Understand the idea

- Hono’s request wrapper and the underlying incoming Request are related objects with different APIs.
- The raw request exposes Cloudflare-specific metadata under cf, separate from ordinary HTTP request headers.
- Country selects the potential country override; latitude and longitude are intended to enrich click events and later map displays.
- The demonstrated metadata also contains information such as city, timezone, network, region, and an edge location code.
- The cf object and individual values need defensive handling because their availability and inferred types are not assumed by the application.

### What the course does

1. Inspect Hono’s request context and locate its raw incoming Request.
2. Compare inspecting ordinary headers with reading the Cloudflare-specific cf metadata object.
3. Review the demonstrated JSON metadata to identify country and coordinate fields.
4. Extract country, latitude, and longitude using the optional cf object.
5. Return those values from the development route temporarily and inspect the response before adding database-based routing.

### Watch for

- The instructor informally calls cf metadata headers; keep the metadata property and ordinary HTTP headers conceptually separate.
- Added advice: the narration mixes edge-server location with request geolocation. Consult current Cloudflare field definitions before interpreting coordinates or colo; the lesson does not establish that they mean the same location.
- Added advice: a country or EU-related flag is not by itself a complete access-control or compliance mechanism.

### Recall and practice

**Question:** Where does this implementation obtain the country used for routing?

**Answer:** From the Cloudflare-specific cf metadata on Hono’s raw incoming Request, rather than from a country supplied in the short-link URL.

**Try it:** Inspect a development request and identify its country and coordinate values, including any missing values.

**Success check:** You can distinguish Hono’s request wrapper, the raw Request, ordinary headers, and Cloudflare metadata without treating them as interchangeable.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:02 (course video 14) — Access the original raw request
- 00:02:00 (course video 14) — Inspect the Cloudflare cf object
- 00:03:00 (course video 14) — Country and other location metadata
- 00:04:00 (course video 14) — Extract optional routing and coordinate values

Companion documentation: [Cloudflare Request API](https://developers.cloudflare.com/workers/runtime-apis/request/). Check installed versions before copying recorded commands.

## 15. Connecting our database

Smart Routing Service · Original video 00:08:08 · Evidence: transcript

The data service reuses the existing Data Ops get-link query to read the same configuration created through the dashboard. The lesson connects that reusable code to this Worker’s own D1 binding and initializes the shared database accessor in the WorkerEntrypoint constructor. It also demonstrates how a missing await can hide the real error by returning before the asynchronous query finishes.

Original video (course video 15) · [Focused study page](../lessons/0016-cloudflare-15-connecting-our-database.html)

### Understand the idea

- The dynamic Hono path parameter supplies the link ID used by the shared database query.
- Importing query code does not itself connect this separate Worker to D1; each deployed service needs its own binding configuration.
- Remote development selects the hosted database used by the dashboard rather than a separate local SQLite file.
- The shared database initialization must run before query functions access it.
- The constructor forwards its execution context and environment through super, then performs the demonstrated dependency initialization before handlers execute.

### What the course does

1. Read the ID through Hono’s request-param helper and call the imported get-link query.
2. Copy the intended D1 configuration into the data service and regenerate Cloudflare environment types.
3. Run locally against a link ID that already exists in the user application’s database.
4. Add the missing await so failures occur in the request’s actual control flow.
5. Diagnose database-not-initialized and initialize the Data Ops accessor from the constructor’s D1 environment binding.
6. Repeat the request and confirm that the returned JSON contains the stored link configuration.

### Watch for

- A Promise that has not been awaited is not the query result; returning too early can obscure the initialization failure.
- The experimental remote option is recording-specific and must match the tooling version used.
- Added advice: the shared module-level accessor is an architectural choice in the sample. Review initialization and concurrency carefully before using it with varying per-request database bindings.

### Recall and practice

**Question:** Why did importing a working query from the dashboard package not make it immediately work in the data service?

**Answer:** The separate Worker still needed its D1 resource binding and the shared database accessor initialized for its own execution path.

**Try it:** Use a link created through the dashboard to retrieve its configuration through the data-service ID route.

**Success check:** The returned name and destinations match D1, and you can explain the binding and initialization steps that made the shared query work.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:01 (course video 15) — Extract the ID and reuse get-link
- 00:03:01 (course video 15) — Configure the data service D1 binding
- 00:05:02 (course video 15) — Missing await reveals the initialization error
- 00:06:00 (course video 15) — Initialize the database in the constructor

Companion documentation: [Cloudflare resource bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/). Check installed versions before copying recorded commands.

## 16. Geo based smart routing

Smart Routing Service · Original video 00:11:50 · Evidence: transcript

This lesson turns the data-service lookup into an actual redirect. It validates the relevant Cloudflare metadata, chooses a country-specific destination when configured, and otherwise chooses the default URL. A small routing helper holds the selection logic while the Hono route coordinates lookup, error handling, and the redirect response.

Original video (course video 16) · [Focused study page](../lessons/0017-cloudflare-16-geo-based-smart-routing.html)

### Understand the idea

- A missing link record is different from a known link without a country override: the former returns not found, the latter still has a default destination.
- A shared Zod schema narrows country and coordinate metadata and converts coordinate strings to numbers.
- safeParse exposes success or failure as a result that the route handles explicitly.
- Destination selection accepts an optional country: missing country or missing country mapping falls back to the link’s default URL.
- Separating destination selection from the HTTP route keeps the decision easy to inspect and reuse.

### What the course does

1. Add not-found handling for a link ID with no configuration.
2. Define or inspect the shared Cloudflare-info schema and parse the cf metadata with it.
3. Add a routing helper that checks the country mapping and falls back to the default destination.
4. Replace the temporary JSON result with Hono’s redirect response.
5. Set a known default destination in the dashboard and test the redirect locally, then deploy the data service.
6. Use the instructor’s VPN experiment: configure Malaysia with an alternate destination and verify the deployed redirect changes when that country is detected.

### Watch for

- Although missing country falls back to default, the demonstrated route returns an error when parsing the metadata object fails. The instructor explicitly identifies default fallback as a possible improvement.
- VPN regions may not match the country Cloudflare observes, so the instructor warns that geographic testing can be misleading.
- Added advice: distinguish an unknown link from unavailable location information when deciding user-facing fallback behavior.

### Recall and practice

**Question:** What destination is chosen for a valid link when its country is absent or has no override?

**Answer:** The routing helper chooses the default destination; a failure to parse the entire metadata object follows a separate error path in the demonstrated route.

**Try it:** Predict and test three cases: a configured country, an unconfigured country, and a missing link ID.

**Success check:** The configured country uses its override, the unconfigured country uses the default, and the missing link receives a not-found response.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:00 (course video 16) — Missing-link handling
- 00:02:00 (course video 16) — Shared metadata schema and coordinate conversion
- 00:04:00 (course video 16) — Metadata parsing failure behavior
- 00:06:00 (course video 16) — Country override and default selection
- 00:10:01 (course video 16) — Malaysia VPN redirect experiment

Companion documentation: [Hono on Cloudflare Workers](https://hono.dev/docs/getting-started/cloudflare-workers). Check installed versions before copying recorded commands.

## 17. Speed things up with KV

Smart Routing Service · Original video 00:12:06 · Evidence: transcript

The redirect service adds a KV cache to reduce repeated database round trips, especially for visitors far from the demonstrated D1 location. It first looks up the link ID in KV, validates cached JSON, and falls back to D1 on a miss or invalid cached value. A successful database lookup populates KV with a 24-hour expiration before returning the same link shape used by the routing helper.

Original video (course video 17) · [Focused study page](../lessons/0018-cloudflare-17-speed-things-up-with-kv.html)

### Understand the idea

- This is cache-aside behavior: read the cache first, query the database when necessary, then populate the cache.
- D1 remains the source of stored link configuration while KV supplies a copy suited to repeated reads.
- The cached string is JSON-parsed and checked with the shared Zod link schema, so downstream routing receives a known shape.
- KV is eventually consistent: writes may not be immediately visible near all readers, which matters for frequent updates or immediate-read requirements.
- An expiration TTL deletes a cache entry after the configured period; it is not the same as actively synchronizing every edit from D1.

### What the course does

1. Create a stage KV namespace, bind it as cache in the data service, and regenerate environment types.
2. Build a helper that reads one ID from KV and returns either a validated link object or no usable cached result.
3. Build a coordinator that falls back to the shared D1 query and returns not found if neither source yields a link.
4. Serialize successful database results into KV with the demonstrated 24-hour expiration.
5. Replace the direct database call in the route with this cache-aware helper.
6. Deploy, compare first and repeated redirects, and optionally add cache-hit logs to inspect through the Worker dashboard.

### Watch for

- The instructor explains eventual consistency but does not implement write-side invalidation in this lesson. Added advice: edits to a cached destination can remain stale; a 24-hour TTL alone does not provide immediate update visibility.
- A faster second browser request is an illustration, not a controlled latency benchmark.
- Remote-binding syntax and free-tier claims reflect the recording’s tooling.

### Recall and practice

**Question:** After editing a link in D1, why might the redirect still use its previous destination?

**Answer:** The routing service can continue using a cached copy until it is invalidated, replaced, or expires; KV visibility is also eventually consistent.

**Try it:** Diagram the cache hit, cache miss with a database record, and missing-link paths, then inspect logging for one repeated link request.

**Success check:** You can explain which paths call D1, when KV is populated, and why freshness needs an explicit policy.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:00 (course video 17) — Database distance and eventual consistency
- 00:03:02 (course video 17) — Cache-first lookup order
- 00:05:00 (course video 17) — Validate cached JSON and coordinate fallback
- 00:08:01 (course video 17) — Set the 24-hour expiration
- 00:11:01 (course video 17) — Repeated-request observation and cache logging

Companion documentation: [How Workers KV works](https://developers.cloudflare.com/kv/concepts/how-kv-works/). Check installed versions before copying recorded commands.

## 18. Extending our data services

Smart Routing Service · Original video 00:03:08 · Evidence: transcript

The completed redirect becomes the event source for the rest of the product. Instead of expanding the request handler with analytics, realtime updates, and AI processing, the course proposes recording a click event on a queue and letting later processing react to it. This transition explains why the next sections introduce Queues, Durable Objects, and Workflows.

Original video (course video 18) · [Focused study page](../lessons/0019-cloudflare-18-extending-our-data-services.html)

### Understand the idea

- A link click is both a user-facing redirect and a useful event containing link, destination, and location context.
- Redirect latency and downstream processing have different requirements, so their work can be separated.
- The proposed queue message carries the link ID, country, coordinates, and the destination actually selected.
- Consumers can use the same click event to store analytics, update realtime clients, or start destination-health evaluation.
- The lesson contrasts periodically querying accumulated database rows with a more event-driven processing model.

### What the course does

1. Review the dashboard features that still need actual data: click analytics, the live map, and destination-health results.
2. Identify the successful redirect as the moment from which click-event information is available.
3. Outline publishing a compact event as background work associated with the request.
4. Assign analytics persistence to downstream processing rather than the public redirect route.
5. Connect realtime map updates conceptually to Durable Objects and destination-health orchestration to Workflows.
6. Use this event flow as the design motivation for the upcoming queue implementation lessons.

### Watch for

- This is an architectural preview; it does not yet implement publishing, consumption, realtime connections, or AI processing.
- Added advice: queue-based processing still needs delivery, retry, duplicate-handling, and failure policies; the preview should not be read as an immediate-delivery guarantee.
- Added advice: keep the event’s selected destination so downstream analysis can describe what happened at click time rather than a later edited configuration.

### Recall and practice

**Question:** Why make a click event feed several backend features instead of performing all of them in the redirect route?

**Answer:** The route can stay focused on getting the visitor to the destination while downstream processing independently persists, broadcasts, and evaluates the event.

**Try it:** Draw a click event flowing from the redirect route into a queue and then to analytics storage, a realtime update, and a health workflow.

**Success check:** You can name the event fields each downstream feature needs and distinguish implemented redirect behavior from the upcoming processing design.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:00 (course video 18) — Remaining dashboard and health-check features
- 00:01:00 (course video 18) — Separate redirecting from click processing
- 00:02:01 (course video 18) — Queue events feeding analytics, Durable Objects, and Workflows

Companion documentation: [Hono on Cloudflare Workers](https://hono.dev/docs/getting-started/cloudflare-workers). Check installed versions before copying recorded commands.

## 19. What are queues?

Working with Queues · Original video 00:05:36 · Evidence: transcript

The course introduces a queue as a handoff between the user-facing request and background processing. A producer records an event, and a consumer processes it separately, allowing the redirect service to stay responsive while the application builds analytics and other features. Queues also give failed work a defined retry and recovery path instead of tying every operation to the lifetime of an HTTP connection.

Original video (course video 19) · [Focused study page](../lessons/0020-cloudflare-19-what-are-queues.html)

### Understand the idea

- A producer describes what happened; a consumer contains the work that should happen afterward. The two roles can evolve independently.
- Background work lets the interface acknowledge that processing is underway and check for results later.
- Event-driven design uses one event, such as a link click, to start several downstream activities.
- At-least-once delivery means a successfully accepted message can be delivered again; it is not a promise that the business operation runs exactly once.
- A dead-letter queue collects messages that have exhausted normal retries, so failed work can be investigated separately.

### What the course does

1. Compare a long API request with a quick request that hands work to a queue.
2. Follow the producer → queue → consumer path in the conceptual example.
3. Consider transient failures and how retries preserve another opportunity to process the event.
4. Add a dead-letter path for work that still fails after its retry budget.
5. Apply the pattern to link clicks: later consumers will support analytics, AI page checks, and live updates.

### Watch for

- Course caveat: the introductory “no data loss” wording is broader than the later discussion of message expiry and failed sends.
- Added advice: design repeated delivery to be safe, and decide what happens when retries or retention are exhausted.

### Recall and practice

**Question:** Why use a queue for link-click analytics when redirecting a visitor?

**Answer:** The redirect can complete quickly while a separate consumer processes the click; failures in downstream processing have a retry path.

**Try it:** Draw the path for one successful click and one click whose analytics handler temporarily fails. Label where the visitor receives a response.

**Success check:** Your diagram distinguishes the HTTP response from background completion and shows the failed message returning for another attempt.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:21 (course video 19) — Queue as a background-processing middle layer
- 00:02:41 (course video 19) — Event-driven systems
- 00:03:00 (course video 19) — At-least-once delivery motivation
- 00:04:21 (course video 19) — Dead-letter queue
- 00:05:04 (course video 19) — Link-click pipeline and downstream features

Companion documentation: [Queues delivery guarantees](https://developers.cloudflare.com/queues/reference/delivery-guarantees/). Check installed versions before copying recorded commands.

## 20. Workers Paid Tier + Pricing breakdown

Working with Queues · Original video 00:04:45 · Evidence: transcript

This lesson explains the account upgrade used in the recording and separates the base subscription from usage-based charges. Its durable teaching point is to estimate costs from both request volume and CPU work, then improve those estimates with real measurements. The specific prices, limits, and requirement to upgrade for Queues describe the recording and require a current documentation check before following them.

Original video (course video 20) · [Focused study page](../lessons/0021-cloudflare-20-workers-paid-tier-pricing-breakdown.html)

### Understand the idea

- The instructor describes an account-level charge rather than a separate charge for each teammate.
- A base allowance and incremental usage charges are different parts of the bill.
- Request count alone is insufficient: CPU time also contributes to the cost model discussed.
- Early deployment and beta usage can reveal which Workers receive traffic and consume more CPU.
- A known high-volume workload deserves a calculation before launch; an unknown workload needs assumptions that can later be replaced with measurements.

### What the course does

1. Inspect account membership and permissions as part of understanding the account-level model.
2. Review the recorded standard-plan allowance and its extra-request pricing example.
3. Compare CPU time with the total elapsed duration of a request.
4. Read the provider’s worked pricing examples to understand how the components combine.
5. Use observed traffic and average CPU consumption to refine a forecast.
6. Return to Queues after completing the account setup required by the recorded environment.

### Watch for

- Recording-era claims include $5 per account per month, 500 Workers, 10 million included requests, and $0.30 per additional million; do not treat these as a current quote.
- The instructor’s experience of many clients remaining near the base charge is anecdotal, not a budget guarantee.
- Added advice: include the separate products used later—AI, browser rendering, Queues, and storage—when estimating the full application bill.

### Recall and practice

**Question:** Why can two applications with the same request count have different compute costs?

**Answer:** The amount of CPU work performed per request can differ, and the application may also use different separately billed products.

**Try it:** Create a small cost-estimate worksheet in your notes with placeholders for requests, average CPU time, and downstream product usage. Mark every recorded number as historical.

**Success check:** You can explain the inputs you need to measure and distinguish a course example from a current price.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:01 (course video 20) — Recorded paid-tier requirement
- 00:00:41 (course video 20) — Account and team billing model
- 00:01:53 (course video 20) — Usage-based pricing
- 00:02:36 (course video 20) — Learning costs from observed usage
- 00:04:06 (course video 20) — Forecasting a known high-volume workload

Companion documentation: [Queues pricing](https://developers.cloudflare.com/queues/platform/pricing/). Check installed versions before copying recorded commands.

## 21. Creating & Using Queues

Working with Queues · Original video 00:08:50 · Evidence: transcript

The instructor creates a staging queue, configures the data-service Worker as its consumer, and implements a queue handler that logs message bodies. Sending test messages in the dashboard makes batching visible: one handler invocation can contain several messages. Pausing and resuming delivery then demonstrates that accepting messages and delivering them to a consumer are separate operations.

Original video (course video 21) · [Focused study page](../lessons/0022-cloudflare-21-creating-using-queues.html)

### Understand the idea

- The queue name in configuration connects the deployed Worker to the intended queue resource.
- A consumer configuration needs an actual queue handler exported through the Worker entrypoint.
- The handler receives a batch; each message has a body and delivery-related metadata and methods.
- Explicit acknowledgement marks a message as processed; retry requests another attempt.
- Pausing delivery lets messages accumulate, which is useful when investigating a consumer problem.

### What the course does

1. Create a queue with a staging name in the dashboard.
2. Add a consumer entry to the data service’s Wrangler configuration using that queue name.
3. Implement an asynchronous queue handler and iterate through the batch to log each body.
4. Deploy the data service and open its real-time logs.
5. Send “Hello World” messages and observe one invocation containing multiple messages.
6. Pause delivery, send more messages, inspect the pending queue, then resume and observe it drain.

### Watch for

- The recording states one consumer Worker per queue; a single Worker may consume multiple queues. These are different relationships.
- Course caveat: live logs may appear with a delay and sometimes behave inconsistently.
- Added advice: pausing is not indefinite archival storage; account for the queue’s configured retention.

### Recall and practice

**Question:** What is the difference between a message and a batch in this lesson?

**Answer:** A message contains one event body; a batch groups several messages delivered together to one queue-handler invocation.

**Try it:** Using a test queue, send several distinguishable messages while delivery is paused, then resume it and compare invocation logs with individual message logs.

**Success check:** You can identify both the batch-level trigger and each message processed within it, and explain why the message count need not equal the invocation count.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:54 (course video 21) — Implementing the Worker queue handler
- 00:02:50 (course video 21) — Message metadata and acknowledgement
- 00:05:14 (course video 21) — One consumer Worker per queue
- 00:06:37 (course video 21) — Observing a batch containing several messages
- 00:07:04 (course video 21) — Pause and resume delivery

Companion documentation: [Queues delivery guarantees](https://developers.cloudflare.com/queues/reference/delivery-guarantees/). Check installed versions before copying recorded commands.

## 22. Creating a Producer

Working with Queues · Original video 00:09:16 · Evidence: transcript

The redirect route becomes a queue producer: it packages each click with the destination and location context, then sends that event for later processing. The course uses the execution context’s waitUntil facility so waiting for the send does not delay the visitor’s redirect. This completes the connection between the fast HTTP path and the queue consumer built in the previous lesson.

Original video (course video 22) · [Focused study page](../lessons/0023-cloudflare-22-creating-a-producer.html)

### Understand the idea

- The producer binding supplies send for one event and sendBatch for submitting multiple events efficiently.
- A shared DataOps message type defines the event contract used by the producer.
- The click payload includes link ID, account ID, destination, country, latitude, longitude, and a timestamp.
- waitUntil keeps track of a background promise after the response, but it is not the same thing as durable queue acceptance.
- The same Worker project can contain an HTTP producer and a queue consumer while the invocations remain separate.

### What the course does

1. Add a producer binding in Wrangler, pointing to the same queue already used by the consumer.
2. Regenerate Cloudflare environment types so the queue binding becomes available to application code.
3. Construct a typed link-click message in the Hono redirect route using lookup results and request location data.
4. Pass the send promise to the request execution context’s waitUntil method.
5. Deploy, visit a valid smart-link ID, and confirm the browser still redirects to its destination.
6. Inspect consumer logs for the structured click payload rather than the earlier plain-text test message.

### Watch for

- The instructor explicitly notes that background sending is not completely fail-safe and questions its suitability for data that cannot be lost.
- Added advice: successful redirection alone does not prove the queue accepted the event; inspect background-send failures.
- Recorded subrequest limits and waitUntil time limits are platform details to verify before using them as design constraints.

### Recall and practice

**Question:** At what point has a click become a durable queued event?

**Answer:** After the queue has accepted the send; returning the redirect or starting a waitUntil promise is not itself that acknowledgement.

**Try it:** Trace each payload field back to its source: link lookup, selected destination, request location, or generated timestamp.

**Success check:** You can explain why the consumer has enough context to process a click without repeating the redirect request.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:38 (course video 22) — Capturing clicks without delaying redirects
- 00:02:03 (course video 22) — send and sendBatch
- 00:03:11 (course video 22) — Shared link-click message type
- 00:05:38 (course video 22) — Moving queue send into waitUntil
- 00:08:20 (course video 22) — Checking the received JSON event

Companion documentation: [Queues delivery guarantees](https://developers.cloudflare.com/queues/reference/delivery-guarantees/). Check installed versions before copying recorded commands.

## 23. Building Type Safe Queue Handlers

Working with Queues · Original video 00:11:00 · Evidence: transcript

The queue consumer now validates incoming messages and routes recognised event types to dedicated handlers. A Zod discriminated union turns the event’s type label into a dependable choice of payload shape, and the first handler persists link-click data to D1 through the shared DataOps package. This creates an end-to-end path from a visitor’s click to a queryable database record.

Original video (course video 23) · [Focused study page](../lessons/0024-cloudflare-23-building-type-safe-queue-handlers.html)

### Understand the idea

- TypeScript helps producers during development; runtime schema validation checks the actual message arriving at the consumer boundary.
- A discriminated union associates each literal event type with its corresponding data schema.
- safeParse returns a success or failure result, allowing the code to choose how to handle invalid data.
- The queue entrypoint performs validation and routing, while a dedicated handler owns the business operation.
- Shared database queries keep persistence code reusable across the web application and data service.

### What the course does

1. Inspect the common queue envelope and the specific link-click schema in DataOps.
2. Validate each message body with the queue schema’s safeParse operation.
3. Log validation errors for unrecognised or malformed messages in the course implementation.
4. Create the add-link-click query that inserts the event fields into the link-clicks table, then rebuild DataOps.
5. Add a link-click handler that calls the shared query, and await it from the consumer after checking the event type.
6. Deploy, visit a known smart link, and query D1 to confirm that a real click record appears.

### Watch for

- The course’s invalid-message branch only logs an error; it does not demonstrate a quarantine or recovery strategy.
- Added advice: decide explicitly whether invalid events should be rejected, archived, alerted on, or retried.
- Added advice: account for duplicate delivery before extending the handler with operations that must not repeat.

### Recall and practice

**Question:** Why validate messages if the producer already uses a TypeScript type?

**Answer:** Types are development-time checks; the actual queue payload can still be malformed or come from a different producer or version.

**Try it:** Describe a second event type that could use the same queue. Specify its type label, required fields, and dedicated handler responsibility.

**Success check:** Your new type has a distinct schema and handler, and adding it does not require mixing its business logic into the existing link-click handler.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:23 (course video 23) — Runtime validation with Zod
- 00:01:42 (course video 23) — Discriminated union by event type
- 00:02:48 (course video 23) — safeParse on message.body
- 00:05:14 (course video 23) — Shared add-link-click database query
- 00:09:10 (course video 23) — Confirming a persisted click in D1

Companion documentation: [Queues delivery guarantees](https://developers.cloudflare.com/queues/reference/delivery-guarantees/). Check installed versions before copying recorded commands.

## 24. Advanced Configuration: Delay, Retries & Dead Letter

Working with Queues · Original video 00:13:04 · Evidence: transcript

This lesson extends queue operation with delayed delivery, retry configuration, and a dead-letter recovery path. The instructor deliberately breaks the consumer, observes failed events arriving in a paused dead-letter queue, restores the handler, and resumes delivery to recover the records into D1. It also distinguishes code organisation from deployment topology: producer and consumer may share one Worker or live in separate services.

Original video (course video 24) · [Focused study page](../lessons/0025-cloudflare-24-advanced-configuration-delay-retries-dead-letter.html)

### Understand the idea

- Producer delay postpones eligibility for delivery; the Twilio example waits before checking whether an SMS arrived.
- Retry count and retry delay express how much additional work the consumer should attempt after failure.
- A dead-letter queue provides a separate destination after the normal retry budget is exhausted.
- A Worker consuming two queues can inspect the batch’s queue name if different handling is required.
- Local queue state belongs to the development setup; separate local services do not automatically share the same queue state.

### What the course does

1. Review send options, including content representation and a delivery delay.
2. Inspect consumer configuration for maximum retries and the delay between attempts.
3. Create a staging dead-letter queue, pause delivery, and attach it to the main consumer configuration.
4. Temporarily throw a test error, deploy, trigger clicks, and inspect messages collected in the dead-letter queue.
5. Configure the data-service Worker to consume the dead-letter queue as well, with the demonstration retry setting.
6. Restore and deploy the working handler before resuming delivery; confirm the D1 row count increases as queued failures are recovered.

### Watch for

- Course caveat: repeated expensive AI or image operations can multiply costs, so retry policy must fit the operation.
- Added advice: a paused dead-letter queue still needs monitoring and a retention-aware recovery process.
- Added advice: replaying a partly completed handler can repeat earlier writes; recovery requires more than simply turning delivery back on.

### Recall and practice

**Question:** Why fix the consumer before resuming a paused dead-letter queue?

**Answer:** Resuming sends those failed events back to processing; if the original defect remains, recovery will fail again and may exhaust the new retry policy.

**Try it:** Write a five-step recovery checklist for a temporary database outage, from detecting dead-letter messages to confirming their records were saved.

**Success check:** The checklist includes investigation, a verified fix, controlled replay, outcome checks, and duplicate-handling considerations.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:23 (course video 24) — Local queue testing and separate-service caveats
- 00:04:31 (course video 24) — Producer delivery delay
- 00:06:23 (course video 24) — Retry settings
- 00:08:05 (course video 24) — Creating and pausing the dead-letter queue
- 00:11:58 (course video 24) — Recovering failed events after restoring the handler

Companion documentation: [Queues delivery guarantees](https://developers.cloudflare.com/queues/reference/delivery-guarantees/). Check installed versions before copying recorded commands.

## 25. What are Cloudflare Workflows?

Building AI Workflows · Original video 00:07:27 · Evidence: transcript

The next feature evaluates destination pages: render their content, ask AI whether the product remains available, and preserve evidence. Cloudflare Workflows supplies named steps with persisted progress and configurable retry behaviour, making the process easier to manage than a long HTTP request. The lesson explains the intended architecture before implementation and contrasts it with much heavier data-processing orchestration.

Original video (course video 25) · [Focused study page](../lessons/0026-cloudflare-25-what-are-cloudflare-workflows.html)

### Understand the idea

- The evaluator checks content health, such as a sold-out product, rather than merely whether a URL responds.
- Rendering matters because JavaScript can populate important information after the initial HTML arrives.
- A workflow coordinates a sequence of operations and retains state between steps.
- Per-step retry policies allow cheap retrieval and expensive inference to have different failure budgets.
- Sleep and waiting for events support delayed or externally coordinated work without treating the whole operation as one short request.

### What the course does

1. Identify the destination URL as the input and page health as the application-facing result.
2. Plan a rendering step that collects visible text and HTML.
3. Plan an AI step that interprets the collected page content.
4. Plan evidence storage so the collected input can later support debugging and evaluation.
5. Inspect the WorkflowEntrypoint class, its run method, and named steps in the conceptual example.
6. Review concurrency and duration constraints before treating the example as a high-volume production design.

### Watch for

- The recording’s concurrency counts, sleep limits, product availability, and competitor comparisons are historical claims rather than current guidance.
- The instructor presents more advanced model-evaluation systems as extensions; they are not built in this section.
- Added advice: a completed workflow only proves the processing finished, not that the AI judgement is correct.

### Recall and practice

**Question:** Why give browser rendering and AI inference different retry policies?

**Answer:** They have different failure modes and costs; retrying an expensive inference repeatedly may waste money even when retrying a transient rendering failure is useful.

**Try it:** Sketch three or four named steps for checking a product page, and write the output needed by the following step.

**Success check:** Each step has a clear responsibility and a reason for the data it passes onward; the AI input comes from rendered evidence.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:30 (course video 25) — Render, evaluate, and preserve destination evidence
- 00:02:08 (course video 25) — Persisted multi-step applications
- 00:03:09 (course video 25) — Workflow entrypoint and run method
- 00:04:06 (course video 25) — Per-step retries and backoff
- 00:05:09 (course video 25) — Recorded workflow concurrency constraints

Companion documentation: [Workflows Workers API](https://developers.cloudflare.com/workflows/build/workers-api/). Check installed versions before copying recorded commands.

## 26. Build & Deploy a Workflow

Building AI Workflows · Original video 00:09:15 · Evidence: transcript

The instructor creates and deploys the first destination-evaluation workflow with one named step returning dummy data. The class, entrypoint export, Wrangler binding, resource name, and class name are connected so Cloudflare can discover and execute it. A manual dashboard trigger then confirms the wiring before real browser and AI work are introduced.

Original video (course video 26) · [Focused study page](../lessons/0027-cloudflare-26-build-deploy-a-workflow.html)

### Understand the idea

- A WorkflowEntrypoint subclass owns the run method that coordinates the operation.
- The event carries parameters supplied when a workflow instance is created.
- The step object runs named operations whose results can be used by later steps.
- An individual workflow execution is an instance with an ID; it is distinct from the deployed workflow definition.
- Returning data from a step makes it available to subsequent code and visible as step output in the demonstrated dashboard.

### What the course does

1. Create a workflows folder and the destination-evaluation class in the data service.
2. Implement run with an event argument and a step argument, initially using an unknown payload type.
3. Add a descriptive step that logs an action and returns a small dummy object.
4. Export the workflow class from the Worker entrypoint.
5. Configure the workflow binding, deployed name, and exported class name in Wrangler, then deploy.
6. Trigger an instance in the dashboard, inspect its completed step and output, and note how instance parameters and IDs are supplied.

### Watch for

- The first successful deployment proves configuration only; it does not yet render a page or evaluate a destination.
- The dashboard is a teaching and debugging aid; application-triggered execution is introduced later.
- Added advice: lesson 34 changes how large step outputs are handled, so do not generalise this small dummy-output example to screenshots or full documents.

### Recall and practice

**Question:** What is the difference between a workflow definition and an instance?

**Answer:** The definition is the deployed class and its steps; an instance is one execution of that definition with its own identity, inputs, and progress.

**Try it:** Change the dummy step to return a small diagnostic object describing the input URL, then trigger two distinct test instances.

**Success check:** You can locate each instance separately and connect its visible output to the parameters supplied for that execution.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:20 (course video 26) — WorkflowEntrypoint subclass
- 00:02:37 (course video 26) — Defining a named step
- 00:05:05 (course video 26) — Export and Wrangler configuration
- 00:06:58 (course video 26) — Manual instance trigger
- 00:07:58 (course video 26) — Inspecting returned step output

Companion documentation: [Workflows Workers API](https://developers.cloudflare.com/workflows/build/workers-api/). Check installed versions before copying recorded commands.

## 27. Rendering Webpages with Cloudflare Browser Render

Building AI Workflows · Original video 00:14:50 · Evidence: transcript

The dummy collection step becomes a real Browser Rendering operation using Cloudflare’s Puppeteer integration. The workflow opens the destination, waits for network activity to settle, and extracts visible body text, HTML, and the response status for later analysis. The instructor then moves this logic into a helper and explicitly closes the browser to release the session.

Original video (course video 27) · [Focused study page](../lessons/0028-cloudflare-27-rendering-webpages-with-cloudflare-browser-render.html)

### Understand the idea

- A browser binding connects Worker code to the rendering service; Puppeteer exposes page navigation and inspection operations.
- The workflow’s typed input includes link ID, destination URL, and account ID.
- Visible text is the concise content intended for AI; HTML preserves additional evidence for inspection.
- HTTP status can reveal failures such as a missing page before an AI interpretation is useful.
- Named workflow steps coordinate the process while helper functions contain detailed rendering logic.

### What the course does

1. Install the Cloudflare-compatible Puppeteer package and add the virtual-browser binding.
2. Regenerate environment types and pass the binding to Puppeteer’s launch operation.
3. Define the workflow payload type and use its destination URL when opening a page.
4. Wait for network idle, extract body text and HTML, and capture the response status.
5. Deploy and trigger a test against the instructor’s site, then compare returned content with the page.
6. Inspect logs on the associated Worker, extract rendering into a helper, and close the browser after collection.

### Watch for

- The recording cites ten concurrent browser sessions per account; use current limits before planning capacity.
- Course caveat: network-idle waiting is only one available way to decide when a page is ready.
- Added advice: ensure browser cleanup also happens on errors, and treat missing or blocked page content as a separate issue from AI quality.

### Recall and practice

**Question:** Why preserve both body text and HTML rather than feed the whole HTML directly to AI?

**Answer:** Text gives the model the content it needs with less markup, while HTML provides richer evidence for later debugging.

**Try it:** Compare a simple static page with a page that fills content after loading. Record what the rendered text contains and what readiness condition you used.

**Success check:** You can explain whether the collected input actually includes the content needed for a page-health decision.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:26 (course video 27) — Cloudflare Puppeteer integration
- 00:05:18 (course video 27) — Browser binding setup
- 00:07:51 (course video 27) — Typed workflow input
- 00:08:31 (course video 27) — Wait for network idle and extract content
- 00:13:55 (course video 27) — Closing the browser session

Companion documentation: [Workflows Workers API](https://developers.cloudflare.com/workflows/build/workers-api/). Check installed versions before copying recorded commands.

## 28. Workers AI & AI SDK

Building AI Workflows · Original video 00:06:46 · Evidence: transcript

This lesson introduces Workers AI as the model service and the AI SDK as a common interface for calling providers. The key design choice is structured output: instead of interpreting a free-form paragraph, application code receives named fields whose shape is described by a schema. The forthcoming destination checker will use that approach to turn scraped content into a status the rest of the application can consume.

Original video (course video 28) · [Focused study page](../lessons/0029-cloudflare-28-workers-ai-ai-sdk.html)

### Understand the idea

- Workers AI is presented as Cloudflare-hosted models reached through an environment binding.
- Different models offer different capabilities, context sizes, costs, and latency; the feature must fit the chosen model.
- A response schema describes object fields, their types, required properties, and possible enumerated values.
- System instructions, user content, and the requested output shape each contribute to the model call.
- The AI SDK provides a common calling pattern across providers, which makes comparison and substitution easier.

### What the course does

1. Review the model catalogue and focus on support for the structured response needed by the application.
2. Compare a free-form answer with the example object containing a country’s name, capital, and languages.
3. Inspect how the example schema specifies strings, an array, and required fields.
4. Consider comparing two model outputs within separate workflow steps as a possible extension.
5. Review the AI SDK’s provider abstraction before implementing the checker.
6. Inspect the minimal Workers AI binding and run pattern that the provider integration will build upon.

### Watch for

- The model list and supported capabilities shown in the recording are time-sensitive.
- Added advice: JSON syntax, schema conformity, and factual correctness are separate properties; do not treat structured output as proof of a correct classification.
- The instructor’s claims about which organisation pioneered an output format are background commentary, not required knowledge for this application.

### Recall and practice

**Question:** What problem does a response schema solve, and what does it leave unsolved?

**Answer:** It makes the output shape usable by application code; it does not ensure the model understood the page or chose the correct status.

**Try it:** Define a small destination-check result with a status and a reason. Explain which values should be allowed when the evidence is incomplete.

**Success check:** Your design represents uncertainty explicitly and does not require fragile string searching through a paragraph.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:38 (course video 28) — Workers AI as the course model service
- 00:01:22 (course video 28) — Why structured responses help application code
- 00:02:28 (course video 28) — JSON response schema example
- 00:04:23 (course video 28) — AI SDK provider abstraction
- 00:05:44 (course video 28) — Workers AI binding and model invocation

Companion documentation: [Workflows Workers API](https://developers.cloudflare.com/workflows/build/workers-api/). Check installed versions before copying recorded commands.

## 29. Create an AI Workflow Step

Building AI Workflows · Original video 00:21:52 · Evidence: transcript

The course implements an AI destination-checking helper with a Zod output schema, the Workers AI provider, and the AI SDK’s recorded generateObject API. The helper receives extracted page text and returns an availability status plus a concise reason, and a new workflow step calls it with a restrained retry policy. Testing reveals an important limitation: a more detailed prompt still produces an unknown result when the collected page content is incomplete or unexpected.

Original video (course video 29) · [Focused study page](../lessons/0030-cloudflare-29-create-an-ai-workflow-step.html)

### Understand the idea

- Using body text reduces irrelevant markup in the prompt and avoids spending tokens on styling and layout.
- The schema permits available, unavailable, and unknown outcomes so incomplete evidence need not force a confident answer.
- Field descriptions and the main prompt explain what evidence should justify each classification.
- A reason makes a classification inspectable, but the instructor’s belief that reasons improve accuracy is an empirical preference, not a guarantee.
- Model/provider selection is separate from the workflow that coordinates rendering, inference, and persistence.

### What the course does

1. Create the AI helper and define its status-and-reason output schema.
2. Add the AI binding, regenerate types, and configure the Workers AI provider.
3. Call the structured-output API with a supported model, instructions, the extracted body text, and the schema.
4. Add an AI workflow step with a deliberately limited retry policy.
5. Test an unavailable AliExpress listing: the first run reports unavailable, though its explanation is not the expected evidence.
6. Strengthen the prompt and rerun; the result becomes unknown, motivating storage of the actual scraped input for diagnosis.

### Watch for

- Recorded SDK versions and APIs need checking before copying them into a new project.
- HTTP 200 did not prove the renderer received the expected product information.
- Added advice: keep a small labelled evaluation set and compare changes against it; one plausible output does not establish accuracy.

### Recall and practice

**Question:** Why is the unknown result useful evidence rather than simply a failed prompt?

**Answer:** It can indicate that the model was given missing or different page content; the pipeline must inspect its input before blaming the model.

**Try it:** Write three short page-text samples: clearly available, clearly unavailable, and missing product information. State the expected status and supporting phrase for each.

**Success check:** Your expected results distinguish uncertainty from unavailability and can be used as a small repeatable evaluation set.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:02:59 (course video 29) — Body text rather than full HTML as model input
- 00:04:27 (course video 29) — Zod output schema
- 00:10:24 (course video 29) — Workers AI binding
- 00:13:44 (course video 29) — AI step and retry budget
- 00:20:21 (course video 29) — Unknown result despite successful HTTP status

Companion documentation: [Workflows Workers API](https://developers.cloudflare.com/workflows/build/workers-api/). Check installed versions before copying recorded commands.

## 30. Saving AI output in our DB

Building AI Workflows · Original video 00:09:20 · Evidence: sampled-video-frames

This lesson connects the AI classification to persistent application data. Sampled video frames show a database-save workflow step, shared evaluation queries, and a fix that initializes the database inside the workflow entrypoint. The final D1 Studio frame shows an evaluation saved with UNKNOWN_STATUS, preserving uncertainty instead of silently treating it as an available product. This summary covers visible implementation; the narration was unavailable.

Original video (course video 30) · [Focused study page](../lessons/0031-cloudflare-30-saving-ai-output-in-our-db.html)

### Understand the idea

- An evaluation combines the model's status and reason with the link ID, account ID, and destination URL so the application can attribute and display the result.
- A shared query helper inserts the evaluation and returns its generated ID; the workflow can use that ID in later work.
- Initializing the database in the ordinary Worker entrypoint does not establish initialization in the workflow entrypoint. The video adds initDatabase(this.env.DB) inside run().
- The visible read queries scope evaluations by account, support a list of unavailable products, and order general evaluation history by creation time.

### What the course does

1. Add a named database-save step after the AI classification and pass the workflow payload identifiers plus the model's status and reason.
2. Implement addEvaluation in the shared data-ops package, insert into destinationEvaluations, and return the ID.
3. Add account-filtered read helpers for unavailable evaluations and evaluation history, then rebuild the shared package for the consuming service.
4. Inspect the deployed workflow's failed save step and the database initialization code; add initialization at the start of the workflow run method.
5. Check the completed step history and query destination_evaluations in D1 Studio to verify the saved record.

### Watch for

- Evidence limitation: sampled 28 frames at 20-second intervals from 00:00:05 to 00:09:05; no subtitle file or narration transcript. Intermediate edits and the precise explanation of the failure may be missing.
- Visible code caveat: the createdBefore filter uses gt while sorting descending. Added advice: test that pagination actually returns older rows; do not copy that comparison blindly.
- Later course correction: lesson 34 moves ID generation and R2 artifact storage earlier and passes the existing ID into the final database save. Use that patched ordering when completing the workflow.

### Recall and practice

**Question:** Why might the workflow fail to save when the ordinary Worker already initializes the database?

**Answer:** The workflow has its own entrypoint and execution path. Its database helper must be initialized with the binding available to that path; initialization in another entrypoint cannot be assumed.

**Try it:** Sketch the fields in a saved evaluation and mark which come from the workflow payload and which come from the AI result. Add where database initialization occurs.

**Success check:** Link/account/destination identifiers come from the payload, status/reason come from the model result, and the workflow initializes its database access before querying.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:45 (course video 30) — Shared insert helper generates an ID and writes evaluation fields
- 00:02:25 (course video 30) — Account-filtered history query and the createdBefore comparison
- 00:03:05 (course video 30) — Workflow database-save step passes model output and identifiers
- 00:07:05 (course video 30) — Database initialization added inside the workflow run method
- 00:09:05 (course video 30) — D1 Studio displays the persisted UNKNOWN_STATUS evaluation

Companion documentation: [Drizzle with Cloudflare D1](https://orm.drizzle.team/docs/sqlite/connect-cloudflare-d1). Check installed versions before copying recorded commands.

## 31. Saving Web Page Data in R2

Building AI Workflows · Original video 00:15:36 · Evidence: transcript

The workflow backs up rendered page evidence to R2 and links it to the evaluation saved in D1. The lesson explains why relational records belong in a database while larger documents and images fit object storage, then uses a shared evaluation ID to connect those two kinds of data. Inspecting the stored AliExpress text confirms that the renderer did not collect the expected product information, giving a concrete reason to improve observability.

Original video (course video 31) · [Focused study page](../lessons/0032-cloudflare-31-saving-web-page-data-in-r2.html)

### Understand the idea

- D1 stores queryable application records; R2 holds large evidence objects addressed by keys.
- A database record can act as an index into object storage by retaining an identifier used in object keys.
- The course organises evaluation evidence by account, evaluation identity, and content kind.
- Object-storage access centres on known keys and prefixes rather than relational joins.
- An R2 binding exposes storage operations directly inside the Worker environment.

### What the course does

1. Enable R2 in the course account and create a staging evaluation bucket with standard storage.
2. Add its binding and bucket name to Wrangler, then regenerate environment types.
3. Use the evaluation ID returned by the preceding database-save step to build the evidence paths.
4. Write the rendered HTML and extracted body text to separate R2 objects.
5. Deploy and trigger a workflow; check both the new D1 record and the corresponding R2 objects.
6. Download the text and HTML to investigate why the model returned unknown for the AliExpress page.

### Watch for

- This initial ordering is superseded by lesson 34: evidence storage moves into the first step, and the evaluation ID is created earlier.
- Recorded pricing, free allowances, storage-class beta status, and jurisdiction commentary should not be treated as current operational or legal guidance.
- The instructor suspects bot detection, but the observed fact here is missing expected content; the cause is not proved in this lesson.

### Recall and practice

**Question:** How can a database query lead to the exact evidence objects for an evaluation?

**Answer:** The evaluation’s ID and account identify the object-key pattern, so query results provide the information needed to retrieve those objects.

**Try it:** Design key names for one evaluation’s HTML, text, and screenshot, then write the minimum database fields needed to locate them.

**Success check:** All three objects are unambiguously associated with the same evaluation and can be located without scanning their contents.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:33 (course video 31) — Database records versus large objects
- 00:05:15 (course video 31) — Creating the staging R2 bucket
- 00:08:12 (course video 31) — Backup step and account-based paths
- 00:09:25 (course video 31) — Connecting D1 and R2 with an evaluation ID
- 00:12:24 (course video 31) — Inspecting stored text to debug missing content

Companion documentation: [Workflows Workers API](https://developers.cloudflare.com/workflows/build/workers-api/). Check installed versions before copying recorded commands.

## 32. Add Evaluation Data in UI

Building AI Workflows · Original video 00:05:45 · Evidence: transcript

The application’s evaluation screens are connected to real backend records instead of placeholder arrays. Two shared DataOps queries supply problematic destinations and recent evaluations through tRPC, filtered by the account context. The debugging sequence shows that an empty UI can be correct when the selected account differs from the account attached to the stored evaluation.

Original video (course video 32) · [Focused study page](../lessons/0033-cloudflare-32-add-evaluation-data-in-ui.html)

### Understand the idea

- The backend produces evaluation data; tRPC exposes that data to the application’s existing screens.
- The problematic-destinations view selects evaluations classified as unavailable.
- The general evaluations query returns recent records and supports a created-before filter for pagination.
- Account filtering determines which records a user-facing query can see.
- Remote D1 development depends on the developer’s Cloudflare authentication as well as application query logic.

### What the course does

1. Start the user application and locate the problematic-links and evaluations sections.
2. Import the shared evaluation queries into the tRPC evaluation routes.
3. Replace problematic-destination dummy data with the query for unavailable results.
4. Replace the general evaluation placeholder array with the real recent-evaluations query and await the results.
5. Resolve the expired Cloudflare login affecting the remote D1 connection.
6. Compare the test account ID in D1 with the application’s temporary user/account value, align the test setup, and inspect the displayed evaluations.

### Watch for

- The lesson temporarily uses a hard-coded user ID as an account proxy; this is development scaffolding that the later auth section replaces.
- Unknown evaluations do not automatically appear in the unavailable-only problematic-links list.
- Added advice: when a list is empty, distinguish missing records, filtering, authentication, and loading failures before changing the query.

### Recall and practice

**Question:** Why could D1 contain evaluations while the application displays none?

**Answer:** The route filters by account, and the demonstration’s stored test account initially did not match the application’s temporary account value.

**Try it:** Write a troubleshooting sequence for an empty evaluations screen. Include a query with the exact account ID being used by the route.

**Success check:** You can separate a valid empty result from a broken connection and identify whether the stored account matches the requested account.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:29 (course video 32) — Shared evaluation queries
- 00:01:13 (course video 32) — Replacing tRPC dummy responses
- 00:02:35 (course video 32) — Cloudflare login caused remote-query trouble
- 00:03:33 (course video 32) — Account-ID mismatch explains an empty list
- 00:04:29 (course video 32) — Viewing real evaluation reasons and URLs

Companion documentation: [Workflows Workers API](https://developers.cloudflare.com/workflows/build/workers-api/). Check installed versions before copying recorded commands.

## 33. Taking Screenshot in Browser Rendering

Building AI Workflows · Original video 00:03:29 · Evidence: transcript

This update adds screenshots to the rendering evidence so the instructor can inspect what the remote browser actually saw. The browser helper returns an encoded screenshot, and the R2 backup step converts it into image bytes and stores it beside the HTML and text. The resulting image shows an error page, explaining why the AI could not infer the product’s availability from the collected content.

Original video (course video 33) · [Focused study page](../lessons/0034-cloudflare-33-taking-screenshot-in-browser-rendering.html)

### Understand the idea

- A screenshot records visible rendering state that may be difficult to diagnose from text or HTML alone.
- A base64 data URL is a textual representation of an image, which the lesson converts back into bytes for storage.
- The screenshot is evidence of the browser’s observed page, not necessarily the page a person sees in another session.
- The existing evaluation ID and storage organisation can connect the screenshot to its other evidence.
- Observability features can be added within the rendering helper without redesigning the whole application.

### What the course does

1. Take a screenshot after the page-loading work in the browser helper.
2. Return the screenshot as an encoded data URL alongside the collected page data.
3. Extend the R2 backup step with a screenshot-specific path.
4. Extract the encoded image portion, convert it into bytes, and write it to R2.
5. Run a workflow and open the saved screenshot to inspect the unexpected AliExpress page.

### Watch for

- The instructor speculates about bot detection and a reload challenge; the demonstrated evidence is an error page, not a confirmed explanation of the site’s internal behaviour.
- This screenshot-as-step-output design can exceed the persistence-size limit and is corrected immediately in lesson 34.
- Added advice: diagnose using the exact saved evidence for an execution rather than assuming a later manual browser visit shows identical content.

### Recall and practice

**Question:** What did the screenshot explain that the unknown AI status could not explain by itself?

**Answer:** It showed that the rendering session saw an error page, so the expected product evidence was absent before the model was called.

**Try it:** For an unexpected classification, write the evidence you would inspect in order: rendered screenshot, collected text, response status, prompt, and model output.

**Success check:** Your investigation can identify whether the problem began during collection or interpretation instead of treating every unknown status as a model defect.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:18 (course video 33) — Using screenshots to diagnose missing content
- 00:00:47 (course video 33) — Capturing and encoding the screenshot
- 00:01:18 (course video 33) — Adding a screenshot R2 path
- 00:02:00 (course video 33) — Converting encoded image data for upload
- 00:02:25 (course video 33) — Inspecting the saved error-page screenshot

Companion documentation: [Workflows Workers API](https://developers.cloudflare.com/workflows/build/workers-api/). Check installed versions before copying recorded commands.

## 34. Update: Fixing issues with 1MiB Output limit

Building AI Workflows · Original video 00:02:52 · Evidence: transcript

This corrective lesson fixes workflow failures caused by oversized persisted step output, especially after screenshots were added. The first step now creates the evaluation ID, renders the page, and writes bulky evidence directly to R2, returning only the text needed for AI and that ID. The AI step remains, and the final database save accepts the already-created ID rather than generating its own.

Original video (course video 34) · [Focused study page](../lessons/0035-cloudflare-34-update-fixing-issues-with-1mib-output-limit.html)

### Understand the idea

- A workflow step’s returned value is persisted state, so its size matters independently of whether the rendering operation succeeded.
- The recording’s failure concerns a 1 MiB allowed output size; screenshots can make the earlier return object exceed it.
- Large artifacts belong in object storage, while small values or references cross workflow step boundaries.
- Creating the evaluation ID before storage lets R2 keys and the later D1 record share the same identity.
- Moving work into a step changes the checkpoint boundary without changing the application’s overall purpose.

### What the course does

1. Identify the first collection step as the source of the oversized saved-state error.
2. Generate an evaluation ID at the start of that step.
3. Render the destination and move the HTML, text, and screenshot R2 writes into the same step.
4. Return the evaluation ID and body text instead of the full HTML and encoded image.
5. Keep the AI step using the returned text.
6. Change the add-evaluation query to accept the caller’s ID, save the database record last, and remove the separate final R2 step.

### Watch for

- This patch supersedes the earlier four-step implementation; later videos may still show the pre-patch ordering.
- The instructor expects text to be small, but explicitly notes that it too could be stored and retrieved from R2 if necessary.
- Added advice: measure serialized byte size rather than assuming a page is small, and consider references for every potentially large result.

### Recall and practice

**Question:** Why does moving the R2 upload into the first step fix the screenshot-related failure?

**Answer:** The large screenshot no longer has to be returned and persisted as that step’s output; it is stored in R2 before the checkpoint.

**Try it:** Redraw the final workflow after this patch and annotate which data is stored in R2 versus returned between steps.

**Success check:** Your diagram shows collection plus backup first, AI second, database save last, with a shared ID and no large image crossing a step boundary.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:04 (course video 34) — Student-reported maximum-output-size error
- 00:00:36 (course video 34) — Step result persisted as workflow state
- 00:01:09 (course video 34) — Moving R2 writes into the collection step
- 00:01:38 (course video 34) — Returning only text and evaluation identity
- 00:02:25 (course video 34) — Database query now accepts the existing ID

Companion documentation: [Workflows limits](https://developers.cloudflare.com/workflows/reference/limits/). Check installed versions before copying recorded commands.

## 35. System design & How we can use Durable Objects

Managing Workflows with Durable Objects · Original video 00:07:58 · Evidence: transcript

The course introduces Durable Objects to decide when expensive destination-evaluation workflows should run. Instead of launching a workflow for every click, a stateful coordinator groups relevant clicks and schedules later work with an alarm. This places the scheduling and future subscription rules between the queue consumer and the evaluation workflow while keeping the redirect path fast.

Original video (course video 35) · [Focused study page](../lessons/0036-cloudflare-35-system-design-how-we-can-use-durable-objects.html)

### Understand the idea

- The web Worker uses tRPC and D1 for application data; the data-service Worker performs redirect handling and queue consumption.
- Workflows already handle evaluation execution, but manual dashboard triggering is not a product integration.
- A Durable Object combines application-defined state with code that can act on that state.
- Alarms schedule future execution, giving the coordinator a way to wake up and launch work later.
- Choosing the identity of each object determines which clicks share scheduling state and which remain independent.

### What the course does

1. Review the full path from the application’s stored links through Hono redirection and queue consumption.
2. Identify browser rendering and AI as the comparatively expensive work that should not run on every click.
3. Consider a rule that groups repeated clicks and performs one evaluation after a delay.
4. Place a Durable Object between the queue handler and workflow creation.
5. Keep the click context in persistent state and use an alarm to schedule the evaluation.
6. Treat account tier and first-click behaviour as possible future business rules rather than completed features.

### Watch for

- The conceptual explanation groups by destination URL; lesson 38’s actual implementation uses link ID plus destination URL.
- The subscription and first-click scheduling examples are design possibilities, not behaviour already implemented here.
- Added advice: an identity choice is a business decision—grouping too broadly can combine work that different links or accounts expect separately.

### Recall and practice

**Question:** What responsibility belongs to the Durable Object here, and what remains in the workflow?

**Answer:** The Durable Object decides when to start an evaluation using stored context and an alarm; the workflow performs the rendering, AI analysis, and persistence.

**Try it:** For 500 clicks to the same link and destination, describe the state and alarm you need to run one evaluation after a chosen delay.

**Success check:** Your design keeps scheduling independent from evaluation execution and explains why the other 499 clicks do not require new workflows.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:14 (course video 35) — Reviewing the application and redirect data flow
- 00:02:30 (course video 35) — Replacing manual workflow triggers
- 00:03:07 (course video 35) — Avoiding an evaluation on every click
- 00:05:22 (course video 35) — State plus alarms as the required capabilities
- 00:06:13 (course video 35) — Independent object instances and scheduling state

Companion documentation: [What are Durable Objects?](https://developers.cloudflare.com/durable-objects/concepts/what-are-durable-objects/). Check installed versions before copying recorded commands.

## 36. Durable Object API

Managing Workflows with Durable Objects · Original video 00:05:12 · Evidence: transcript

This lesson separates a Durable Object’s in-memory fields from its persistent storage and introduces the alarm API. A counter illustrates why a field alone is insufficient: when an inactive instance is restarted, memory must be rebuilt from stored data. The course begins with key-value storage for the scheduler and previews SQLite-backed objects for later live analytics.

Original video (course video 36) · [Focused study page](../lessons/0037-cloudflare-36-durable-object-api.html)

### Understand the idea

- A Durable Object class defines methods that use the object’s state, storage, alarms, and other platform facilities.
- A constructor runs when an instance is created in memory, including when an object is reactivated.
- In-memory fields provide convenient current state but are not durable across shutdown.
- Persistent storage allows a restarted object to recover earlier values.
- An alarm uses a future timestamp and invokes the object’s alarm method to perform scheduled work.

### What the course does

1. Inspect the base class and constructor pattern used to access Durable Object capabilities.
2. Consider an in-memory flag or counter and predict its value after the object is shut down.
3. Compare this with a counter read from storage and written back after incrementing.
4. Recognise the two storage approaches described: simple key-value access and SQLite tables.
5. Inspect alarm scheduling using the current time plus a future interval.
6. Connect the alarm callback to the later goal of starting a workflow with recovered click context.

### Watch for

- A variable declared on the class is not automatically persisted simply because the class is a Durable Object.
- The key-value backend choice reflects this course’s initial implementation; do not infer that all durable state must use that backend.
- Added advice: use milliseconds consistently when constructing alarm timestamps, and explain the interval clearly in code.

### Recall and practice

**Question:** What must happen when a Durable Object wakes up if its last click context only existed in memory?

**Answer:** That context cannot be recovered; it needed to be stored persistently and then loaded during reinitialisation.

**Try it:** Trace a counter through initialise, increment, write, shutdown, restart, and read. Write down both its memory and stored values after each event.

**Success check:** Your trace preserves the increment across restart only when a storage write occurred and startup loaded that saved value.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:35 (course video 36) — Durable Object class and APIs
- 00:00:59 (course video 36) — Memory versus persistent state
- 00:02:15 (course video 36) — Key-value and SQLite storage approaches
- 00:03:06 (course video 36) — Persistent counter example
- 00:04:10 (course video 36) — Alarm scheduling with timestamps

Companion documentation: [What are Durable Objects?](https://developers.cloudflare.com/durable-objects/concepts/what-are-durable-objects/). Check installed versions before copying recorded commands.

## 37. Building the Base Class

Managing Workflows with Durable Objects · Original video 00:13:44 · Evidence: transcript

The instructor builds a small persistent counter inside the future evaluation-scheduler class to make Durable Object identity and state tangible. The constructor restores stored state under blockConcurrencyWhile, methods update and return the count, and a temporary HTTP route calls those methods through a stub. Calling the route with two names demonstrates two independent objects whose values remain separate.

Original video (course video 37) · [Focused study page](../lessons/0038-cloudflare-37-building-the-base-class.html)

### Understand the idea

- The class defines the behaviour shared by many object instances; the instance name selects which independent state is accessed.
- blockConcurrencyWhile protects asynchronous initialisation so other operations do not observe incomplete restored state.
- Each increment updates memory and writes the new count to persistent storage.
- A namespace binding resolves an application-defined name to an ID, then returns a stub for calling the object.
- Wrangler bindings, migrations, entrypoint exports, and generated types connect the class to the deployed application.

### What the course does

1. Create the evaluation-scheduler class, call the base constructor, and initialise a count field.
2. Restore count from storage under blockConcurrencyWhile, defaulting when no value exists.
3. Implement increment with a storage write and get-count with an in-memory read.
4. Configure the Durable Object binding and the initial migration for the course’s key-value-backed class.
5. Export the class, regenerate environment types, and add a temporary route that selects an object by name.
6. Run locally and alternate between two names, observing independent counters and retained values when returning to each name.

### Watch for

- The counter and temporary route are teaching scaffolding; lesson 38 removes them.
- ID-from-name uses an application-defined identity; ID-from-string expects the representation of an existing Durable Object ID.
- Added advice: a class migration is a lifecycle/configuration change, not the same thing as a SQL data migration within an object.

### Recall and practice

**Question:** Why do test-one and test-two keep separate counts even though they use the same class?

**Answer:** The namespace resolves their different names to different object instances, and each instance owns its own state.

**Try it:** Reproduce the two-name counter experiment, then stop and restart the local service without deleting its persistent state and check the counts again.

**Success check:** You can distinguish separate object identities from separate in-memory lifetimes and explain how storage restores each value.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:49 (course video 37) — Restoring persisted count safely
- 00:03:02 (course video 37) — Increment and storage write
- 00:05:20 (course video 37) — Initial Durable Object migration
- 00:08:26 (course video 37) — Application identity with idFromName
- 00:11:19 (course video 37) — Testing independent named counters

Companion documentation: [What are Durable Objects?](https://developers.cloudflare.com/durable-objects/concepts/what-are-durable-objects/). Check installed versions before copying recorded commands.

## 38. Full Link Click Integration with Alarms

Managing Workflows with Durable Objects · Original video 00:23:25 · Evidence: transcript

The persistent counter becomes a working evaluation scheduler integrated with the link-click consumer. Each object stores click context, schedules an alarm only when no alarm is pending, and starts the destination workflow when that alarm fires. The demonstration uses ten seconds to verify the full pipeline and then changes the interval to twenty-four hours so repeated clicks share one scheduled evaluation.

Original video (course video 38) · [Focused study page](../lessons/0039-cloudflare-38-full-link-click-integration-with-alarms.html)

### Understand the idea

- The implemented object identity combines link ID and destination URL, so grouping is more specific than URL alone.
- collect-link-click updates both in-memory and persistent context for the future alarm.
- Checking for an existing alarm prevents each additional click from scheduling another evaluation or pushing back the existing alarm.
- The alarm validates that context exists and creates a workflow with typed link, destination, and account parameters.
- A custom environment interface refines the workflow binding’s payload type without editing the generated configuration file.

### What the course does

1. Remove the teaching counter and its temporary route, then introduce persisted click context.
2. Implement collection to save that context and create an alarm only if none exists.
3. Implement the alarm callback to require click data and create the workflow instance.
4. Refine the binding type so workflow creation checks its input parameters.
5. Extend the queue handler to resolve the object from link ID plus destination URL and pass the click context to it.
6. Deploy and follow one click through redirect, queue, D1, alarm, workflow, and stored screenshot.
7. Change the delay from the ten-second demonstration to twenty-four hours and extract scheduling calls into a helper.

### Watch for

- This is click-triggered scheduling, not an autonomous daily cron: a later click is needed to schedule another alarm after the pending one fires.
- Later playback shows the earlier four-step workflow; retain lesson 34’s size-limit correction in your final implementation.
- Added advice: retries and partial failures can repeat persistence or workflow creation, so scheduling alone does not establish exactly-once processing.

### Recall and practice

**Question:** If a destination receives no further clicks after an evaluation, will this scheduler automatically evaluate it again tomorrow?

**Answer:** No. The implementation schedules from incoming clicks when no alarm is pending; it does not show the alarm scheduling its own next run.

**Try it:** Trace clicks at minute zero, minute five, and after the first alarm has fired. State how many pending alarms and workflow launches exist at each point.

**Success check:** Your trace shows one shared pending alarm before the first firing and a new delayed run only after another click arrives.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:02:02 (course video 38) — Persistent click context replaces the counter
- 00:04:52 (course video 38) — Schedule only when no alarm exists
- 00:08:46 (course video 38) — Refining workflow input types
- 00:13:40 (course video 38) — Identity combines link and destination
- 00:19:49 (course video 38) — Changing the delay to twenty-four hours

Companion documentation: [What are Durable Objects?](https://developers.cloudflare.com/durable-objects/concepts/what-are-durable-objects/). Check installed versions before copying recorded commands.

## 39. Overview of realtime link tracking

Advanced Durable Objects · Original video 00:06:21 · Evidence: transcript

This section designs a live map of link clicks using one Durable Object per account. The redirect service keeps its existing queue path for background processing, while also sending geographic click data directly to the object so map updates avoid queue delay. Each object combines a small SQLite history with connected WebSocket clients, allowing one incoming click to be broadcast to several viewers.

Original video (course video 39) · [Focused study page](../lessons/0040-cloudflare-39-overview-of-realtime-link-tracking.html)

### Understand the idea

- Account identity chooses the Durable Object instance, so each account has its own click state and connected viewers.
- SQLite provides stored click history; WebSockets provide live delivery. These solve different parts of the feature.
- The proposed design includes an initial history message for a newly connected viewer, followed by updates shared with all viewers.
- Offsets represent boundaries in retained click history and help prevent the object’s table growing indefinitely.

### What the course does

1. Locate the new tracker alongside the existing workflow scheduling object in the system diagram.
2. Send click metadata to both the existing queue and the account’s tracker after redirect handling.
3. Store geographic fields and click time in the tracker’s SQLite table.
4. Plan history delivery for new viewers and broadcasts for existing viewers.
5. Track history boundaries and active connections as separate forms of state.

### Watch for

- The instructor explicitly describes this implementation as educational overkill and suggests database polling could be simpler for this feature.
- Initial history replay is proposed here; later demonstrations show new tabs empty until another click, so the outline should not be mistaken for fully implemented behavior.
- Added advice: the informal estimate of hundreds of clients is not a verified capacity guarantee.

### Recall and practice

**Question:** Why retain both the queue and a direct Durable Object call?

**Answer:** The queue supports existing background processing, while the direct call lets the live map receive updates without waiting for queue consumption.

**Try it:** Draw the path of one click and one newly connected viewer, labeling storage and live delivery separately.

**Success check:** Your drawing includes the redirect, queue, account-scoped object, SQLite table, and all connected viewers.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:05 (course video 39) — SQL and WebSocket learning goals
- 00:02:09 (course video 39) — Account-scoped tracker design
- 00:03:43 (course video 39) — Proposed startup history delivery
- 00:05:19 (course video 39) — Offsets and retained state

Companion documentation: [Durable Objects WebSockets](https://developers.cloudflare.com/durable-objects/best-practices/websockets/). Check installed versions before copying recorded commands.

## 40. Build a SQL backed Durable Object

Advanced Durable Objects · Original video 00:16:36 · Evidence: transcript

The tracker becomes a SQLite-backed Durable Object with a small geographic click table and an insertion method. A temporary HTTP endpoint reads the table so storage can be verified before WebSockets are introduced. The redirect’s background helper now enqueues the original analytics event and then writes usable geographic data to the correct account object.

Original video (course video 40) · [Focused study page](../lessons/0041-cloudflare-40-build-a-sql-backed-durable-object.html)

### Understand the idea

- The object obtains its SQL interface from context storage and initializes its table when the instance starts.
- The lesson wraps initialization in blockConcurrencyWhile so subsequent object work does not run ahead of initialization.
- SQL parameter bindings supply values to placeholders; they are different from Cloudflare resource bindings.
- A stub is the handle used to invoke an identified Durable Object, including its fetch handler.
- Each account name maps to its own object and consequently its own SQLite contents.

### What the course does

1. Create the tracker class and a table containing latitude, longitude, country, and time.
2. Implement a parameterized insert method and a temporary query that returns up to 100 rows as JSON.
3. Export the class, add its Durable Object binding and SQLite-class migration, then regenerate environment types.
4. Extract the redirect’s background work into a helper that first sends the queue message.
5. Resolve the account’s object, require usable geographic fields, and call its insert method.
6. Proxy a temporary Hono route to the object, deploy, click a redirect link, and inspect the returned data.

### Watch for

- The instructor corrects a query ordering by a nonexistent ID and changes the migration to new SQLite classes before deploying.
- Geographic metadata may be absent; the helper skips map tracking when required fields are missing.
- The temporary query endpoint is a teaching scaffold, and the instructor’s Drizzle migration difficulties describe their experience at recording time.

### Recall and practice

**Question:** What determines whether two clicks share the same tracker database?

**Answer:** They share it when their links belong to the same account and therefore resolve to the same Durable Object identity.

**Try it:** Explain how you would test account isolation using the temporary endpoint.

**Success check:** You predict that clicks for account A appear under A’s endpoint while another account’s object remains separate.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:02:17 (course video 40) — Create the geographic click table
- 00:04:29 (course video 40) — Parameterized SQL bindings
- 00:10:22 (course video 40) — Queue-first background capture
- 00:13:21 (course video 40) — Correct SQLite class migration

Companion documentation: [What are Durable Objects?](https://developers.cloudflare.com/durable-objects/concepts/what-are-durable-objects/). Check installed versions before copying recorded commands.

## 41. Setting up Websockets

Advanced Durable Objects · Original video 00:08:50 · Evidence: transcript

This lesson replaces the tracker’s temporary JSON response with a WebSocket handshake. It introduces the object’s message, close, and error handlers, then explains how one connection can receive a response or how all connected clients can receive a broadcast. The surrounding Hono route forwards the incoming request to the selected account object.

Original video (course video 41) · [Focused study page](../lessons/0042-cloudflare-41-setting-up-websockets.html)

### Understand the idea

- A WebSocket establishes a persistent, two-way channel between browser and server.
- The object’s event handlers receive connection lifecycle events rather than ordinary independent HTTP requests.
- The context can enumerate accepted sockets, allowing a message to be broadcast to every connected viewer.
- A WebSocketPair supplies client and server ends; the server end is accepted by the object and the client end is returned in the upgrade response.
- The handshake response uses status 101 to switch protocols.

### What the course does

1. Review message, close, and error handlers on the Durable Object class.
2. Explore echoing a received message and broadcasting to all registered sockets.
3. Create a WebSocket pair inside fetch and accept the server endpoint through the object context.
4. Return the client endpoint with the protocol upgrade response.
5. Adapt the Hono socket route to identify the account from a request header and proxy to its object.

### Watch for

- The lesson discusses checking the Upgrade header but does not present this explanation as a complete authentication implementation.
- Account selection is still scaffolding; later lessons place authenticated account selection in the user application’s server.
- Added advice: status 101 means Switching Protocols; it does not guarantee a connection can never close.

### Recall and practice

**Question:** Which end of a WebSocketPair remains associated with the Durable Object?

**Answer:** The server end is accepted by the object; the client end is returned to establish the browser’s connection.

**Try it:** Trace an incoming message through an echo implementation and then through a broadcast implementation.

**Success check:** You distinguish sending to the originating socket from iterating over every accepted socket.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:45 (course video 41) — Durable Object WebSocket handlers
- 00:03:22 (course video 41) — Enumerating connected sockets
- 00:05:12 (course video 41) — Creating a WebSocket pair
- 00:08:07 (course video 41) — Selecting an account through headers

Companion documentation: [Durable Objects WebSockets](https://developers.cloudflare.com/durable-objects/best-practices/websockets/). Check installed versions before copying recorded commands.

## 42. Connecting from the Client (Localhost)

Advanced Durable Objects · Original video 00:09:07 · Evidence: transcript

The frontend hook is connected to the locally running data service to verify the WebSocket handshake before adding richer server behavior. Incoming messages feed the shared geographic click store, whose state drives the map. The lesson then explains the intended architecture: browsers connect through the user application’s Worker, where authentication will happen, and that Worker proxies to the data service.

Original video (course video 42) · [Focused study page](../lessons/0043-cloudflare-42-connecting-from-the-client-localhost.html)

### Understand the idea

- The browser hook creates its connection when the root component mounts and maintains connection status.
- The socket URL uses ws for a local HTTP page and wss for HTTPS.
- Received click messages update a Zustand store, separating transport from map rendering.
- A healthy persistent socket appears pending in the browser network panel; an immediately finished request indicates a failed connection here.
- The user application includes server code as well as browser code, making it a useful place to enforce authentication before proxying.

### What the course does

1. Start both the user application and data service development servers.
2. Inspect the dashboard’s disconnected indicator and its socket hook.
3. Temporarily point the browser at the data service on localhost port 8787.
4. Observe failed requests caused by the missing account header, then temporarily hardcode an account in the backend.
5. Confirm connected status and a pending network connection; close a tab to observe the close event.
6. Restore the browser’s environment-based host setting before proceeding to the service-binding architecture.

### Watch for

- Hardcoded account identity and direct backend URLs are temporary experiments, not the final access-control design.
- The instructor initially attributes duplicate connections to development behavior, then notices two browser windows were open.
- Added advice: the lesson’s service-binding billing statement is historical course commentary, not current pricing guidance.

### Recall and practice

**Question:** Why place the socket proxy in the user application’s Worker?

**Answer:** That server can validate the user and choose authorized account data before forwarding the connection to the data service.

**Try it:** Write a three-observation checklist for a successful local socket connection.

**Success check:** Your checklist includes connected UI state, a persistent pending socket request, and a server close event after closing the tab.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:02:04 (course video 42) — Client hook and socket lifecycle
- 00:04:06 (course video 42) — Missing account header causes failure
- 00:05:08 (course video 42) — Observing socket close events
- 00:06:41 (course video 42) — Planned authentication and proxy boundary

Companion documentation: [Durable Objects WebSockets](https://developers.cloudflare.com/durable-objects/best-practices/websockets/). Check installed versions before copying recorded commands.

## 43. Realtime link clicks with Websockets

Advanced Durable Objects · Original video 00:17:32 · Evidence: transcript

The tracker now uses an alarm to batch clicks before broadcasting them to connected browsers. Each new click is stored, and if no alarm exists one is scheduled roughly two seconds ahead; the alarm queries a bounded recent set, sends it, advances offsets, and removes older rows. The instructor verifies the full redirect-to-map flow using local clicks, a Malaysia VPN location, and a phone.

Original video (course video 43) · [Focused study page](../lessons/0044-cloudflare-43-realtime-link-clicks-with-websockets.html)

### Understand the idea

- Batching trades a small display delay for fewer WebSocket messages when many clicks arrive together.
- Durable storage preserves offset values across object restarts; in-memory fields make them convenient during execution.
- A query helper returns click records plus newest and oldest times, so delivery and cleanup can share boundaries.
- A shared Zod schema defines the shape of click data sent to the client.
- The map is a bounded recent view, while the existing queue path remains responsible for the broader analytics flow.

### What the course does

1. Set an alarm only when one is not already scheduled, using a two-second target.
2. Load offset state during object initialization, defaulting missing values to zero.
3. Create a parameterized query helper for clicks after an offset, with a default result limit of 50.
4. On alarm, query recent clicks and broadcast the click array to all connected sockets.
5. Persist updated boundary times and delete records older than the retained boundary.
6. Temporarily connect the frontend to the deployed secure socket, test geographic clicks, then restore host configuration.

### Watch for

- The instructor presents the batching policy as an illustrative solution, not an optimal implementation for every workload.
- Only a bounded subset of a burst is intended for visualization; do not read the map as an exhaustive event ledger.
- Added advice: timestamp-only offsets deserve collision and ordering tests before relying on them for lossless delivery.

### Recall and practice

**Question:** Why send the batch before performing cleanup?

**Answer:** The lesson prioritizes getting the selected clicks to viewers promptly, then updates offsets and trims retained history.

**Try it:** Walk through a burst of 200 clicks with a 50-record query limit and explain the map’s purpose.

**Success check:** You describe bounded visualization and identify the separate queue path instead of claiming all 200 clicks must appear individually.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:31 (course video 43) — Two-second batching alarm
- 00:05:03 (course video 43) — Persistent offset initialization
- 00:06:35 (course video 43) — Recent-click query helper
- 00:10:39 (course video 43) — Removing older retained clicks
- 00:14:47 (course video 43) — Malaysia redirect and map verification

Companion documentation: [Durable Objects WebSockets](https://developers.cloudflare.com/durable-objects/best-practices/websockets/). Check installed versions before copying recorded commands.

## 44. Service to Service Bindings

Advanced Durable Objects · Original video 00:10:49 · Evidence: transcript

The frontend’s Worker receives a service binding to the data service and becomes the browser’s WebSocket entry point. A small Hono application handles both tRPC routing and the socket proxy, adding an account header before forwarding the request through the binding. Local and deployed checks confirm that one click reaches multiple open dashboard tabs.

Original video (course video 44) · [Focused study page](../lessons/0045-cloudflare-44-service-to-service-bindings.html)

### Understand the idea

- A service binding references another Worker by its configured service name and exposes a fetch interface.
- The browser connects to its own application host; server-side code forwards the request to the data service.
- The future authentication boundary sits before proxying, where trusted account information can be added.
- Hono replaces manual pathname branching as the user application gains additional routes.
- Environment configuration must provide the correct browser base host for local and deployed builds.

### What the course does

1. Remove the backend’s hardcoded account so it again reads the account header.
2. Declare the data-service binding in the user application’s Wrangler configuration and regenerate types.
3. Create Hono routes for existing tRPC handling and the new socket endpoint.
4. Clone the incoming request with an account header and call the bound service’s fetch handler.
5. Point the frontend environment at localhost for the local test, then at the deployed user-application host for deployment.
6. Open two dashboard tabs and confirm that a redirect click broadcasts to both.

### Watch for

- The proxy still sets account identity manually until the authentication section implements a real session-derived value.
- The lesson uses an experimental remote-development setting; treat that configuration as recording-time tooling.
- The instructor explicitly notes that several dashboard statistics are still dummy values at this point.

### Recall and practice

**Question:** What changes in the browser when the service binding is introduced?

**Answer:** It targets the user application’s host instead of hardcoding the data service host; the Worker performs the forwarding.

**Try it:** Describe the request route from a dashboard tab to the account tracker, identifying where authentication will be added.

**Success check:** Your route is browser → user-application Worker → bound data service → account Durable Object, with authentication at the user-application Worker.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:02:02 (course video 44) — Declaring a service binding
- 00:05:03 (course video 44) — Hono socket proxy and account header
- 00:07:05 (course video 44) — Local proxy connection test
- 00:09:38 (course video 44) — Broadcast verification across two tabs

Companion documentation: [Workers remote procedure calls](https://developers.cloudflare.com/workers/runtime-apis/rpc/). Check installed versions before copying recorded commands.

## 45. Create a specific stage deployment

Dev Ops & Environments · Original video 00:10:44 · Evidence: transcript

The existing deployment is reorganized into an explicitly named stage environment, preparing for a separate production copy. Resource bindings move under the stage section of each Wrangler configuration, and deployment and type-generation commands must select that environment. The lesson exposes practical migration issues: Worker names change, queue consumers conflict, Vite selects environments differently, and service bindings must follow the renamed backend.

Original video (course video 45) · [Focused study page](../lessons/0046-cloudflare-45-create-a-specific-stage-deployment.html)

### Understand the idea

- Stage is the lower environment used to validate changes before promoting them to production.
- Shared Worker settings can remain at the top level while resource choices belong to each named environment.
- Selecting an environment affects the deployed Worker identity as well as its bindings.
- Type generation needs the selected environment to include the correct resource handles.
- The Vite build’s environment choice must agree with the intended Wrangler deployment.

### What the course does

1. Outline a branch-based flow in which stage changes are verified before merging to the production branch.
2. Move data-service bindings and environment-specific settings into the stage environment.
3. Add stage selection to its deployment and environment-type generation scripts.
4. Resolve the existing queue-consumer conflict caused by the newly named stage Worker.
5. Repeat environment organization for the user application, selecting stage through the Vite-related environment setting.
6. Update the backend service binding and browser base host to the new stage names, then verify the application.

### Watch for

- The instructor deletes earlier tutorial Workers to resolve migration conflicts; this is a disposable-course-environment action, not a general production migration recipe.
- The deployment initially fails because the queue already has a consumer.
- The frontend’s build-tool-generated configuration rejects the same deployment flag used successfully for the plain Worker.

### Recall and practice

**Question:** Why does renaming the backend deployment require a frontend configuration change?

**Answer:** Its service binding targets the deployed service name, which now includes the stage suffix.

**Try it:** List the names and settings that must agree for a stage frontend to reach its stage backend.

**Success check:** You identify the selected environment, deployed backend name, service binding target, and browser base host.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:02:37 (course video 45) — Shared versus environment-specific settings
- 00:04:09 (course video 45) — Deployment and type-generation environment selection
- 00:05:42 (course video 45) — Queue consumer conflict
- 00:08:47 (course video 45) — Vite environment selection

Companion documentation: [Wrangler environments](https://developers.cloudflare.com/workers/wrangler/environments/). Check installed versions before copying recorded commands.

## 46. Creating a Production Instance

Dev Ops & Environments · Original video 00:08:47 · Evidence: transcript

A production environment is created with its own storage and messaging resources, mirroring stage without sharing application data. The backend is deployed first, followed by a production-specific Vite build of the user application. A failed dashboard then reveals a separate requirement: provisioning a database does not create the application tables.

Original video (course video 46) · [Focused study page](../lessons/0047-cloudflare-46-creating-a-production-instance.html)

### Understand the idea

- Environment isolation requires distinct R2, D1, KV, queue, and dead-letter resources, not only a different Worker name.
- The user application and data service must reference the same production D1 database where appropriate.
- Workflow names and service targets must also align with the production deployment.
- Frontend build mode selects production configuration and host information.
- Database schema creation is an independent step from creating the database resource.

### What the course does

1. Copy the stage environment configuration and rename it production.
2. Create production R2, D1, KV, main queue, and dead-letter queue resources and insert their identifiers.
3. Give the workflow a production-specific name and deploy the data service.
4. Configure the user application’s production database and backend service binding.
5. Add a production build mode and production environment file, then deploy the frontend.
6. Use Drizzle’s introspected SQL definitions to create the required tables in the new production D1 database and reload the application.

### Watch for

- The instructor removes remote-development settings from production bindings because local development should use the lower environment.
- The first production page fails because its D1 database has no tables; a successful Worker deployment does not prove database readiness.
- Schema SQL is applied manually here, including correcting statement execution order; this is not an automated migration pipeline.

### Recall and practice

**Question:** Why can the production Worker deploy successfully while the dashboard fails?

**Answer:** The Worker exists and runs, but queries fail until its separate production database has the expected schema.

**Try it:** Make a production-readiness checklist from this lesson’s resource and schema steps.

**Success check:** It distinguishes creating resources, connecting bindings, building the correct frontend, and creating database tables.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:32 (course video 46) — Create isolated production storage
- 00:02:03 (course video 46) — Production dead-letter configuration
- 00:04:35 (course video 46) — Production-specific Vite build
- 00:06:06 (course video 46) — Diagnose missing production tables

Companion documentation: [Wrangler environments](https://developers.cloudflare.com/workers/wrangler/environments/). Check installed versions before copying recorded commands.

## 47. Auto Deployments

Dev Ops & Environments · Original video 00:15:50 · Evidence: transcript

Cloudflare’s repository-connected builds automate the user application’s stage and production deployments. A push to the stage branch triggers its stage command, and merging stage into main triggers production. The monorepo build must install dependencies, build the shared data package, and then deploy the selected application; the lesson uses build failures to uncover missing dependencies and missing build-time variables.

Original video (course video 47) · [Focused study page](../lessons/0048-cloudflare-47-auto-deployments.html)

### Understand the idea

- The implementation uses Cloudflare’s Git-connected builds; GitHub Actions is discussed as an alternative for more customization.
- Monorepo deployment starts at the repository root so shared packages are built before the application consuming them.
- A clean build environment reveals dependencies accidentally supplied by the instructor’s local machine.
- Build-time variables are consumed while compiling the frontend; Worker runtime variables are a separate configuration surface.
- A successful build still needs an application-level smoke test.

### What the course does

1. Grant the Cloudflare integration access to the specific course repository and push a stage branch.
2. Configure the stage user application to build from that branch using a root deployment script.
3. Add dependency installation after the first build reports missing node_modules.
4. Declare TypeScript as a workspace-root development dependency after the next build cannot find tsc.
5. Configure production to build from main and test promotion through a pull request merge.
6. Set the frontend host and Cloudflare environment as build variables, rebuild, and verify the dashboard.

### Watch for

- The Git-connected build feature is described as beta at recording time.
- Backend auto-deployment configuration is left as an exercise; only frontend stage and production automation are demonstrated end to end.
- The instructor returns to CLI deployments for course speed, so later commands do not necessarily exercise the automated pipeline.

### Recall and practice

**Question:** Why did a locally successful build fail when tsc was unavailable in Cloudflare?

**Answer:** The local machine supplied TypeScript globally, while the clean build needed it declared and installed as a project dependency.

**Try it:** Explain the full command chain for a production frontend deployment from the monorepo root.

**Success check:** You include installation, shared-package build, application filtering, production build, and deployment.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:00 (course video 47) — Connect repository to Cloudflare builds
- 00:05:38 (course video 47) — Missing dependency installation
- 00:06:38 (course video 47) — Missing TypeScript build dependency
- 00:11:50 (course video 47) — Build variables versus runtime secrets

Companion documentation: [Wrangler environments](https://developers.cloudflare.com/workers/wrangler/environments/). Check installed versions before copying recorded commands.

## 48. Custom Domains & Routes

Dev Ops & Environments · Original video 00:15:29 · Evidence: transcript

The application receives its own domain, first through whole-host custom domains and then through path-based Worker routes. Separate production and stage hosts are configured, and the frontend’s socket host must be updated to match them. For short links, the final demonstrated design reserves an /r/ path prefix so requests can reach the data service without swallowing frontend routes.

Original video (course video 48) · [Focused study page](../lessons/0049-cloudflare-48-custom-domains-routes.html)

### Understand the idea

- A custom domain assigns an entire hostname to a Worker, including all paths on that host.
- A route selects traffic with a pattern, allowing different Workers to serve different paths under one domain.
- URL design and routing rules are coupled: random identifiers at the root are harder to distinguish from application routes.
- Build mode and browser host configuration remain important after domain changes.
- The lesson configures HTTP-to-HTTPS redirection at the network layer before Worker execution.

### What the course does

1. Purchase a demonstration domain in Cloudflare and add an HTTP-to-HTTPS redirect rule.
2. Configure the user application’s production custom domain and a stage subdomain.
3. Correct the stage Vite build mode when deployment unexpectedly targets the production domain.
4. Update frontend host variables in both Cloudflare builds and local environment files so WebSockets reconnect.
5. Demonstrate separate go and go-stage subdomains for the redirect service.
6. Replace that approach with /r/* route patterns and update the Hono redirect path to match.

### Watch for

- The exact domain price, DNS timing, and dashboard screens are historical details of the recording.
- The instructor observes old workers.dev URLs inactive in this configuration; do not treat that as a universal rule for every custom-domain deployment.
- Broad wildcard routes can capture frontend paths unintentionally; the instructor reserves /r/ to avoid ambiguity.

### Recall and practice

**Question:** Why introduce /r/ before every short-link identifier?

**Answer:** It gives the routing configuration an unambiguous pattern for sending short links to the data service while leaving other application paths with the frontend.

**Try it:** Assign /app, /api/auth, and /r/example to the appropriate service in the final design.

**Success check:** You place application and authentication paths with the user-application Worker and /r/example with the redirect data service.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:02:32 (course video 48) — Custom domains versus routes
- 00:06:07 (course video 48) — Wrong environment selected during stage build
- 00:07:07 (course video 48) — Socket host broken after domain change
- 00:13:13 (course video 48) — Reserve the /r/ redirect prefix

Companion documentation: [Wrangler environments](https://developers.cloudflare.com/workers/wrangler/environments/). Check installed versions before copying recorded commands.

## 49. Auth Overview

Better Auth · Original video 00:13:00 · Evidence: transcript

This overview separates authentication UI, server verification, sessions, and reusable middleware. Better Auth is chosen as an application-integrated library that supplies provider handling, client hooks, and schema generation while keeping authentication data in the project’s database. Its plugin model is introduced as the bridge to organizations, API keys, and the subscription integration developed later.

Original video (course video 49) · [Focused study page](../lessons/0050-cloudflare-49-auth-overview.html)

### Understand the idea

- Client hooks display session state and steer navigation, but server-side checks establish whether protected requests may proceed.
- After social sign-in, the application creates or locates its user and establishes a session for subsequent requests.
- Middleware centralizes request checks so multiple protected endpoints can reuse them.
- Better Auth handles provider-specific callbacks behind an authentication route instead of requiring separate hand-written integrations.
- Plugins can extend both behavior and the database schema, so configuration changes have data-model consequences.

### What the course does

1. Trace browser sign-in through the provider, application server, user records, and session creation.
2. Compare the lesson’s database-backed session explanation with its token-validation explanation.
3. Identify where reusable authentication middleware sits before protected route handlers.
4. Review Better Auth’s client hooks, server integration, and extension points.
5. Prepare to generate the core authentication tables before wiring the library into the application.

### Watch for

- The recording calls JWTs encrypted and presents them opposite session cookies; these are simplified course claims that need correction in a current technical reference.
- Managed-provider pricing and styling comparisons express the instructor’s recording-time experience and preferences.
- Added advice: authentication identifies a user; deciding which account’s records they may access also requires authorization checks.

### Recall and practice

**Question:** Why is hiding a dashboard with a frontend hook insufficient protection?

**Answer:** A caller can reach the server directly, so protected server requests still need authentication and authorization checks.

**Try it:** Draw a sign-in sequence and label which responsibilities belong to the provider, Better Auth server, database, and frontend hook.

**Success check:** Your explanation places identity verification and session validation on the server side, with UI state reflecting the result.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:02:03 (course video 49) — Session creation after social login
- 00:04:06 (course video 49) — Reusable authentication middleware
- 00:05:36 (course video 49) — Why the course chooses Better Auth
- 00:11:16 (course video 49) — Schema generation and plugins

Companion documentation: [RFC 7519: JSON Web Token](https://www.rfc-editor.org/info/rfc7519/). Check installed versions before copying recorded commands.

## 50. Configuration & Schema Creation

Better Auth · Original video 00:15:44 · Evidence: transcript

Better Auth configuration is connected to the existing Drizzle workflow so authentication tables can be generated and then created in D1. A shared factory supports both schema generation, where provider credentials are unnecessary, and runtime initialization, where they are required. The generated authentication schema is also registered with the runtime Drizzle adapter, and package exports are repaired after TypeScript’s output layout changes.

Original video (course video 50) · [Focused study page](../lessons/0051-cloudflare-50-configuration-schema-creation.html)

### Understand the idea

- The original workflow introspects manually created D1 tables into Drizzle schema definitions.
- The new authentication workflow goes from Better Auth configuration to Drizzle schemas to SQL creation statements.
- Generation-time configuration can use placeholder database information because it describes the schema rather than serving real requests.
- Runtime configuration receives the actual Drizzle-backed database and Google credentials.
- A single configuration factory keeps schema-generation choices aligned with application behavior.

### What the course does

1. Create shared Better Auth setup with email/password disabled and Google provider support.
2. Implement the runtime accessor using the project’s existing Drizzle database setup.
3. Configure a generation entry point with the SQLite Drizzle adapter and point the Better Auth CLI at it.
4. Generate the user, session, account, and verification schemas, then generate their SQL statements.
5. Execute the required statements against stage and production D1 and supply schema references to the runtime adapter.
6. Update package exports to the new dist/source layout after including the generation folder in TypeScript compilation.

### Watch for

- The instructor deletes generated migration metadata to produce creation SQL; the course is not using those files as an authoritative migration history.
- The narration says six tables after executing six statements, but the core schema explicitly shown is user, session, account, and verification.
- Adding a plugin later requires regenerating and applying its schema changes, not merely changing a runtime flag.

### Recall and practice

**Question:** Why share one Better Auth configuration factory between generation and runtime?

**Answer:** It keeps the enabled providers and plugins aligned, while allowing generation placeholders and real runtime dependencies to be supplied differently.

**Try it:** Write the ordered steps required after adding a plugin that needs a new table.

**Success check:** You include configuration, schema generation, SQL generation, applying database changes, and making the runtime adapter aware of the schemas.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:33 (course video 50) — Configuration-to-schema-to-SQL workflow
- 00:06:10 (course video 50) — Generation credentials versus runtime credentials
- 00:09:43 (course video 50) — Generated core authentication schemas
- 00:13:48 (course video 50) — Fix package output and exports

Companion documentation: [Better Auth basic usage](https://better-auth.com/docs/basic-usage). Check installed versions before copying recorded commands.

## 51. Integration with web app

Better Auth · Original video 00:16:58 · Evidence: transcript

Google social login is wired through the user application’s Hono Worker and Better Auth’s React client. The lesson configures Google OAuth origins and callback URLs, routes authentication traffic through Worker code, replaces mock frontend authentication, and guards dashboard navigation. It then repeats the necessary provider and runtime-secret configuration for the deployed custom domain.

Original video (course video 51) · [Focused study page](../lessons/0052-cloudflare-51-integration-with-web-app.html)

### Understand the idea

- The authentication handler accepts GET and POST requests under the auth path and lets Better Auth manage provider callbacks.
- Google OAuth configuration identifies permitted origins and the exact callback endpoint used by the application.
- Authentication routes must run Worker code rather than accidentally resolve through static asset handling.
- The React client supplies social sign-in, session retrieval, and sign-out behavior to existing UI components.
- A TanStack before-load session check redirects signed-out visitors away from dashboard pages.

### What the course does

1. Mount the Better Auth handler in Hono and pass server-side Google credentials through its configuration.
2. Create a Google OAuth web client with localhost origin and the /api/auth/callback/google redirect.
3. Configure local credentials, regenerate environment types, and set Worker-first handling for authentication, tRPC, and socket routes.
4. Replace mock clients in login and user components with the shared Better Auth React client.
5. Test Google sign-in, display session information, navigate home after sign-out, and add the dashboard route guard.
6. Register deployed callback URLs and configure Google credentials as Worker runtime secrets before testing the live domain.

### Watch for

- The local environment-file behavior is tied to a Wrangler update discussed in the recording.
- Google credentials belong to runtime server configuration; build variables are a different surface.
- The router guard protects navigation experience, while server API protection is implemented in the next lesson.

### Recall and practice

**Question:** Why can a working login still leave API data unprotected at this stage?

**Answer:** The UI and navigation now know about sessions, but protected API handlers still need server-side session enforcement.

**Try it:** Trace one Google sign-in from button click to the dashboard and identify the two callback destinations involved.

**Success check:** You distinguish Google returning to the auth callback endpoint from Better Auth navigating the signed-in user to /app.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:01 (course video 51) — Mount Hono authentication handler
- 00:03:33 (course video 51) — Google callback URL configuration
- 00:06:39 (course video 51) — Worker-first routing for auth paths
- 00:12:47 (course video 51) — Dashboard before-load session guard
- 00:15:49 (course video 51) — Deployed runtime secrets

Companion documentation: [Better Auth basic usage](https://better-auth.com/docs/basic-usage). Check installed versions before copying recorded commands.

## 52. Protecting our API Routes

Better Auth · Original video 00:08:42 · Evidence: transcript

The server now protects both tRPC calls and WebSocket connections with Hono authentication middleware. Better Auth validates the session from request headers, and the middleware stores the authenticated user ID in typed request context. That identity replaces hardcoded account values in the socket proxy and tRPC context, so each user sees their own data.

Original video (course video 52) · [Focused study page](../lessons/0053-cloudflare-52-protecting-our-api-routes.html)

### Understand the idea

- A frontend dashboard guard does not stop someone from calling its underlying API or socket endpoint directly.
- Middleware runs before the route handler and either rejects the request or passes it onward with next.
- The authentication route remains accessible because signed-out users need it to sign in.
- Typed Hono context carries verified identity to later handlers without asking the browser to supply a trusted account ID.
- The course uses one user per application account; an organization model would require different account selection.

### What the course does

1. Attach a custom middleware to tRPC and socket routes, initially logging to verify that it runs.
2. Extract a helper that initializes the server-side Better Auth instance from environment configuration.
3. Read the session using request headers and return an unauthorized response if no authenticated user exists.
4. Place the user ID in Hono context and retrieve it in both protected route handlers.
5. Set the socket’s forwarded account header from that identity and pass it into tRPC context creation.
6. Deploy and verify that records associated with the old hardcoded identity are no longer listed for the signed-in user.

### Watch for

- The disappearance of old demo links reflects a different identity filter, not deletion of the records.
- Account and user IDs are deliberately equivalent only for this course’s single-user-account model.
- Added advice: every data operation must continue applying account authorization; a valid session alone does not permit access to arbitrary records.

### Recall and practice

**Question:** Why should the socket’s account header come from middleware rather than browser input?

**Answer:** Middleware derives it from a validated session, preventing the browser from simply selecting another account’s tracker.

**Try it:** Explain the expected result for a signed-out API request and a signed-in request from a different user than the demo account.

**Success check:** The first is rejected; the second proceeds with its own identity and does not inherit the hardcoded demo account’s data.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:00 (course video 52) — Identify unprotected tRPC and socket endpoints
- 00:03:02 (course video 52) — Apply middleware selectively
- 00:05:06 (course video 52) — Validate session from request headers
- 00:06:38 (course video 52) — User identity versus organization identity

Companion documentation: [Better Auth basic usage](https://better-auth.com/docs/basic-usage). Check installed versions before copying recorded commands.

## 53. Better Auth Stripe Integration

Payments with Stripe · Original video 00:15:57 · Evidence: transcript

The Better Auth Stripe plugin is added to the shared authentication configuration, first to create Stripe customers and then to support subscriptions. Each configuration change is used to regenerate the authentication schema and produce the corresponding database alteration. The lesson also separates generation-time Stripe setup from runtime configuration so the deployed application can receive its own key and webhook settings.

Original video (course video 53) · [Focused study page](../lessons/0054-cloudflare-53-better-auth-stripe-integration.html)

### Understand the idea

- A Stripe customer is a Stripe-side identifier mapped to an application user; an email address alone is not that mapping.
- Customer creation at signup can happen before the user purchases anything.
- The plugin supplies server subscription behavior, client APIs, and much of the webhook synchronization logic.
- Enabling plugin features changes schema requirements: a customer ID field is added to users and a subscription table is introduced.
- Schema metadata lets Drizzle distinguish an incremental alteration from recreating all tables.

### What the course does

1. Install Stripe and the Better Auth Stripe plugin alongside the matching Better Auth version used by the lesson.
2. Extend the shared authentication factory to accept Stripe configuration and enable customer creation at signup.
3. Create a Stripe sandbox and provide the key required by this generation setup.
4. Regenerate the schema and apply the user customer-ID column alteration to stage and production D1.
5. Enable subscriptions, regenerate again, and create the subscription table in both databases.
6. Rebuild the shared package and allow runtime Stripe configuration to be supplied by the consuming application.

### Watch for

- Versions 1.3.4 are pinned in the recording to address compatibility; these are historical course versions, not a recommendation for a new project today.
- Deleting Drizzle metadata loses incremental-change knowledge, so generated SQL must be reviewed against the actual database.
- The instructor explicitly limits this section to basic subscriptions, not advanced usage-based or multi-product billing.

### Recall and practice

**Question:** Why does adding the Stripe plugin require database work?

**Answer:** The plugin needs persistent customer and subscription state, so its configuration introduces columns and tables the database must actually contain.

**Try it:** Compare the schema changes caused by enabling customer creation and by enabling subscriptions.

**Success check:** You identify the user’s Stripe customer-ID field for the first and the subscription table for the second.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:31 (course video 53) — Customer mapping and plugin benefits
- 00:05:08 (course video 53) — Pin matching plugin versions
- 00:10:14 (course video 53) — Generate the Stripe customer-ID schema change
- 00:12:17 (course video 53) — Enable and generate subscriptions

Companion documentation: [Better Auth Stripe plugin](https://better-auth.com/docs/plugins/stripe). Check installed versions before copying recorded commands.

## 54. Creating Stripe Products

Payments with Stripe · Original video 00:11:44 · Evidence: transcript

The application’s authentication initialization is updated to accept Stripe configuration, and Node compatibility is enabled after a dependency fails on a Node utility import. A new sign-in verifies automatic Stripe customer creation. The instructor then creates three sandbox subscription products and maps their recurring price IDs to named plans in Better Auth.

Original video (course video 54) · [Focused study page](../lessons/0055-cloudflare-54-creating-stripe-products.html)

### Understand the idea

- A Stripe product describes what is sold; the price ID identifies the particular recurring charge used in checkout.
- The server maps stable application plan names to Stripe price IDs.
- Cloudflare compatibility settings matter when an imported SDK depends on Node APIs.
- The shared auth helper is simplified to accept an API key and construct the Stripe client internally.
- A user created before the plugin was enabled does not necessarily exercise customer-on-signup behavior.

### What the course does

1. Pass initial Stripe settings into the expanded authentication helper and load the sandbox secret key from environment configuration.
2. Regenerate types and add Node compatibility after the dynamic util dependency error.
3. Refactor the helper interface from a Stripe client argument to an API-key argument, then rebuild.
4. Sign in with a fresh test user and verify the matching Stripe customer ID in D1 and Stripe.
5. Create Basic, Pro, and Enterprise recurring products and copy their price IDs.
6. Populate the server plan list with lowercase plan names and the corresponding configured prices.

### Watch for

- The $9, $25, and $259 tiers are illustrative course product choices.
- Deleting tutorial user rows creates duplicate Stripe customers during testing; this is not a migration strategy for existing real users.
- The hardcoded plan list is a deliberate teaching simplification; dynamic plans, annual pricing, and trials are discussed as extensions.

### Recall and practice

**Question:** Which Stripe identifier should the backend plan configuration use for billing?

**Answer:** It uses the recurring price ID associated with the product, not merely the product’s identity or display name.

**Try it:** Create a paper mapping of application plan name, Stripe product, and recurring price ID for three tiers.

**Success check:** Each plan points to a specific recurring price, and you can explain where its billing amount comes from.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:02:04 (course video 54) — Node dependency compatibility error
- 00:03:37 (course video 54) — Simplify the Stripe helper interface
- 00:05:10 (course video 54) — Verify customer creation for a fresh user
- 00:07:11 (course video 54) — Create products and obtain price IDs

Companion documentation: [Better Auth Stripe plugin](https://better-auth.com/docs/plugins/stripe). Check installed versions before copying recorded commands.

## 55. Adding Subscriptions in the User Application

Payments with Stripe · Original video 00:20:36 · Evidence: transcript

The frontend gains an upgrade page, subscription status sidebar, and cancellation dialog through Better Auth’s Stripe client plugin. The lesson exercises subscription listing, checkout, upgrades, cancellation, and restoration, while forwarding Stripe webhooks to localhost for state synchronization. Several failures show why the browser, local database, Stripe account, and webhook listener must all agree.

Original video (course video 55) · [Focused study page](../lessons/0056-cloudflare-55-adding-subscriptions-in-the-user-application.html)

### Understand the idea

- The client plugin exposes subscription operations; the server plugin owns their authenticated Stripe integration.
- Plan names in the UI must match the names configured on the server, while displayed prices are hardcoded in this demonstration.
- The subscription list populates the UI’s current-plan state.
- Stripe’s hosted checkout and customer portal handle payment and subscription-change screens.
- Webhooks update local subscription state after Stripe events, so a successful checkout page alone does not prove synchronization.

### What the course does

1. Enable subscriptions in the React Stripe client plugin and add upgrade, sidebar, and cancellation components.
2. Load subscriptions on page entry and call upgrade with the selected plan and return destinations.
3. Register the subscription schema with the Drizzle adapter after listing fails with a missing-table mapping error.
4. Start the Stripe CLI listener, supply its signing secret, and verify it uses the intended sandbox account.
5. Complete sandbox checkout and enable plan switching plus eligible products in the Stripe customer portal.
6. Verify upgrades and prorated examples, then cancel at period end and restore the subscription while observing webhook updates.

### Watch for

- Wrong CLI account and signing configuration leave Stripe and D1 out of sync; the instructor resets disposable data and uses a new test user to recover.
- Hardcoded frontend price cards can drift from actual Stripe prices.
- Cancellation is scheduled for the period end in the demonstrated flow; it does not necessarily mean immediate loss of service.

### Recall and practice

**Question:** Why can Stripe show an upgraded subscription while the application still shows the old plan?

**Answer:** The application relies on webhook processing to update its local subscription records; an incorrect account, endpoint, or signing secret can prevent that update.

**Try it:** Outline a sandbox test covering signup, first purchase, upgrade, cancellation, and restoration.

**Success check:** For each transition you verify both the Stripe state and the application’s stored and displayed subscription state.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:04:37 (course video 55) — Subscription upgrade API
- 00:06:09 (course video 55) — Register subscription schema in adapter
- 00:07:11 (course video 55) — Local webhook forwarding
- 00:12:20 (course video 55) — Enable customer-portal plan switching
- 00:17:24 (course video 55) — Cancellation and restoration

Companion documentation: [Better Auth Stripe plugin](https://better-auth.com/docs/plugins/stripe). Check installed versions before copying recorded commands.

## 56. Deploying & Live Webhooks

Payments with Stripe · Original video 00:16:25 · Evidence: transcript

The subscription integration is deployed to the stage application with a publicly reachable Stripe webhook endpoint, while still using sandbox payments. Nonsecret price IDs move into Wrangler variables, and sensitive keys are configured as runtime secrets. The instructor troubleshoots endpoint typos and the mistaken use of a webhook endpoint ID, then fixes a missing Better Auth application secret discovered in logs.

Original video (course video 56) · [Focused study page](../lessons/0057-cloudflare-56-deploying-live-webhooks.html)

### Understand the idea

- Nonsecret runtime values can be versioned in Wrangler configuration and deployed with the Worker.
- Stripe API credentials, the webhook signing secret, and the Better Auth application secret serve different purposes.
- A deployed webhook needs a valid public URL and the signing secret belonging to that endpoint.
- Stripe delivery logs and replay controls help distinguish connection failures from application verification failures.
- OAuth origins and callbacks must include the stage domain as well as previously configured local or production hosts.

### What the course does

1. Move sandbox price IDs into stage variables, regenerate types, and deploy the stage user application.
2. Create a Stripe endpoint at the Better Auth webhook path and select checkout completion and subscription update/deletion events discussed in the lesson.
3. Configure Stripe and Google runtime credentials and register the stage Google callback.
4. Test signup, purchase, and upgrade; inspect failed webhook deliveries when the plan remains unchanged.
5. Correct the endpoint URL and replace the endpoint ID with its actual signing secret, then replay events and verify cancellation.
6. Add a dedicated Better Auth secret to shared setup and Worker runtime configuration, rebuild, and deploy.

### Watch for

- Despite the title’s “Live Webhooks,” this lesson uses deployed stage endpoints with Stripe sandbox payments; real-money production setup is left to the learner.
- The missing Better Auth secret is fixed only late in the recording; added advice: configure a suitable secret before exposing authentication.
- The displayed webhook event set and secret-generation approach should be checked against current official documentation before implementation.

### Recall and practice

**Question:** What is the difference between a webhook endpoint ID and its signing secret?

**Answer:** The ID identifies the configured endpoint; its signing secret is the value used to verify webhook signatures and is what the integration requires.

**Try it:** Create a diagnosis sequence for a paid-plan change that is absent from the dashboard.

**Success check:** You check Stripe event delivery, exact endpoint URL, response error, signing secret, and resulting local subscription state.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:00:00 (course video 56) — Separate public price IDs from secrets
- 00:02:31 (course video 56) — Configure deployed webhook endpoint
- 00:08:04 (course video 56) — Diagnose webhook URL typos
- 00:09:04 (course video 56) — Endpoint ID versus signing secret
- 00:12:06 (course video 56) — Fix missing Better Auth secret

Companion documentation: [Stripe webhooks](https://docs.stripe.com/webhooks). Check installed versions before copying recorded commands.

## 57. Completing the User Application Dashboard

Wrapping Up · Original video 00:09:19 · Evidence: transcript

The dashboard’s placeholder statistics are replaced with account-filtered database queries exposed through existing tRPC routes. The queries calculate recently active links, click counts over several windows, and country breakdowns. The final UI cleanup supplies the frontend build variable used to construct clickable short-link URLs.

Original video (course video 57) · [Focused study page](../lessons/0058-cloudflare-57-completing-the-user-application-dashboard.html)

### Understand the idea

- Active links means links clicked in the last hour, grouped by link identity and name, with counts and latest click time.
- Every analytics query is scoped to the authenticated account or user.
- Conditional aggregation can calculate 24-hour and 48-hour metrics while scanning the broader 48-hour window once.
- Country grouping turns raw click events into counts and shares of the overall total.
- A frontend environment variable is compiled into the link display, so both local and automated builds need its value.

### What the course does

1. Add an active-links query filtered by time and account, then connect it to the corresponding tRPC handler.
2. Click a real short link and verify that the dashboard shows its count and recent activity time.
3. Add total counts for the last hour, 24/48 hours, and 30 days.
4. Add the 30-day country grouping and replace remaining mock endpoint responses.
5. Pass authenticated user identity from tRPC context into every new query.
6. Set the frontend backend-host variable locally and in stage and production build settings to replace undefined displayed URLs.

### Watch for

- The instructor notes that very large click volumes may need a more suitable analytics storage design than these direct D1 scans.
- Several time-window queries repeat logic; parameterizing the shared pattern is discussed but not implemented.
- The video uses go-stage/go host examples after the earlier /r/ route demonstration; added advice: choose one consistent deployed URL convention.

### Recall and practice

**Question:** Why must analytics queries use the user ID from tRPC context?

**Answer:** That context contains the authenticated identity, allowing results to be restricted to the requesting user’s account.

**Try it:** Define expected dashboard results for two users whose links receive different clicks in the last hour.

**Success check:** Your expected counts, active links, and country breakdowns remain separated by account.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:02 (course video 57) — Active links grouped by recent clicks
- 00:04:03 (course video 57) — Combined 24-hour and 48-hour aggregation
- 00:05:34 (course video 57) — Country breakdown query
- 00:07:05 (course video 57) — Fix the undefined short-link host

Companion documentation: [Cloudflare Workers Vitest integration](https://developers.cloudflare.com/workers/testing/vitest-integration/). Check installed versions before copying recorded commands.

## 58. Writing Tests

Wrapping Up · Original video 00:14:52 · Evidence: transcript

The testing section demonstrates unit tests around business logic, using mocks to isolate database calls, workflow scheduling, and Durable Object dependencies. Queue-handler tests check forwarded data, operation order, and error behavior. Tracker tests cover alarm creation, avoiding duplicate alarms, broadcasting a batch to multiple clients, advancing offsets, and deleting older clicks.

Original video (course video 58) · [Focused study page](../lessons/0059-cloudflare-58-writing-tests.html)

### Understand the idea

- A mock substitutes controlled behavior for an external dependency so a test can focus on application decisions.
- Call-order checks are useful when later work depends on an earlier operation succeeding.
- Failure-path tests make the intended relationship between database writes and workflow scheduling explicit.
- Durable Object logic can be tested with mocked SQL, storage, and sockets, but this requires more setup.
- The instructor prefers small callable units of business logic over deeply embedded chains that are difficult to exercise.

### What the course does

1. Mock click persistence and workflow scheduling in the queue-handler tests and reset mocks between cases.
2. Pass a representative click event and check that the expected data reaches persistence.
3. Verify that persistence precedes workflow scheduling, and that a simulated database error prevents dependent work.
4. Construct mocked Durable Object storage, SQL, and socket dependencies for tracker tests.
5. Test the no-alarm and existing-alarm branches of adding a click.
6. Trigger an alarm with a prepared batch and two sockets, then assert delivery, offset updates, and cleanup.

### Watch for

- These examples illustrate the instructor’s testing preferences, not proof that request routing or platform integration never needs testing.
- The tests mock Cloudflare behavior and therefore do not validate actual runtime storage, alarm, or WebSocket semantics.
- Several simple call-through tests are explicitly described as scaffolding for future, more substantial business logic.

### Recall and practice

**Question:** What can a mocked tracker test prove, and what can it not prove?

**Answer:** It can check the application’s decisions and calls for controlled inputs; it cannot prove that the real Cloudflare runtime behaves exactly like those mocks.

**Try it:** Design one meaningful failure-path test for the tracker or queue handler and state the behavior it protects.

**Success check:** Your test links a realistic failure to an explicit business requirement, rather than merely repeating the implementation’s call sequence.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:02:32 (course video 58) — Mock queue-handler dependencies
- 00:04:02 (course video 58) — Assert persistence before workflow scheduling
- 00:05:04 (course video 58) — Test database failure behavior
- 00:09:40 (course video 58) — Alarm creation and existing-alarm branches
- 00:12:13 (course video 58) — Broadcast and cleanup assertions

Companion documentation: [Cloudflare Workers Vitest integration](https://developers.cloudflare.com/workers/testing/vitest-integration/). Check installed versions before copying recorded commands.

## 59. Stretch Goals

Wrapping Up · Original video 00:10:27 · Evidence: transcript

The final lesson turns the completed tutorial into a set of independent extension projects. Its central gap is that subscription plans currently accept payment without enforcing different feature allowances. The instructor proposes notifications, custom links and domains, thoughtful deletion behavior, smarter evaluations and scheduling, and an administration interface that can inspect AI quality.

Original video (course video 59) · [Focused study page](../lessons/0060-cloudflare-59-stretch-goals.html)

### Understand the idea

- Taking a subscription payment and enforcing what that subscription permits are separate application responsibilities.
- Custom human-readable slugs introduce uniqueness, reservation, and conflict-handling requirements.
- Deleting a public link affects existing references across the internet and may call for a useful fallback rather than a generic missing page.
- Evaluation timing can depend on traffic patterns, stale activity, or unusual spikes instead of only a fixed schedule.
- Stored HTML, text, and screenshots provide material for auditing AI evaluations and building quality scores.

### What the course does

1. Define meaningful entitlements for free and paid plans, then decide where backend limits should be enforced.
2. Consider email alerts for unhealthy destinations and optional affiliate-product suggestions.
3. Explore custom slugs with availability checks, temporary reservation, and confirmed ownership.
4. Design tenant domains or subdomains and an account-specific fallback for removed links.
5. Improve destination evaluation using request status, industry-specific interpretation, and better scheduling triggers.
6. Build a separate administrative interface to inspect traffic, workflow outputs, screenshots, and prediction quality.

### Watch for

- These are proposed stretch goals; the course does not implement them.
- The instructor explicitly says paid plans do not yet change feature access or limits.
- Added advice: choose one bounded extension with observable success criteria so independent practice tests understanding rather than becoming another broad unfinished project.

### Recall and practice

**Question:** Which missing feature most directly connects billing to actual product behavior?

**Answer:** Backend enforcement of plan entitlements and usage limits, because the existing plans currently collect payment without changing allowed features.

**Try it:** Choose one stretch goal and write a small design with a trigger, affected data, success case, and failure case.

**Success check:** You can explain the extension without replaying a tutorial and identify how you will demonstrate that it works.

These prompts are added teaching material; try before reading the answer.

### Source moments

- 00:01:01 (course video 59) — Paid-plan entitlements remain unimplemented
- 00:02:34 (course video 59) — Custom slug reservation and uniqueness
- 00:04:07 (course video 59) — Deletion and existing public links
- 00:06:40 (course video 59) — Traffic-triggered evaluation scheduling
- 00:08:10 (course video 59) — Administrative evaluation and AI quality

Companion documentation: [Cloudflare Workers Vitest integration](https://developers.cloudflare.com/workers/testing/vitest-integration/). Check installed versions before copying recorded commands.

## Companion primary sources

- [Cloudflare Workers runtime APIs](https://developers.cloudflare.com/workers/runtime-apis/) — Worker execution APIs and partial Node.js compatibility.
- [Cloudflare resource bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) — Resource access through env and local versus remote bindings.
- [Drizzle with Cloudflare D1](https://orm.drizzle.team/docs/sqlite/connect-cloudflare-d1) — The D1 driver and database-query integration. Check documentation matching the installed version.
- [tRPC introduction](https://trpc.io/docs/) — Shared TypeScript API contracts and client/server integration.
- [Hono on Cloudflare Workers](https://hono.dev/docs/getting-started/cloudflare-workers) — Routing requests with Hono and binding the Worker fetch handler.
- [Cloudflare Request API](https://developers.cloudflare.com/workers/runtime-apis/request/) — request.cf metadata, including visitor geolocation and the serving colo.
- [How Workers KV works](https://developers.cloudflare.com/kv/concepts/how-kv-works/) — Read caching and eventual consistency; reason about stale redirects.
- [Queues delivery guarantees](https://developers.cloudflare.com/queues/reference/delivery-guarantees/) — At-least-once delivery and duplicate-safe processing.
- [Queues pricing](https://developers.cloudflare.com/queues/platform/pricing/) — Current plan access and per-operation billing, including retries.
- [Workers pricing](https://developers.cloudflare.com/workers/platform/pricing/) — Current subscription, request, and CPU pricing; recording-era estimates are not a quotation.
- [Workflows Workers API](https://developers.cloudflare.com/workflows/build/workers-api/) — Workflow instances, named steps, and their control API.
- [Workflows limits](https://developers.cloudflare.com/workflows/reference/limits/) — Step-result limits, streamed output, and external artifact storage.
- [What are Durable Objects?](https://developers.cloudflare.com/durable-objects/concepts/what-are-durable-objects/) — Stateful coordination and per-object storage.
- [Durable Objects WebSockets](https://developers.cloudflare.com/durable-objects/best-practices/websockets/) — WebSocket coordination and hibernation behavior.
- [Workers remote procedure calls](https://developers.cloudflare.com/workers/runtime-apis/rpc/) — Calling service entrypoints and Durable Object methods through bindings.
- [Wrangler environments](https://developers.cloudflare.com/workers/wrangler/environments/) — Environment-specific Worker configuration and non-inherited bindings.
- [RFC 7519: JSON Web Token](https://www.rfc-editor.org/info/rfc7519/) — Distinguish token claims, signing, and encryption.
- [Better Auth Stripe plugin](https://better-auth.com/docs/plugins/stripe) — Subscription schema, checkout integration, and webhook configuration.
- [Stripe webhooks](https://docs.stripe.com/webhooks) — Signed events, delivery retries, duplicate events, and endpoint handling.
- [Cloudflare Workers Vitest integration](https://developers.cloudflare.com/workers/testing/vitest-integration/) — Complement mocked business-logic tests with actual Worker runtime tests.
- [Better Auth basic usage](https://better-auth.com/docs/basic-usage) — Client session state and server-side session validation.

Ask the agent follow-up questions using the course lesson number and your attempted explanation.
