Render Tutorials
Stock research: from flaky to reliable

What you'll build

⏱ 5 min

You are starting from a real workshop app: enter a ticker, run four web searches, then stream a memo. In the baseline version, one intermittent search error can stop the full run. By the end, you keep the same UI and flow, but runs finish far more often.

This tutorial stays in one repo the whole time: fork ojusave/workshop-demo, deploy it, edit a small set of files, then add one more Render service near the end.

Important note

This app and tutorial are for education and demos only. Output is not investment advice, and it should not be used for real financial decisions.

The presenter, conference, and company are not responsible for actions taken from generated output.

Golden path

Use a single repository throughout.

PhaseStepsWhat you do
Deploy baseline02Fork, Blueprint deploy, ANTHROPIC_API_KEY + EXA_API_KEY, confirm the baseline run in the UI
See the problem03–04Trace one run; understand why Promise.all + maybeFail kills most batches
Add reliable task runs05–08Edit 5 files: SDK, task(), index.ts, dispatch from research.ts
Wire Render services09–10Redeploy web, then create the task service and copy its slug env var
Confirm11Same ticker, far more runs complete

Files you edit (everything else stays untouched):

  • tasks/package.json
  • tasks/src/search.ts
  • tasks/src/index.ts (new)
  • tasks/src/research.ts
  • server/package.json

Files to understand before editing:

  • server/src/runner.ts: starts one research run and streams progress events to the browser.
  • tasks/src/research.ts: orchestrates fan-out search work and fan-in synthesis.
  • tasks/src/search.ts: runs each Exa query and includes the maybeFail transient error simulation.
  • tasks/src/synthesize.ts: builds the final memo from collected sources.

Why this helps

The baseline app fails often by design: each search has about a 30% chance to throw a fake rate-limit error, and all four searches run in one Promise.all.

That means full-run success is about 0.7^4 ≈ 24%.

The goal is simple: keep the UI and business logic, but isolate each search run and add retries so one transient dependency error does not fail the full user request as often.

What you’ll learn

  • Why parallel work in one process fails in batches.
  • How to register a task with retry policy.
  • How the web service dispatches task runs and waits for completion.
  • How to add the second Render service when you are ready.
  • How to verify the before/after reliability difference.

What you’ll need

  • A GitHub account.
  • A Render account. Starter plan is enough.
  • Node.js 20+ if you run the sample app locally.
  • Basic Git and TypeScript. No prior task-run experience.

Final architecture

flowchart LR
  browser(["Browser"])
  web["Web service<br/>waits + memo"]
  tasksvc["Task service<br/>one run per search"]
  exa(["Exa"])
  claude(["Anthropic"])

  browser --> web
  web -->|4 runs| tasksvc
  tasksvc --> exa
  web --> claude

The service name and exact Render setup show up when we reach that step. For now, focus on the baseline behavior and the small code diff.

On this page

  • One fork from workshop-demo: deploy baseline, edit five files, then add one more Render service
  • The baseline fails often because four searches run in one Promise.all with simulated transient errors
  • You keep the same app shape and upgrade reliability in place