Web Development /JavaScript Programming / Lesson
Web Development

Fetch API, JSON and Working with External Data

Learn how to use the JavaScript Fetch API to request external data, work with JSON responses, handle HTTP errors, and build practical data-driven web applications.

🎯

Learning Objectives

["Understand what the Fetch API is and why it is used in modern JavaScript applications.","Understand the basic structure of HTTP requests and responses.","Use fetch() to request data from an external API.","Understand the Promise returned by fetch().","Convert HTTP responses into JavaScript objects using response.json().","Work with JSON data and understand the difference between JSON and JavaScript objects.","Handle successful and unsuccessful HTTP responses.","Use async\/await with fetch() for readable asynchronous code.","Send GET and POST requests using the Fetch API.","Send JSON data to a server using appropriate HTTP headers.","Handle network errors and API errors with try\/catch.","Build practical interfaces that display data received from an API.","Understand basic API security and why sensitive credentials should not be exposed in frontend JavaScript."]

📖

Lesson Content

Introduction

Modern websites are no longer limited to displaying information that is stored directly inside an HTML document. Many applications communicate with servers and external services to retrieve information such as student records, weather information, news, products, statistics, user profiles and other dynamic data.

JavaScript provides a modern browser API called the Fetch API that makes it possible to communicate with web servers and APIs directly from JavaScript.

In this lesson, you will learn how to use fetch(), process JSON data, handle HTTP responses and errors, and build practical data-driven interfaces.

1. What Is the Fetch API?

The Fetch API is a browser feature that allows JavaScript to make HTTP requests and communicate with servers.

For example, JavaScript can request information from an API:

fetch('https://example.com/api/students');

The server receives the request and returns a response. JavaScript can then process that response and use the returned information in the webpage.

The Fetch API can be used for:

  • Retrieving data from an API.
  • Sending form information.
  • Submitting JSON data.
  • Updating records.
  • Deleting records.
  • Building dashboards.
  • Loading products dynamically.
  • Displaying student information.
  • Communicating with WordPress REST APIs.

2. Understanding HTTP Requests

Before using fetch(), it is important to understand the basic idea behind HTTP communication.

A browser or JavaScript application can send a request to a server. The server processes the request and returns a response.

Common HTTP methods include:

  • GET – retrieve information.
  • POST – create or submit information.
  • PUT – replace or update information.
  • PATCH – partially update information.
  • DELETE – remove information.

For example, a student dashboard may use GET to retrieve course information and POST to submit an assignment.

3. Your First fetch() Request

The simplest use of fetch() looks like this:

fetch('https://example.com/api/data')
    .then(response => response.json())
    .then(data => {
        console.log(data);
    });

The first argument passed to fetch() is the URL of the resource we want to request.

The fetch() function returns a Promise because the response does not normally arrive immediately.

4. Fetch and Promises

The Fetch API is asynchronous. This means JavaScript can start a network request while allowing other browser operations to continue.

The returned Promise eventually produces a Response object.

fetch('/api/students')
    .then(response => {
        console.log(response);
    });

The Response object contains information about the server response, including the HTTP status and methods for reading the response body.

5. Reading JSON Responses

APIs commonly return data in JSON format.

JSON stands for JavaScript Object Notation. It is a text-based data format commonly used for exchanging structured information between applications.

For example, an API might return:

{
    "name": "John",
    "course": "Web Development",
    "level": "Intermediate"
}

To convert a JSON response body into a JavaScript object, use:

response.json()

Example:

fetch('/api/student')
    .then(response => response.json())
    .then(student => {
        console.log(student.name);
        console.log(student.course);
    });

6. JSON and JavaScript Objects

JSON looks similar to JavaScript objects, but they are not exactly the same thing.

A JavaScript object exists as a data structure inside a JavaScript program:

const student = {
    name: 'John',
    course: 'Web Development'
};

JSON is a text representation of structured data:

{
    "name": "John",
    "course": "Web Development"
}

JavaScript provides JSON.parse() and JSON.stringify() for converting between JSON text and JavaScript data.

7. JSON.parse()

JSON.parse() converts a valid JSON string into a JavaScript value.

const jsonText = '{"name":"John","course":"Web Development"}';

const student = JSON.parse(jsonText);

console.log(student.name);

8. JSON.stringify()

JSON.stringify() converts a JavaScript value into a JSON string.

const student = {
    name: 'John',
    course: 'Web Development'
};

const jsonText = JSON.stringify(student);

console.log(jsonText);

This is particularly useful when sending JSON data to a server.

9. Using async/await with Fetch

Modern JavaScript commonly uses async/await with fetch() because it makes asynchronous code easier to read.

async function loadStudents() {

    const response = await fetch('/api/students');

    const students = await response.json();

    console.log(students);
}

The await keyword pauses the execution of the async function until the Promise produces a result.

10. Handling Errors with try/catch

Network operations can fail. A request may encounter a network problem, an unavailable server or another unexpected condition.

Use try/catch to handle errors:

async function loadStudents() {

    try {

        const response = await fetch('/api/students');

        const students = await response.json();

        console.log(students);

    } catch (error) {

        console.error('Request failed:', error);
    }
}

This prevents an unexpected network error from breaking the rest of the application without being handled.

11. Understanding response.ok

One important concept when working with fetch() is that a completed fetch Promise does not automatically mean the HTTP request was successful.

For example, a server may return status codes such as:

  • 200 – successful request.
  • 201 – resource created.
  • 400 – bad request.
  • 401 – authentication required or failed.
  • 403 – access forbidden.
  • 404 – resource not found.
  • 500 – server-side error.

The Response object provides the ok property.

const response = await fetch('/api/students');

if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
}

This allows your application to explicitly handle unsuccessful HTTP responses.

12. A Complete GET Request

A practical GET request can be written like this:

async function loadStudents() {

    try {

        const response = await fetch('/api/students');

        if (!response.ok) {
            throw new Error(`Request failed: ${response.status}`);
        }

        const students = await response.json();

        console.log(students);

    } catch (error) {

        console.error(error);
    }
}

13. Displaying API Data on a Webpage

Fetching data is only part of the process. A useful application should also display the data to the user.

Suppose the HTML contains:

<div id="student-list"></div>

JavaScript can fetch student data and insert the results into the page.

async function loadStudents() {

    const container = document.querySelector('#student-list');

    try {

        const response = await fetch('/api/students');

        if (!response.ok) {
            throw new Error('Unable to load students');
        }

        const students = await response.json();

        container.innerHTML = '';

        students.forEach(student => {

            const item = document.createElement('div');

            item.textContent = `${student.name} - ${student.course}`;

            container.appendChild(item);
        });

    } catch (error) {

        container.textContent = 'Unable to load student information.';
        console.error(error);
    }
}

14. Loading States

Network requests may take some time. A good interface should tell users that information is being loaded.

async function loadStudents() {

    const container = document.querySelector('#student-list');

    container.textContent = 'Loading students...';

    try {

        const response = await fetch('/api/students');

        if (!response.ok) {
            throw new Error('Request failed');
        }

        const students = await response.json();

        container.innerHTML = '';

        students.forEach(student => {

            const item = document.createElement('div');

            item.textContent = student.name;

            container.appendChild(item);
        });

    } catch (error) {

        container.textContent = 'Unable to load student information.';
    }
}

15. Sending Data with POST

Fetch can also send information to a server.

For example:

async function createStudent() {

    const student = {
        name: 'John',
        course: 'Web Development'
    };

    const response = await fetch('/api/students', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(student)
    });

    if (!response.ok) {
        throw new Error('Unable to create student');
    }

    const result = await response.json();

    console.log(result);
}

Notice the three important parts:

  • method specifies POST.
  • headers tells the server that JSON is being sent.
  • body contains the JSON representation of the JavaScript object.

16. Fetching Data from a Public API

During development, developers often work with APIs that provide sample data.

For example:

async function loadPosts() {

    try {

        const response = await fetch(
            'https://jsonplaceholder.typicode.com/posts'
        );

        if (!response.ok) {
            throw new Error(`HTTP error: ${response.status}`);
        }

        const posts = await response.json();

        console.log(posts);

    } catch (error) {

        console.error('Unable to load posts:', error);
    }
}

The returned data can then be processed with normal JavaScript array methods such as map(), filter() and forEach().

17. Working with API Arrays

Many APIs return arrays containing multiple records.

const response = await fetch('/api/courses');

const courses = await response.json();

courses.forEach(course => {
    console.log(course.title);
});

You can also filter the data:

