---
title: "Integrating Knockout.js with RESTful APIs"  
description: "Integrating Knockout.js with RESTful APIs allows you to fetch data from a server and update your UI dynamically."  
author: "Ravi Vishwakarma"  
published: 2024-06-26  
updated: 2024-06-26  
canonical: https://www.mindstick.com/articles/336250/integrating-knockout-js-with-restful-apis  
category: "knockcout js"  
tags: ["web development", "knockout.js", "front-end development"]  
reading_time: 6 minutes  

---

# Integrating Knockout.js with RESTful APIs

Integrating Knockout.js with [RESTful APIs](https://answers.mindstick.com/qa/111790/how-do-i-design-and-implement-restful-apis-for-my-applications) allows you to [fetch data](https://www.mindstick.com/forum/156768/how-to-fetch-data-using-union-method-in-linq) from a server and [update your](https://answers.mindstick.com/qa/96426/how-can-you-update-your-profile-without-notifying-contacts) UI dynamically. Here's a beginner's guide to fetching data and updating it using Knockout.js:

#### 1. Setting Up Your HTML

Start by [setting up](https://answers.mindstick.com/qa/39912/key-west-fl-briefly-seceded-from-the-us-in-1982-in-response-to-the-us-border-patrol-setting-up-roadblocks-on-all-roads-leading-into-and-out-of-the-florida-keys-what-did-the-new-country-call-itself) your HTML structure where you want to [display data](https://www.mindstick.com/forum/34030/how-to-display-data-in-the-table-using-angular-js) fetched from the API.

```html
<!doctype html>
<html lang="en">

<head>
    <!-- Meta tags for character set and viewport configuration -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Title of the webpage -->
    <title>Bootstrap demo</title>

    <!-- Link to Bootstrap CSS for styling -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">

    <!-- Link to Knockout.js library for MVVM pattern support -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.1/knockout-latest.min.js"
        integrity="sha512-vs7+jbztHoMto5Yd/yinM4/y2DOkPLt0fATcN+j+G4ANY2z4faIzZIOMkpBmWdcxt+596FemCh9M18NUJTZwvw=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.4/knockout.validation.min.js"
        integrity="sha512-b99MDNv5TqiZtPKH2UeHzDAVydmgrOJEPtaPPEF8AgV86eYyqINFI/K7/7f0+R4WNTAVv8KvvpjwfOYHv5rd5g=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>

    <!-- Link to jQuery library -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>

<body>
    <!-- Main container for the content -->
    <div class="container my-3">
        <!-- Header for the section -->
        <h2>Data from Server</h2>

        <div class="d-flex w-100 justify-content-between ">
            <!-- Button trigger modal -->
            <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#postStaticBackdrop">
                New Post
            </button>
            <div>
                <input type="search" data-bind="value: searchText, valueUpdate: 'input'" class="form-control"
                    placeholder="Search ..." />
            </div>
        </div>
        <hr />
        <p class="fw-semibold text-black-50 text-end">
            <span class="" data-bind="text: filteredItemsLength"></span>
            <span>data found.</span>
        </p>
        <div data-bind="css: { 'd-none': filteredItemsLength() !== 0 }">
            <hr/>
            <div class="d-flex justify-content-center fw-bold">
                <div class="text-center">
                    <p class=" fs-2 ">
                        <span class="me-2">😕</span>
                        No records were found
                    </p>
                    <!-- <p class="fw-normal">try somthing new keywords</p> -->
                </div>
            </div>
        </div>
        <!-- Responsive row to display items fetched from the server -->
        <div class="row g-3 row-cols-1 row-cols-md-2 row-cols-lg-4 row-cols-xl-4" data-bind="foreach: filteredItems">
            <!-- Column for each item -->
            <div class="col">
                <!-- Card to display item details -->
                <div class="card h-100">
                    <div class="card-body">
                        <!-- Card title bound to item's title -->
                        <h5 class="card-title text-capitalize" data-bind="text: title"></h5>
                        <!-- Card text bound to item's body -->
                        <p class="card-text" data-bind="text: body"></p>
                    </div>
                    <div class="card-footer border-0 pt-0">
                        <!-- Footer with item id and a link -->
                        <p class="m-0 d-flex justify-content-between">
                            <!-- Item id displayed -->
                            <span class="fs-5 fw-bold" data-bind="text: id"></span>
                            <!-- Link to comments page for the item -->
                            <a
                                data-bind="attr: { href: 'https://jsonplaceholder.typicode.com/posts/' + id + '/comments' }">
                                <!-- SVG icon inside the link -->
                                <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="currentColor"
                                    class="bi bi-arrow-right-circle-fill" viewBox="0 0 16 16">
                                    <path
                                        d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0M4.5 7.5a.5.5 0 0 0 0 1h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5z" />
                                </svg>
                            </a>
                        </p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
<!-- Modal -->
<div class="modal fade" id="postStaticBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="postStaticBackdropLabel" aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
      <div class="modal-content">
        <div class="modal-header">
          <h1 class="modal-title fs-5" id="postStaticBackdropLabel">Add New Post</h1>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div class="modal-body">
            <form class="row g-3" data-bind="submit: function() { addPost({ title: newTitle(), body: newBody(), userId: userId() }); }">
                <input type="hidden" data-bind="value: userId"/>
                <div class="col-12">
                    <label class="form-label fw-bold">Title</label>
                    <input type="text" placeholder="Title" id="newTitle" name="newTitle" data-bind="value: newTitle, valueUpdate: 'input'" class="form-control">
                </div>
                <div class="col-12">
                    <label class="form-label fw-bold">Body</label>
                    <textarea rows="5" placeholder="Body" id="newBody" name="newBody" data-bind="value: newBody, valueUpdate: 'input'" class="form-control"></textarea>
                </div>
                <div class="col-12">
                    <button type="submit" class="btn btn-primary">Add Post</button>
                </div>
            </form>
        </div>
      </div>
    </div>
  </div>

<!-- Link to external JavaScript file -->
<script src="app.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</html>
```

#### 2. Creating Your ViewModel

In your JavaScript file (`app.js`), define your ViewModel, and include functions to fetch data from the API and update your UI.

```javascript
// app.js
// Initialize Knockout validation
ko.validation.init({
    registerExtenders: true, // Register custom validation rules
    messagesOnModified: true, // Show validation messages as soon as a field is modified
    insertMessages: true, // Insert validation messages next to the input elements
    parseInputAttributes: true, // Parse HTML5 input attributes for validation rules
    errorClass: 'text-danger fw-semibold', // CSS class for validation error messages
    messageTemplate: null // Use default message template
}, true);

class AppViewModel {
    constructor() {
        var self = this;

        // Observable array to hold the data fetched from the server
        self.items = ko.observableArray([]);
        self.searchText = ko.observable('');
        self.newTitle = ko.observable('').extend({
            required: { message: "Title is required." },
            minLength: { params: 2, message: "Title must be at least 2 characters." }
        });
        self.newBody = ko.observable('').extend({
            required: { message: "Body is required." },
            minLength: { params: 2, message: "Body must be at least 2 characters." }
        });

        self.userId = ko.observable('11');
        // Function to fetch data from the API
        self.fetchData = function() {
            fetch('https://jsonplaceholder.typicode.com/posts')
                .then(response => response.json())
                .then(data => {
                    self.items(data);
                })
                .catch(error => {
                    console.error('Error fetching data:', error);
                });
        };

        // Computed observable to filter the items based on the filter criteria
        self.filteredItems = ko.computed(function () {
            //console.log(self.items().reverse());
            // Reverse the items array
            var reversedItems = self.items().slice().reverse();

            //var filter = self.filter().toLowerCase(); // Convert filter criteria to lowercase
            if (!self.searchText) {
                return reversedItems; // If no filter, return all items
            } else {
                // Filter the items based on the filter criteria
                return ko.utils.arrayFilter(reversedItems, function (item) {
                    return JSON.stringify(item).includes(self.searchText().toLowerCase()); // Case-insensitive match
                });
            }
        });

        // Function to reverse the observable array
        self.reverseArray = function() {
            self.items(self.items().slice().reverse());
        };

        self.filteredItemsLength = ko.computed(function () {
            return self.filteredItems().length;
        });
        self.fetchData();

        // Initialize validation
        self.errors = ko.validation.group(self);
    }
}

// Apply the Knockout bindings to the AppViewModel
ko.applyBindings(new AppViewModel());

```

## Understanding the Code

**HTML**: `<ul data-bind="foreach: posts">`: This binds to the `posts` observable array in your ViewModel, iterating over each post to display its `title` and `body`.

**JavaScript (ViewModel)**:

- `self.posts = ko.observableArray([]);`: This defines an observable array `posts` to store fetched data.
- `self.fetchData = function() { ... };`: This function uses `fetch()` to make a GET request to the [RESTful API](https://www.mindstick.com/forum/157904/what-is-a-restful-api-and-how-does-it-differ-from-other-types-of-apis) (`https://jsonplaceholder.typicode.com/posts` in this example). Upon successful response, it updates the `posts` observable array with the fetched data.

**Fetching Data**:

- `fetch('https://jsonplaceholder.typicode.com/posts')`: Initiates a GET request to fetch data from the specified API endpoint.
- `.then(response => response.json())`: Converts the response to [JSON format](https://www.mindstick.com/forum/34468/convert-a-string-written-in-json-format-into-a-javascript-object).
- `.then(data => { self.posts(data); })`: Updates the `posts` observable array with the fetched data.
- `.catch(error => { console.error('Error fetching data:', error); });`: Handles any errors that occur during the fetch operation.

#### 3. Updating Data and Sending Changes

To [update data](https://www.mindstick.com/forum/34639/how-to-create-a-stored-procedure-for-insert-and-update-data) or send changes back to the server (POST, PUT, DELETE requests), you would typically [define functions](https://www.mindstick.com/interview/33884/how-do-you-define-functions-in-css-and-benefits) in your ViewModel similar to `fetchData()` but tailored for specific [HTTP methods](https://www.mindstick.com/interview/34481/what-are-http-methods-how-many-types) (`fetch()` supports various HTTP methods).

```javascript
// Function to add a new post
        self.addPost = function(newPost) {
            // Check if the form is valid
            if (self.errors().length === 0) {
                fetch('https://jsonplaceholder.typicode.com/posts', {
                    method: 'POST',
                    body: JSON.stringify(newPost),
                    headers: {
                        'Content-type': 'application/json; charset=UTF-8',
                    },
                })
                .then(response => response.json())
                .then(data => {
                    self.items.push(data); // Assuming the server responds with the created post object
                    self.newTitle(''); // Clear input after successful addition
                    self.newBody(''); // Clear input after successful addition
                    // Hide modal using jQuery
                    $('#postStaticBackdrop').modal('hide');
                    self.errors.showAllMessages(false); // Hide all validation messages
                })
                .catch(error => {
                    console.error('Error adding post:', error);
                });
            } else {
                // Show validation errors
                self.errors.showAllMessages(true);
            }
        };
```

## Output -

![Integrating Knockout.js with RESTful APIs](https://www.mindstick.com/mindstickarticle/a313771d-86d1-445f-9147-28d3b862f162/images/91de99de-947c-4cc8-9c64-da13cf702cba.png)

---

Original Source: https://www.mindstick.com/articles/336250/integrating-knockout-js-with-restful-apis

Copyright © MindStick Software Pvt. Ltd. This Markdown version is provided for developers, AI systems, and offline reading.
