PeppaPigGame: Web APIs Lesson (with Snippets)

Big Goal

Build a game feature powered by a live API, following this flow:

  1. Request data with Fetch API
  2. Parse JSON safely
  3. Use the result in gameplay (here: API music in PeppaPigGame)
Game Status: Not Started

Foundation: What are Web APIs?

What is an API?

An API (Application Programming Interface) is a set of rules that let one software system ask another system for data or perform actions. Think of it like a restaurant menu:

  • You (the client) look at the menu (the API’s documentation)
  • You order something (make an API request)
  • The kitchen processes your order (the server handles the request)
  • You get your food back (the server sends back a response)

Web APIs specifically live on the internet. Your JavaScript code in the browser can request data from a web server somewhere else in the world.

Real-world example: iTunes Search API

In this lesson, we use iTunes Search API:

  • URL: https://itunes.apple.com/search
  • Input: A search term (e.g., “peppa pig theme”)
  • Output: JSON data with music tracks, artists, preview URLs, artwork, etc.

How the Internet Works (Simple Version)

The Request-Response Cycle

  1. You send a request: Browser -> Server (over HTTP)
  2. Server processes it: Finds matching data
  3. Server sends a response: Server -> Browser (over HTTP)
  4. You use the data: JavaScript code processes the response

HTTP Status Codes

When a server responds, it includes a status code that tells you what happened:

  • 200 = OK, request succeeded
  • 404 = Not found, server can’t find that resource
  • 500 = Server error, something went wrong
  • 403 = Forbidden, you don’t have permission

Why this matters in code: Always check the status before using the response data.


Part 1: The Fetch API (How to make requests)

What is the Fetch API?

fetch() is a built-in JavaScript function that makes HTTP requests. It’s the modern, preferred way to get data from APIs.

Basic syntax:

What fetch does:

  • Sends an HTTP request to the given URL
  • Returns a Promise (a value that will be ready later)
  • Lets you handle success (.then()) or failure (.catch() )
%%js

fetch(url)
  .then(response => { /* handle response */ })
  .catch(error => { /* handle error */ });

Building the Request URL

APIs need parameters to know what to search for. URLs can have query parameters (the part after ?):

Why encodeURIComponent()? Spaces and special characters break URLs. This function converts "peppa pig theme" into "peppa%20pig%20theme" (safe for URLs).

%%js

const filter = "peppa pig theme";
const url = "https://itunes.apple.com/search?term=" + encodeURIComponent(filter);
console.log(url);

Part 2: Understanding the Response

What does fetch() return?

When the server responds, fetch() gives you a Response object with:

  • .status = the HTTP status code
  • .json() = a method that converts the response body to JavaScript data
  • .text() = a method that gets the response as plain text

Validating the Status

Always check if the request succeeded before using the data:

Breaking this down:

  1. fetch() makes the request
  2. First .then() checks if status is 200
  3. If not, throw an error (stops execution)
  4. If yes, .json() parses the response body
  5. Second .then() receives the parsed data
  6. .catch() handles any errors
%%js

const url = "https://itunes.apple.com/search?term=" + encodeURIComponent("peppa pig theme");
fetch(url, { method: 'GET', mode: 'cors' })
  .then(response => {
    if (response.status !== 200) {
      throw new Error('Database response error: ' + response.status);
    }
    return response.json();
  })
  .then(data => {
    console.log('Track count:', data.results.length);
  })
  .catch(err => console.error(err));

Part 3: Understanding JSON

What is JSON?

JSON (JavaScript Object Notation) is a text format for storing data. It looks like JavaScript objects but is just plain text.

Example JSON response from iTunes API:

{
  "results": [
    {
      "artistName": "The Wiggles",
      "trackName": "Peppa Pig Theme",
      "artworkUrl100": "https://example.com/image.jpg",
      "previewUrl": "https://example.com/preview.mp4"
    },
    {
      "artistName": "Kids TV",
      "trackName": "Peppa Pig Song",
      "artworkUrl100": "https://example.com/image2.jpg",
      "previewUrl": "https://example.com/preview2.mp4"
    }
  ]
}

Extracting Data from JSON

Once you have JSON data, you access it like a normal JavaScript object:

Key takeaway: JSON is just structured data. Once parsed, you use it like any JavaScript object.

%%js

const data = {
  results: [
    { artistName: 'The Wiggles', trackName: 'Peppa Pig Theme', artworkUrl100: 'https://example.com/image.jpg', previewUrl: 'https://example.com/preview.mp4' }
  ]
};

for (const row of data.results) {
  const artist = row.artistName;
  const track = row.trackName;
  const imageUrl = row.artworkUrl100;
  const previewUrl = row.previewUrl;
  console.log(artist, track, imageUrl, previewUrl);
}

Part 4: The Audio API (Playing Sound)

What is the Audio API?

The Audio API is a built-in browser interface for creating and controlling audio playback. It’s simple but powerful.

Creating an audio element:

Why async/await? Playing audio is asynchronous (it takes time). await pauses execution until the promise resolves.

%%js

const previewUrl = 'https://example.com/song.mp4';
const audio = new Audio(previewUrl);
audio.volume = 0.35;
audio.loop = true;
audio.play().catch(err => console.warn('Autoplay blocked until user gesture:', err));

Part 5: Putting It Together (Game Integration)

Architecture: Separating Concerns

Good code separates API logic from game logic. Create a controller class:

Benefits:

  • Reusable: Call startMusic() from anywhere
  • Testable: Each method does one thing
  • Maintainable: Changes to API logic are isolated
%%js

class PeppaMusicApiController {
  constructor(endpoint) {
    this.endpoint = endpoint;
  }

  async fetchPreviewUrl() {
    const response = await fetch(this.endpoint);
    if (!response.ok) throw new Error('API request failed (' + response.status + ')');

    const data = await response.json();
    const tracks = (data && Array.isArray(data.results)) ? data.results : [];
    const track = tracks.find(item => item && item.previewUrl);
    if (!track) throw new Error('No playable preview URL found');

    return track.previewUrl;
  }

  async startMusic() {
    const previewUrl = await this.fetchPreviewUrl();
    const audio = new Audio(previewUrl);
    audio.volume = 0.35;
    audio.loop = true;
    await audio.play();
  }
}

console.log('PeppaMusicApiController loaded');

Part 6: Error Handling Best Practices

Why Error Handling Matters

Networks fail. Servers go down. APIs change. Always handle errors gracefully:

Key patterns:

  • try block: Code that might fail
  • catch block: Handle the error gracefully
  • Always log errors for debugging
  • Provide fallback behavior (game continues, shows message, etc.)

This is the same architecture your final game runner uses.

Transition to End Product

Run the final game cell at the top. It is the completed version of this lesson pattern integrated into PeppaPigGame.

%%js

async function startMusicSafely(controller) {
  try {
    const previewUrl = await controller.fetchPreviewUrl();
    const audio = new Audio(previewUrl);
    audio.volume = 0.35;
    audio.loop = true;
    await audio.play();
    console.log('Music started successfully');
  } catch (error) {
    console.warn('Failed to play music:', error);
  }
}

console.log('startMusicSafely helper loaded');