Skip to content

About

Session 18: Connecting Frontend to API ๐Ÿ”Œ

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

ย 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ”Œ Connecting Frontend to API

๐Ÿ“ธ Lesson Snapshot

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


๐ŸŽฏ Age-Tiered Learning Goals

๐Ÿง’ 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

๐ŸŽจ Before You Code: Hands-On Activities (15 minutes)

๐Ÿ“ž Activity 1: The Telephone Game - Understanding Async (Whole Class)

๐ŸŽฌ 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!"


๐Ÿšง Activity 2: The Security Guard Game - Understanding CORS (Groups)

๐ŸŽฌ 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!"


๐ŸŽญ Activity 3: Promise Me! - The async/await Dance (Whole Class)

๐Ÿ”„ 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!"


๐Ÿ—บ๏ธ Visual: The Fetch Flow

    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)

๐Ÿ—บ๏ธ Visual: Async/Await Flow

    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! โœจ

๐Ÿ—บ๏ธ Visual: CRUD Operations

    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

๐Ÿ“ Folder & File Map

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!

โœ… Prerequisites

  • โœ… 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)

๐Ÿ› ๏ธ Step-by-Step Build Guide

1๏ธโƒฃ Fork and Clone


2๏ธโƒฃ Enable CORS in Your C# API

๐Ÿ”“ 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!            ๐Ÿ“ฆ

3๏ธโƒฃ Create the HTML Structure

๐Ÿ“„ 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>

4๏ธโƒฃ Create the CSS Styles

๐Ÿ“„ Create file: styles.css

See the styles.css file in this folder for the complete styling with loading states!


5๏ธโƒฃ Create the JavaScript

๐Ÿ“„ Create file: app.js

See the app.js file in this folder for the complete JavaScript with fetch calls!


6๏ธโƒฃ Understand the API Configuration

โš™๏ธ 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)

7๏ธโƒฃ Commit CORS Changes

cd LibraryApi
git add .
git commit -m "Add CORS configuration for frontend"

8๏ธโƒฃ Write Your First Fetch - GET All Books

// ๐Ÿ“š 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
    }

9๏ธโƒฃ Write POST - Create a Book

// โž• 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
        })
    })

๐Ÿ”Ÿ Handle Loading States

// ๐Ÿ”„ 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!     โ”‚          โ”‚
    โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ”‚
    โ”‚                                                         โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

1๏ธโƒฃ1๏ธโƒฃ Display Books on the Page

// ๐ŸŽจ 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;
}

1๏ธโƒฃ2๏ธโƒฃ Implement Delete

// ๐Ÿ—‘๏ธ 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);
    }
}

1๏ธโƒฃ3๏ธโƒฃ Implement Update (PUT)

// โœ๏ธ 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);
    }
}

1๏ธโƒฃ4๏ธโƒฃ Implement Checkout and Return

// ๐Ÿ“ค 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);
    }
}

1๏ธโƒฃ5๏ธโƒฃ Add Search Functionality

// ๐Ÿ” 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';
            }
        });
    });
}

1๏ธโƒฃ6๏ธโƒฃ Start Your API and Frontend

Terminal 1 - Run the C# API:

cd LibraryApi
dotnet run

Terminal 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]  โ”‚          โ”‚
    โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ”‚
    โ”‚                                                          โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

1๏ธโƒฃ7๏ธโƒฃ Commit Your Frontend

git add .
git commit -m "Complete frontend with CRUD operations"
git push origin main

๐Ÿ“Š Visual: Complete Architecture

    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                                   โ”‚     โ”‚
    โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚
    โ”‚                                                                 โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ’ฌ Group Reflection (5 minutes)

๐Ÿ”„ Circle Share - Answer one:

  1. "fetch() is different from regular function calls because..."
  2. "async/await helps us by..."
  3. "CORS exists because..."
  4. "The loading state is important because..."
  5. "Connecting frontend to backend feels like..."

โœ… Run & Test Checklist

  • ๐Ÿ”Œ 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

๐Ÿš€ Bonus Challenges

โญ Challenge 1: Better Loading (All Levels)

Add a loading spinner to each button when it's clicked (not just the page).

โญโญ Challenge 2: Form Validation (Middle+)

Show validation errors inline in the form (like "Title is required") before sending to API.

โญโญโญ Challenge 3: Optimistic Updates (High School)

Update the UI immediately when deleting, then revert if the API call fails.

โญโญโญ Challenge 4: Real-time Updates (High School)

Use polling or WebSockets to show changes made by other users.


๐Ÿ“– Vocabulary Wall

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

๐Ÿ  Take-Home Connection

๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง 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?

๐Ÿ”ฎ What's Next

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! ๐ŸŽ‰

About

Session 18: Connecting Frontend to API ๐Ÿ”Œ

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages