Build a web frontend that talks to your C# Library API using JavaScript's fetch API! (Ages 8-18)
Session Date: Saturday, April 4, 2026 at 11:00 AM EST
| ๐ง Elementary (8-11) | ๐ง Middle School (12-14) | ๐ High School (15-18) | |
|---|---|---|---|
| ๐ Fetch API | Understand that JavaScript can talk to servers | Use fetch() to GET and POST data | Handle all CRUD operations with proper error handling |
| โณ Async/Await | See that some code "waits" for servers | Use async/await pattern correctly | Manage loading states and race conditions |
| ๐ก๏ธ CORS | Know servers control who can talk to them | Enable CORS on their API | Understand security implications |
| ๐ By End | See data from API appear on webpage | Build working search and display | Complete CRUD frontend with validation |
๐ฌ Setup: Three students form a chain: "Website", "Internet", "Server"
๐ Scenario: Synchronous vs Asynchronous
SYNCHRONOUS (Blocking) - THE BORING WAY ๐ด
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Website: "I need book data..." ๐ง [freezes completely]
Internet: [carrying message...] โณ
Server: "Here's the data!" ๐ฆ
Internet: [carrying response...] โณ
Website: "...NOW I can continue!" ๐
โ Problem: Website is FROZEN while waiting!
Users see: ๐ฅถ Page won't scroll, buttons don't work
ASYNCHRONOUS (Non-blocking) - THE SMART WAY ๐
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Website: "I need book data..." ๐ค "I'll keep working!"
[shows loading spinner] ๐
[user can scroll] ๐
[user can click things] ๐ฑ๏ธ
Internet: [carrying message...] โณ
Server: "Here's the data!" ๐ฆ
Internet: [carrying response...] โณ
Website: "Data arrived! Updating!" โ
โ
Website stayed responsive the whole time!
๐ฌ Discussion:
"Async is like ordering pizza. You don't stand at the door waiting - you do other things and answer when it arrives!"
๐ฌ Setup: One student is the "API Server", others are different "Websites"
CORS = Cross-Origin Resource Sharing ๐
It's like a VIP list at a party! ๐
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ ๐ฅ๏ธ API SERVER (The Party) โ
โ โ
โ VIP LIST (AllowedOrigins): โ
โ โ
localhost:5500 "You're on the list, come in!" โ
โ โ
mywebsite.com "You're on the list, come in!" โ
โ โ hacker.com "You're NOT on the list! ๐ซ" โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Student 1 (localhost): "I'm localhost:5500!"
Server: "โ
Welcome! Here's your data!"
Student 2 (hacker.com): "I'm hacker.com!"
Server: "โ CORS Error! You're not allowed!"
๐ฌ Discussion:
"CORS protects APIs from being called by random websites. Your API decides who's allowed to talk to it!"
๐ Learn the pattern with body movements:
THE ASYNC/AWAIT DANCE ๐๐บ
Step 1: async ๐ฌ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[Raise hand] "I'm an async function!"
"I might do things that take time..."
Step 2: await โณ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[Freeze in place] "I'm waiting for a Promise..."
[Other dancers keep moving] "Others can still work!"
Step 3: Data arrives! ๐ฆ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[Unfreeze] "I got my data!"
[Continue dancing] "Now I can continue!"
Step 4: try/catch ๐ก๏ธ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[Shield motion] "If something breaks..."
[Catch motion] "...I'll catch the error!"
๐ก Key Insight:
"async/await makes code that READS like normal code but WORKS like magic waiting code!"
HOW FETCH() WORKS ๐
YOUR WEBPAGE YOUR C# API
(JavaScript) (localhost:5000)
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ โ โ โ
โ ๐ฅ๏ธ Browser โ โ ๐ฅ๏ธ Server โ
โ โ โ โ
โ โโโโโโโโโโ โ โ โโโโโโโโโโ โ
โ โ HTML โ โ โ โ Books โ โ
โ โ CSS โ โ FETCH() ๐ค โ โ Data โ โ
โ โ JS โโโโผโโโโโโโโโโโโโโโถโ โ โ โ
โ โโโโโโโโโโ โ โ โโโโโโโโโโ โ
โ โ โ โ
โ โโโโโโโโโโ โ JSON ๐ฅ โ โ
โ โ Books โโโโผโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโ
โ โ List โ โ โ โ
โ โโโโโโโโโโ โ โ โ
โ โ โ โ
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
THE JOURNEY OF A FETCH REQUEST ๐
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
1๏ธโฃ JavaScript calls fetch()
โ
โผ
2๏ธโฃ Browser creates HTTP request
โ GET http://localhost:5000/api/books
โผ
3๏ธโฃ Request travels to your C# API
โ
โผ
4๏ธโฃ C# Controller handles request
โ BooksController.GetAllBooks()
โผ
5๏ธโฃ Server sends JSON response
โ [{"id": 1, "title": "Harry Potter"...}]
โผ
6๏ธโฃ JavaScript receives the data
โ response.json()
โผ
7๏ธโฃ You update the webpage! ๐
displayBooks(books)
ASYNC/AWAIT TIMELINE โฑ๏ธ
WITHOUT async/await (callback hell ๐ฑ):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
fetch(url)
.then(response => response.json())
.then(data => {
processData(data)
.then(result => {
saveResult(result)
.then(saved => {
// ๐ต So many levels deep!
})
})
})
WITH async/await (clean and clear ๐):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
async function doEverything() {
const response = await fetch(url); // โณ Wait
const data = await response.json(); // โณ Wait
const result = await processData(data); // โณ Wait
const saved = await saveResult(result); // โณ Wait
// ๐ So clean and readable!
}
WHAT HAPPENS DURING await:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Timeline โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโถ
Main Code: [RUN]โโ[await]โโโโโโโโโโโโโโโโโ[CONTINUE]
โ โฒ
fetch(): โโโ[REQUEST]โโ[WAIT]โโ[DONE]โ
โ โ
Server: โโ[PROCESS]โ
Browser: [RESPONSIVE]โโ[RESPONSIVE]โโ[RESPONSIVE]
User can User can Page
scroll! ๐ click! ๐ฑ๏ธ updates! โจ
FULL CRUD WITH FETCH ๐
๐ READ (GET) - Get all books
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
fetch('http://localhost:5000/api/books')
JavaScript โโGETโโโถ C# API โโโถ Returns: [{...}, {...}]
โ CREATE (POST) - Add a new book
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
fetch('http://localhost:5000/api/books', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'New Book', author: 'Me' })
})
JavaScript โโPOST + {data}โโโถ C# API โโโถ Returns: {id: 4, ...}
โ๏ธ UPDATE (PUT) - Change a book
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
fetch('http://localhost:5000/api/books/1', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Updated Title' })
})
JavaScript โโPUT + {data}โโโถ C# API โโโถ Returns: {updated book}
๐๏ธ DELETE - Remove a book
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
fetch('http://localhost:5000/api/books/1', {
method: 'DELETE'
})
JavaScript โโDELETEโโโถ C# API โโโถ Returns: 204 No Content
Session18-Frontend/
โโโ ๐ index.html โ ๐ Main HTML page
โโโ ๐ styles.css โ ๐จ Styling with loading states
โโโ ๐ app.js โ ๐ง JavaScript with fetch calls
โโโ ๐ LibraryApi/ โ ๐ฅ๏ธ Your C# API (from previous sessions)
โโโ Controllers/
โ โโโ BooksController.cs
โโโ Models/
โโโ Program.cs โ โ๏ธ CORS configuration here!
- โ Completed Session 15 (Testing APIs)
- โ Sessions 16-17 (HTML/CSS/JavaScript basics)
- โ Working Library API from previous sessions
- โ VS Code with Live Server extension (or similar)
- Go to https://github.com/PhillyCoderDojo/Session18-Frontend โ Fork โ Clone
git clone https://github.com/YOUR-USERNAME/Session18-Frontend.git
๐ CORS lets your frontend talk to your API!
โ๏ธ Update Program.cs in your LibraryApi project:
var builder = WebApplication.CreateBuilder(args);
// Add services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// ๐ ADD CORS POLICY
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowFrontend", policy =>
{
policy.WithOrigins(
"http://localhost:5500", // Live Server default
"http://127.0.0.1:5500", // Also Live Server
"http://localhost:3000" // Common dev server
)
.AllowAnyHeader()
.AllowAnyMethod();
});
});
var app = builder.Build();
// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// ๐ USE CORS - This must come before UseAuthorization!
app.UseCors("AllowFrontend");
app.UseAuthorization();
app.MapControllers();
app.Run();๐ Visual: What this does:
BEFORE CORS: โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Your Frontend Your API
(localhost:5500) (localhost:5000)
๐ค โโโโโโโโโโโโโโโโโโโโโโโโโถ ๐ซ
"I want data!" "Who are you?! BLOCKED!"
AFTER CORS: โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Your Frontend Your API
(localhost:5500) (localhost:5000)
๐ค โโโโโโโโโโโโโโโโโโโโโโโโโถ โ
"I want data!" "You're on the list!"
๐ฅ โโโโโโโโโโโโโโโโโโโโโโโโโ
Here's your data! ๐ฆ
๐ Create file: index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>๐ Library Manager</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<!-- ๐ Header -->
<header>
<h1>๐ Library Manager</h1>
<p>Connected to C# API</p>
</header>
<!-- โ Add Book Form -->
<section class="add-book-form">
<h2>โ Add New Book</h2>
<form id="addBookForm">
<div class="form-group">
<label for="title">๐ Title:</label>
<input type="text" id="title" required minlength="1" maxlength="200">
</div>
<div class="form-group">
<label for="author">โ๏ธ Author:</label>
<input type="text" id="author" required minlength="1" maxlength="100">
</div>
<div class="form-group">
<label for="pageCount">๐ Pages:</label>
<input type="number" id="pageCount" required min="1" max="10000">
</div>
<button type="submit" id="addButton">
<span class="button-text">Add Book</span>
<span class="spinner hidden">โณ</span>
</button>
</form>
</section>
<!-- ๐ Search Section -->
<section class="search-section">
<h2>๐ Search Books</h2>
<input type="text" id="searchInput" placeholder="Search by title or author...">
</section>
<!-- ๐ Books Display -->
<section class="books-section">
<h2>๐ All Books</h2>
<!-- Loading State -->
<div id="loading" class="loading">
<div class="spinner-large">๐</div>
<p>Loading books...</p>
</div>
<!-- Error State -->
<div id="error" class="error hidden">
<p>โ <span id="errorMessage"></span></p>
<button onclick="loadBooks()">๐ Try Again</button>
</div>
<!-- Books List -->
<div id="booksList" class="books-grid hidden"></div>
<!-- Empty State -->
<div id="emptyState" class="empty-state hidden">
<p>๐ญ No books found. Add one above!</p>
</div>
</section>
</div>
<!-- ๐ Edit Modal -->
<div id="editModal" class="modal hidden">
<div class="modal-content">
<h2>โ๏ธ Edit Book</h2>
<form id="editBookForm">
<input type="hidden" id="editBookId">
<div class="form-group">
<label for="editTitle">๐ Title:</label>
<input type="text" id="editTitle" required>
</div>
<div class="form-group">
<label for="editAuthor">โ๏ธ Author:</label>
<input type="text" id="editAuthor" required>
</div>
<div class="form-group">
<label for="editPageCount">๐ Pages:</label>
<input type="number" id="editPageCount" required min="1">
</div>
<div class="modal-buttons">
<button type="submit">๐พ Save</button>
<button type="button" onclick="closeModal()">โ Cancel</button>
</div>
</form>
</div>
</div>
<script src="app.js"></script>
</body>
</html>๐ Create file: styles.css
See the styles.css file in this folder for the complete styling with loading states!
๐ Create file: app.js
See the app.js file in this folder for the complete JavaScript with fetch calls!
โ๏ธ In app.js, set your API URL:
// ๐ง CONFIGURATION
// Change this to match where your C# API is running!
const API_BASE_URL = 'http://localhost:5000/api';๐ How to find your API URL:
When you run your C# API, look for:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ info: Microsoft.Hosting.Lifetime[14] โ
โ Now listening on: http://localhost:5000 โ
โ Now listening on: https://localhost:5001 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โฒ
โ
Use this URL! (with http, not https)
cd LibraryApi
git add .
git commit -m "Add CORS configuration for frontend"// ๐ GET all books from the API
async function loadBooks() {
showLoading(); // Show spinner ๐
try {
// ๐ค Send GET request to API
const response = await fetch(`${API_BASE_URL}/books`);
// โ
Check if request was successful
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
// ๐ฅ Parse JSON response
const books = await response.json();
// ๐จ Display the books
displayBooks(books);
} catch (error) {
// โ Handle errors
showError(error.message);
}
}๐ Understanding the code:
async function loadBooks() { โ ๐ฌ Mark as async
โผ
await fetch(url) โ โณ Wait for response
โผ
await response.json() โ โณ Wait for JSON parsing
โผ
displayBooks(books) โ ๐จ Update the page
THE TRY/CATCH SAFETY NET:
try { โ ๐ก๏ธ "Try this code..."
// risky code here
} catch (error) { โ ๐ฅ
"If it fails, catch here!"
// handle the error
}
// โ CREATE a new book
async function createBook(bookData) {
try {
const response = await fetch(`${API_BASE_URL}/books`, {
method: 'POST', // ๐ค POST method
headers: {
'Content-Type': 'application/json' // ๐ Tell API we're sending JSON
},
body: JSON.stringify(bookData) // ๐ฆ Convert object to JSON string
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.title || 'Failed to create book');
}
const newBook = await response.json();
return newBook;
} catch (error) {
console.error('Create failed:', error);
throw error;
}
}๐ Visual: POST Request Structure
fetch(url, {
method: 'POST', โ ๐ท๏ธ What action (POST = create)
headers: {
'Content-Type': โ ๐ Metadata about the request
'application/json' "I'm sending JSON data"
},
body: JSON.stringify({ โ ๐ฆ The actual data
title: 'My Book',
author: 'Me',
pageCount: 100
})
})
// ๐ Show loading spinner
function showLoading() {
document.getElementById('loading').classList.remove('hidden');
document.getElementById('booksList').classList.add('hidden');
document.getElementById('error').classList.add('hidden');
document.getElementById('emptyState').classList.add('hidden');
}
// โ
Show books (hide loading)
function showBooks() {
document.getElementById('loading').classList.add('hidden');
document.getElementById('booksList').classList.remove('hidden');
}
// โ Show error
function showError(message) {
document.getElementById('loading').classList.add('hidden');
document.getElementById('error').classList.remove('hidden');
document.getElementById('errorMessage').textContent = message;
}๐ Visual: UI States
THE FOUR UI STATES ๐ญ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ 1๏ธโฃ LOADING 2๏ธโฃ SUCCESS โ
โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ
โ โ ๐ โ โ ๐ Harry Potter โ โ
โ โ Loading... โ โ ๐ Hunger Games โ โ
โ โโโโโโโโโโโโโโโโโโโ โ ๐ Percy Jacksonโ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ
โ 3๏ธโฃ ERROR 4๏ธโฃ EMPTY โ
โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ
โ โ โ Failed to โ โ โ โ
โ โ connect! โ โ ๐ญ No books yet โ โ
โ โ [Try Again] โ โ Add one! โ โ
โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ๐จ Display books in the grid
function displayBooks(books) {
const booksList = document.getElementById('booksList');
if (books.length === 0) {
showEmptyState();
return;
}
// ๐งน Clear existing books
booksList.innerHTML = '';
// ๐ Create a card for each book
books.forEach(book => {
const bookCard = createBookCard(book);
booksList.appendChild(bookCard);
});
showBooks();
}
// ๐ Create a single book card
function createBookCard(book) {
const card = document.createElement('div');
card.className = `book-card ${book.isAvailable ? 'available' : 'checked-out'}`;
card.innerHTML = `
<h3>${book.title}</h3>
<p class="author">โ๏ธ ${book.author}</p>
<p class="pages">๐ ${book.pageCount} pages</p>
<p class="status">${book.isAvailable ? 'โ
Available' : '๐ค Checked Out'}</p>
<div class="book-actions">
<button onclick="editBook(${book.id})">โ๏ธ Edit</button>
<button onclick="deleteBook(${book.id})" class="delete-btn">๐๏ธ Delete</button>
${book.isAvailable
? `<button onclick="checkoutBook(${book.id})">๐ค Checkout</button>`
: `<button onclick="returnBook(${book.id})">๐ฅ Return</button>`
}
</div>
`;
return card;
}// ๐๏ธ DELETE a book
async function deleteBook(id) {
if (!confirm('Are you sure you want to delete this book?')) {
return; // User cancelled
}
try {
const response = await fetch(`${API_BASE_URL}/books/${id}`, {
method: 'DELETE'
});
if (!response.ok) {
throw new Error('Failed to delete book');
}
// ๐ Refresh the books list
loadBooks();
} catch (error) {
alert('โ ' + error.message);
}
}// โ๏ธ UPDATE a book
async function updateBook(id, bookData) {
try {
const response = await fetch(`${API_BASE_URL}/books/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(bookData)
});
if (!response.ok) {
throw new Error('Failed to update book');
}
closeModal();
loadBooks(); // Refresh the list
} catch (error) {
alert('โ ' + error.message);
}
}// ๐ค Checkout a book
async function checkoutBook(id) {
try {
const response = await fetch(`${API_BASE_URL}/books/${id}/checkout`, {
method: 'POST'
});
if (!response.ok) {
throw new Error('Failed to checkout book');
}
loadBooks(); // Refresh to show new status
} catch (error) {
alert('โ ' + error.message);
}
}
// ๐ฅ Return a book
async function returnBook(id) {
try {
const response = await fetch(`${API_BASE_URL}/books/${id}/return`, {
method: 'POST'
});
if (!response.ok) {
throw new Error('Failed to return book');
}
loadBooks();
} catch (error) {
alert('โ ' + error.message);
}
}// ๐ Search books (client-side filtering)
function setupSearch() {
const searchInput = document.getElementById('searchInput');
searchInput.addEventListener('input', (e) => {
const searchTerm = e.target.value.toLowerCase();
const bookCards = document.querySelectorAll('.book-card');
bookCards.forEach(card => {
const title = card.querySelector('h3').textContent.toLowerCase();
const author = card.querySelector('.author').textContent.toLowerCase();
if (title.includes(searchTerm) || author.includes(searchTerm)) {
card.style.display = 'block';
} else {
card.style.display = 'none';
}
});
});
}Terminal 1 - Run the C# API:
cd LibraryApi
dotnet runTerminal 2 - Run the Frontend (using Live Server in VS Code):
Right-click index.html โ "Open with Live Server"
๐ What you'll see:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๐ Library Manager โ
โ Connected to C# API โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โ Add New Book โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Title: [________________________] โ โ
โ โ Author: [________________________] โ โ
โ โ Pages: [____] โ โ
โ โ [Add Book] โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ ๐ Search: [________________________] โ
โ โ
โ ๐ All Books โ
โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ
โ โHarry Potterโ โThe Hobbit โ โ 1984 โ โ
โ โ JK Rowling โ โ Tolkien โ โ Orwell โ โ
โ โ 309 pages โ โ 310 pages โ โ 328 pages โ โ
โ โ โ
Available โ โ ๐คChecked โ โ โ
Available โ โ
โ โ[Edit][Del] โ โ[Edit][Del] โ โ[Edit][Del] โ โ
โ โ[Checkout] โ โ[Return] โ โ[Checkout] โ โ
โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
git add .
git commit -m "Complete frontend with CRUD operations"
git push origin main THE FULL PICTURE ๐ผ๏ธ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ YOUR BROWSER โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ index.html โ โ
โ โ (The structure - what things ARE) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ styles.css โ โ
โ โ (The looks - how things APPEAR) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ app.js โ โ
โ โ (The brain - how things WORK) โ โ
โ โ โ โ
โ โ fetch() โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโ โ
โ โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโ
โ
๐ INTERNET โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโ
โ YOUR C# API โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโค
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Program.cs โ โ
โ โ (CORS configuration) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ BooksController.cs โ โ
โ โ (Handles GET, POST, PUT, DELETE) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Models/ โ โ
โ โ Book, DTOs, Mapper โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Data (memory) โ โ
โ โ List<Book> _books โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ Circle Share - Answer one:
- "fetch() is different from regular function calls because..."
- "async/await helps us by..."
- "CORS exists because..."
- "The loading state is important because..."
- "Connecting frontend to backend feels like..."
- ๐ CORS is enabled in C# API
- ๐ Books load automatically on page load
- โ Can add a new book via form
- ๐ Search filters books in real-time
- โ๏ธ Can edit existing books
- ๐๏ธ Can delete books (with confirmation)
- ๐ค Can checkout available books
- ๐ฅ Can return checked-out books
- ๐ Loading spinner shows during fetch
- โ Error messages display when API is down
- ๐ญ Empty state shows when no books exist
Add a loading spinner to each button when it's clicked (not just the page).
Show validation errors inline in the form (like "Title is required") before sending to API.
Update the UI immediately when deleting, then revert if the API call fails.
Use polling or WebSockets to show changes made by other users.
| Term | ๐ Kid-Friendly Definition | ๐ป Example |
|---|---|---|
| ๐ fetch() | JavaScript function that talks to servers | fetch('http://api.com/books') |
| โณ async | Marks a function that might wait for things | async function getBooks() |
| โธ๏ธ await | Pauses until a promise resolves | await fetch(url) |
| ๐ค Promise | An "IOU" for future data | fetch() returns a Promise |
| ๐ CORS | Security rule about who can talk to an API | Cross-Origin Resource Sharing |
| ๐ฆ JSON.stringify() | Converts JavaScript object to JSON text | JSON.stringify({name: 'Book'}) |
| ๐ฅ response.json() | Parses JSON response into JavaScript object | await response.json() |
| ๐ Loading State | What users see while waiting | Spinner, skeleton, message |
๐จโ๐ฉโ๐ง Talk to a family member:
"Today I connected a website to my own server! The website uses JavaScript to send requests to my C# API, and the API sends back data as JSON. It's like my website is making phone calls to my server!"
๐ Real-world examples:
- ๐ Amazon's website fetching product data
- ๐ฑ Instagram loading your feed
- ๐ฎ Fortnite checking your inventory
- ๐ต Spotify loading your playlists
๐ค Questions to explore:
- What happens when you open a website with no internet?
- Why do some pages load faster than others?
- What would happen if websites didn't show loading spinners?
In Session 19, we'll learn Python basics and compare it to C#! ๐
YOUR JOURNEY SO FAR ๐บ๏ธ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ ๐ Sessions 1-10: C# Fundamentals & OOP โ
โ ๐ Sessions 11-15: Building Web APIs โ
โ ๐จ Sessions 16-18: Frontend & Connecting Things โโโ YOU ARE HERE!
โ ๐ Sessions 19-20: Python & Flask (coming up!) โ
โ ๐ญ Session 21: Final Showcase! โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
You're building real full-stack applications! ๐