React Prework — Optional Head Start¶
Optional. Not required for the capstone. This packet is for students who finish the PRS backend capstone early and want to get a running start on the React pass instead of waiting. Nothing here is graded, and skipping it costs you nothing — the React pass teaches all of it from scratch.
What it front-loads: React opens with two intro lessons — JavaScript, then TypeScript, for C# developers (Lessons 1–2) — and first fetches real data in Lesson 4. This packet is a light on-ramp to exactly that material, ending with a mini-project that calls your own PRS API from the browser. When those lessons arrive, they'll feel like review instead of a firehose.
The one big idea: you already know how to program. React is written in JavaScript +
TypeScript, and TypeScript is close enough to C# that most of this is translation, not
new concepts. Learn the handful of genuinely new pieces (destructuring, spread, modules,
fetch) and the rest maps onto what you already know.
For the full treatment of Parts 1–2 below, the actual lesson guides are right here in this folder: JavaScript for C# developers and TypeScript for C# developers. This packet is the short, do-it-yourself version — the lessons go deeper.
Before you start¶
- Node.js installed (
node --versionshould print v18+). - Your PRS API runs and you can hit it (you just finished it). Run it over
http, nothttps, so the browser doesn't block it over a self-signed dev certificate — the same reason the Insomnia collection uses anhttp://base URL. - Two tools, two jobs: you edit code in VS Code and observe in the browser — the running page plus DevTools → Console / Network (F12, or right-click → Inspect). You never type code into DevTools; it's only for reading output. (No Insomnia here — this is the front-end world now.)
Part 0 — Set up the scratch project (once)¶
Create the same throwaway TypeScript sandbox the React pass uses in Lessons 1–2. From a folder outside your API work:
npm create vite@latest js-ts-playground -- --template vanilla-ts
cd js-ts-playground
npm install
npm run dev
--template vanilla-ts gives you plain TypeScript with no framework — just a file to
run code in. Open the js-ts-playground folder in VS Code — that's where you edit
code. npm run dev prints a URL (usually http://localhost:5173); open it in the
browser and show DevTools → Console. Now edit src/main.ts in VS Code and
save — Vite reloads the page automatically, and anything you console.log(...) appears in
the browser Console. You write code in the editor; the browser (and its DevTools) is only
for running and reading output. This is your C# console app equivalent.
You are not throwing this project away — it's the exact sandbox Lessons 1–2 use, so you're set up early.
Part 1 — JavaScript & TypeScript warm-up¶
Work through this checklist in src/main.ts, logging results to the Console. Each item is
anchored to the C# you already know. Don't just read — type a line or two for each and
watch the output. If you already know modern JS/TS, skim this and jump to Part 2.
JavaScript (Lesson 1 material):
-
constby default,letwhen a value changes; nevervar. Compare with===/!==(never==).constis like C#'svar, but not reassignable. - Arrow functions —
const double = (n) => n * 2;— same idea and same=>as a C# lambda. - Template literals —
`${item} — $${price}`— C#'s$"{item} — ${price}", with backticks. - Objects & destructuring —
const { name, price } = product;pulls properties into variables in one line. No C# equivalent; worth slowing down for. - Spread —
const copy = { ...product, onSale: true };makes a new object (doesn't mutate). Also[...arr, next]for arrays. -
.map()and.filter()— these are LINQ's.Select()and.Where():products.map(p => p.name),products.filter(p => p.price < 50). Both return a new array. Rendering a list in React is just.map()— this is the single most important pattern to bring with you. - ES modules —
exportfrom one file,importinto another. Components are default exports; helpers/interfaces/hooks are named exports (import { x } from).
TypeScript (Lesson 2 material):
- The type map:
numberfor every C# numeric (int/long/double/decimal),string,boolean,T[]forList<T>,Record<K,V>forDictionary<K,V>. -
interfacedescribes a data shape — the front-end echo of your C# model class. We prefix ours withI(IProduct,IRequest). TypeScript matches structurally (by shape, not name) — that's the one real surprise coming from C#. - Optional
?(phone?: string) and union|(number | null,"NEW" | "REVIEW" | "APPROVED") — how TS expressesstring?and enum-like values. -
async/await— identical keywords to C#. The only new part: a JS async function returns aPromise<T>(C#'sTask<T>). You'll use this in Part 2.
References (official docs): - TypeScript for Java/C# Programmers — written for exactly your background. - MDN: Destructuring — the one genuinely new idea.
Part 2 — Mini-project: call your own PRS API from the browser¶
This is the payoff. You'll fetch real data from the PRS backend you just built, type it,
transform it, and render it — the exact shape of what React does, minus React. Do it all
in src/main.ts.
1. Describe the data with an interface¶
Mirror your PRS Product model as a TypeScript interface (this is Lesson 2's whole
point — the interface is the front-end echo of your C# model):
export interface IProduct {
id: number;
partNumber: string;
name: string;
price: number;
unit: string;
photoPath: string | null; // C# string? → string | null
vendorId: number;
vendor?: { id: number; code: string; name: string }; // included by GetAll — see below
}
2. Fetch the list¶
fetch + await + .json() is the whole data-loading pattern. Point BASE_URL at
your PRS API's port:
const BASE_URL = "http://localhost:5555/api"; // ← use your API's actual port
async function loadProducts(): Promise<IProduct[]> {
const response = await fetch(`${BASE_URL}/products`);
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
return response.json();
}
async function main() {
try {
const products = await loadProducts();
console.log(products.length, "products");
console.log(products.map((p) => p.name)); // LINQ .Select
console.log(products.filter((p) => p.price < 50)); // LINQ .Where
console.log(products[0].vendor); // the included nav object
} catch (error) {
console.error(error);
}
}
main();
Notice products[0].vendor is a whole nested object, not just a vendorId — because your
ProductsController.GetAll uses .Include(product => product.Vendor). That's the same
navigation-property idea from the API pass, now visible in the browser.
async/await, not.then()—fetchreturns a Promise (JavaScript'sTask<T>), and there are two ways to handle one:awaitit inside anasyncfunction and wrap errors intry/catch— the C# style, and what this packet uses — or chain.then(...).catch(...). They're the same thing, two spellings. You'll see the.then()form in step 5 and all over MDN; preferasync/awaitfor readable top-to-bottom code.
3. Hit the CORS wall (expected!)¶
Your first run will very likely fail with a message like "blocked by CORS policy" in
the Console, and the Network tab will show the request in red. This is not a bug — it's the
browser's Cross-Origin Resource Sharing rule: your scratch app runs on
http://localhost:5173 and your API on a different port, so they're different origins,
and the browser blocks the cross-origin read unless the API opts in.
The fix: your PRS Program.cs already has a wide-open CORS policy registered from the API
pass, with the app.UseCors() line commented out. Uncomment it and restart the API:
You're getting an early look at something the React pass covers in Lesson 4 ("hit a CORS error, fix it live"). Leave the policy wide open — this course uses a permissive dev CORS policy on purpose; don't lock it down.
Reference: MDN: Cross-Origin Resource Sharing (CORS).
4. Render the list to the page (a .map() preview of React)¶
Rendering a list is just a transform: an array of data mapped into an array of
markup. Replace the sandbox #app contents:
async function render() {
const products = await loadProducts();
const app = document.querySelector<HTMLDivElement>("#app")!;
app.innerHTML = `
<ul>
${products.map((p) => `<li>${p.name} — $${p.price} (${p.vendor?.name ?? "no vendor"})</li>`).join("")}
</ul>
`;
}
render();
When React arrives, you'll write almost this exact .map() — but producing JSX
elements instead of a string of HTML, and letting React put them on the page. Same idea,
nicer tools.
5. (Optional) The other style — a .then() chain¶
.then() / .catch() is the original way to handle a Promise, and you'll meet it
constantly — MDN examples use it, and the real TableServe/PRS front ends centralize fetch
in a fetchUtilities module composed with .then() chains. It's the same Promise as
async/await, just written as a chain; small compose-and-forget helpers read nicely this
way:
async function checkStatus(response: Response): Promise<Response> {
if (response.ok) return response;
throw new Error(`There was an error retrieving data (${response.status}).`);
}
function parseJSON<T>(response: Response): Promise<T> {
return response.json();
}
// .then() runs on success; .catch() handles a rejection — the chain form of try/catch:
fetch(`${BASE_URL}/products`)
.then(checkStatus)
.then((response) => parseJSON<IProduct[]>(response))
.then((products) => console.log(products.length, "products"))
.catch((error) => console.error(error));
That's the exact same request as loadProducts() in step 2 — pick whichever reads better.
This course leans on async/await (it maps to C#) and reaches for .then() chains for
tiny helpers like these.
6. (Optional) Create a product with POST¶
Send a body and read back what the API returns (a preview of the create-form flow):
const response = await fetch(`${BASE_URL}/products`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
partNumber: "PRE-001", name: "Prework Widget", price: 12.99,
unit: "Each", photoPath: null, vendorId: 1,
}),
});
const created = await response.json();
console.log("created id:", created.id, "with vendor:", created.vendor?.name);
Because main.ts is an ES module, you can await at the top level — no wrapper function
needed.
Reference: MDN: Using the Fetch API.
Part 3 — What this sets you up for¶
Everything you touched returns in the React pass — you'll recognize it instead of meeting it cold:
| What you did here | Where it returns |
|---|---|
interface IProduct, typed arrays |
Lessons 2–3 (typed data, the I{Entity} interface per feature folder) |
.map() / .filter() on the response |
Lesson 3 (rendering a list = transforming an array into UI) |
fetch + async/await + .json() |
Lesson 4 (loading real data into a page) |
| Hitting and fixing the CORS error | Lesson 4 (the live CORS moment) |
The checkStatus / parseJSON helper |
Lesson 12 (centralized fetch + error handling) |
| POST with a JSON body | Lesson 7 (create/edit forms) |
You do not need to start React itself, install React, or write any JSX — stop at the edge of the language and the browser APIs. That boundary is exactly where Lesson 3 picks up.
Guardrails¶
- Optional and ungraded — do as much or as little as you like; stop any time.
- No tokens / no auth headers. The PRS endpoints are open (no JWT in this course), so
fetchneeds noAuthorizationheader. Don't add one. - Keep CORS wide open — enable it, don't restrict it. It's a deliberate dev simplification.
- Run the API over
http, and use your API's port inBASE_URL.