const webCourses = courses.filter(course => {
    return course.category === 'Web Development';
});

Or transform it:

const courseNames = courses.map(course => {
    return course.title;
});

18. Fetching WordPress REST API Data

WordPress provides a REST API that can expose content and other information to JavaScript applications.

For example, WordPress posts can commonly be requested through:

/wp-json/wp/v2/posts

A JavaScript application can request the endpoint:

async function loadWordPressPosts() {

    const response = await fetch('/wp-json/wp/v2/posts');

    if (!response.ok) {
        throw new Error('Unable to load WordPress posts');
    }

    const posts = await response.json();

    posts.forEach(post => {
        console.log(post.title.rendered);
    });
}

This approach is useful when building custom WordPress dashboards, LMS interfaces and interactive frontend components.

19. Query Parameters

Many APIs accept query parameters to control the requested data.

For example:

fetch('/api/courses?category=web-development');

For multiple parameters:

fetch('/api/courses?category=web-development&level=beginner');

JavaScript also provides URLSearchParams for constructing query strings safely.

const params = new URLSearchParams({
    category: 'web-development',
    level: 'beginner'
});

const response = await fetch(`/api/courses?${params}`);

20. Headers

HTTP headers provide additional information about a request or response.

For example, when sending JSON:

const response = await fetch('/api/students', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        name: 'John'
    })
});

Some APIs may also require authentication headers. Sensitive authentication credentials should never be casually exposed in publicly accessible frontend JavaScript.

21. Fetching Multiple Resources

Sometimes an application needs information from multiple endpoints.

Promises can be combined with Promise.all().

const [studentsResponse, coursesResponse] = await Promise.all([
    fetch('/api/students'),
    fetch('/api/courses')
]);

const students = await studentsResponse.json();
const courses = await coursesResponse.json();

console.log(students);
console.log(courses);

This can be useful when several independent pieces of information are required for a dashboard.

22. Avoiding Common Fetch Mistakes

Beginners commonly make several mistakes when using fetch().

Mistake 1: Forgetting response.json()

const response = await fetch('/api/students');

console.log(response);

The Response object is not the actual JavaScript data. You normally need to read the response body.

const response = await fetch('/api/students');

const students = await response.json();

console.log(students);

Mistake 2: Not Checking response.ok

A fetch request may complete while the server still returns an HTTP error status. Check response.ok when appropriate.

Mistake 3: Forgetting await

const students = fetch('/api/students');

console.log(students);

This gives you a Promise rather than the final data.

Use:

const response = await fetch('/api/students');

const students = await response.json();

Mistake 4: Ignoring Errors

Network requests should be handled carefully. Use try/catch and provide meaningful feedback to the user.

23. Fetch and Form Submission

Fetch can be used to submit forms without forcing the browser to reload the entire page.

Example:

const form = document.querySelector('#student-form');

form.addEventListener('submit', async event => {

    event.preventDefault();

    const formData = new FormData(form);

    const student = {
        name: formData.get('name'),
        email: formData.get('email')
    };

    try {

        const response = await fetch('/api/students', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(student)
        });

        if (!response.ok) {
            throw new Error('Submission failed');
        }

        const result = await response.json();

        console.log(result);

    } catch (error) {

        console.error(error);
    }
});

This pattern is useful for modern web applications where users can submit information without leaving the current page.

24. Fetch in a Student Learning Management System

In an LMS such as Dwallnet, Fetch API techniques can be used for many practical features.

  • Loading student course information.
  • Retrieving lessons dynamically.
  • Loading assignments.
  • Submitting assignment responses.
  • Retrieving examination results.
  • Updating student profile information.
  • Loading announcements.
  • Retrieving notifications.
  • Updating course progress.
  • Loading certificates and achievements.

For example, a dashboard could request the student’s progress from an API and update the progress indicator without reloading the entire page.

25. API Security Fundamentals

Frontend JavaScript is visible to users. Therefore, developers should not place private secrets, passwords or sensitive API keys directly inside browser JavaScript.

For applications requiring authentication or privileged operations, sensitive operations should normally be handled through a secure server-side system.

Students should understand that hiding a secret in JavaScript variable names, comments or minified code does not make the secret secure.

26. Practical Example: Course Loader

The following example demonstrates a small course loader.

async function loadCourses() {

    const container = document.querySelector('#course-list');

    container.textContent = 'Loading courses...';

    try {

        const response = await fetch('/api/courses');

        if (!response.ok) {
            throw new Error(`HTTP ${response.status}`);
        }

        const courses = await response.json();

        container.innerHTML = '';

        courses.forEach(course => {

            const article = document.createElement('article');

            const title = document.createElement('h3');
            title.textContent = course.title;

            const description = document.createElement('p');
            description.textContent = course.description;

            article.appendChild(title);
            article.appendChild(description);

            container.appendChild(article);
        });

    } catch (error) {

        container.textContent =
            'Courses could not be loaded at this time.';

        console.error(error);
    }
}

loadCourses();

This demonstrates a complete data flow:

  1. Find the HTML container.
  2. Display a loading message.
  3. Send a fetch request.
  4. Check the response.
  5. Convert JSON into JavaScript data.
  6. Loop through the returned records.
  7. Create HTML elements.
  8. Display the information.
  9. Handle errors.

27. Practical Exercise

Create a webpage containing a button and an empty content container.

When the button is clicked:

  1. Send a fetch request to a suitable test API.
  2. Display a loading message.
  3. Convert the response to JSON.
  4. Display at least five returned records.
  5. Show an error message if the request fails.
  6. Add a button that can request the data again.

28. Advanced Exercise

Create a small student dashboard interface that retrieves three independent resources:

  • Student profile information.
  • Course information.
  • Course progress.

Use Promise.all() to request the resources and display them together.

Add:

  • A loading state.
  • An error state.
  • A success state.
  • Clean semantic HTML.
  • Responsive styling.

29. Key Concepts to Remember

  • Fetch API is used for network requests.
  • fetch() returns a Promise.
  • response.json() reads JSON response data.
  • JSON is a text-based data exchange format.
  • JSON.parse() converts JSON text into JavaScript data.
  • JSON.stringify() converts JavaScript data into JSON text.
  • async/await makes asynchronous code easier to read.
  • try/catch can handle errors in asynchronous operations.
  • response.ok helps detect unsuccessful HTTP responses.
  • POST requests can send data to servers.
  • Content-Type identifies the format of submitted data.
  • Promise.all() can coordinate multiple independent requests.
  • Frontend code should not expose sensitive secrets.
  • Good applications provide loading, success and error states.

30. Conclusion

The Fetch API is one of the most important tools for building modern JavaScript applications. It allows a webpage to communicate with servers and external APIs without requiring a traditional full-page reload.

By combining fetch(), JSON, async/await, error handling and DOM manipulation, developers can build interactive applications that respond to live data.

These skills are particularly important when building dashboards, LMS platforms, WordPress integrations, web applications and frontend interfaces that communicate with backend services.

💻

Code Example

async function loadCourses() {

    const container = document.querySelector('#course-list');

    container.textContent = 'Loading courses...';

    try {

        const response = await fetch('/api/courses');

        if (!response.ok) {
            throw new Error(`HTTP error: ${response.status}`);
        }

        const courses = await response.json();

        container.innerHTML = '';

        courses.forEach(course => {

            const article = document.createElement('article');

            const title = document.createElement('h3');
            title.textContent = course.title;

            const description = document.createElement('p');
            description.textContent = course.description;

            article.appendChild(title);
            article.appendChild(description);

            container.appendChild(article);
        });

    } catch (error) {

        container.textContent =
            'Unable to load courses at this time.';

        console.error(error);
    }
}

loadCourses();
⭐

Key Takeaways

["The Fetch API allows JavaScript to communicate with web servers and APIs.","fetch() returns a Promise because network operations are asynchronous.","Use response.json() to read a JSON response as JavaScript data.","Use response.ok and response.status to inspect HTTP results.","Use async\/await to write readable asynchronous fetch code.","Use try\/catch to handle network and application errors.","Use POST requests when submitting data to a server.","Use JSON.stringify() when sending JavaScript objects as JSON.","Use Promise.all() when multiple independent requests can run together.","Use loading, success and error states to create better user experiences.","Never expose sensitive secrets or private credentials in frontend JavaScript.","The Fetch API is highly useful for dashboards, LMS platforms and WordPress integrations."]

Enjoying this lesson?

Sign in to your Dwallnet Academy student account to track your learning progress and mark lessons as complete.

Sign In to Track Progress