Programming on the Server Side
38 Fetching Data with AJAX
When you start typing into the Google search box, you immediately get suggestions from the Google database about what to search for. When you view the feed of a social media app like BlueSky, it starts you with a small list of items, and as you scroll down, it loads more and more items for an endless news feed. Both of these are examples of AJAX in action.
Without AJAX, if a web app needs to fetch more content for the user, it has to load and display an entirely new page. With AJAX, the app can launch an HTTP Request from a JavaScript command, receive the HTTP Response without a page load, and incorporate the new content into the page through DOM manipulation. This is done using the built-in JavaScript fetch function.
AJAX stands for Asynchronous JavaScript and XML. XML (eXtensible Markup Language) is a data transfer format that looks a bit like HTML. These days, most apps use JSON for data transfer instead of XML, but the name AJAX has stuck.
Your first AJAX Request
Do it Yourself
Get the helloAjax folder from the Full Stack Example Pack and load it into a browser through a local server. This app presents the user with a single button. When it’s pressed, the contents of the file hello.txt are loaded from the server and displayed on the page. Try it out!
To convince yourself that the text is really coming from hello.txt when you press the button, try this:
- Change the contents of hello.txt, then without reloading the page, press the button again.
- On Chrome, open the Developer Tools and go to the Network tab. Reload the page, then press the button. You will see the AJAX HTTP Request appear, and you can view it along with the HTTP Response.
The Anatomy of the Fetch Command
The code in helloAjax.js (from the Do it Yourself box above) attaches a click listener to a button containing the following code.
fetch("hello.txt") .then(response => response.text()) .then(success)
The above code launches a GET request for the file “hello.txt”, and then calls the function named success when the HTTP Response is received. The success function is a callback function, so named because it’s called later in response to some future event.
The fetch function is asynchronous (the A in AJAX), meaning that it performs its work in the background. The rest of the code can continue to run while we wait for the callback function to get called.
The success function will be called when the response is received. It should accept a single parameter, which will contain the text of the response. The success function for helloAjax looks like this:
function success(text) { let span = document.getElementById("target"); span.innerHTML = text; }
Promises, Chaining, and Arrow Functions
In most cases, you can just cut and paste the above fetch statement, then modify the URL and the success function to suit your needs. But to use fetch most effectively, it’s best if you understand a little more about the syntax and what is going on under the hood.
The fetch function returns a special kind of object called a Promise. This object has a then method you can call to specify a callback function that should be run later, once the Promise is fulfilled. The fetch function launches an HTTP Request and receives an HTTP Response. The Promise object represents its commitment to fulfilling this task.
fetch("hello.txt") // returns a Promise .then(function) // attaches a callback function to it
The then method also returns a Promise. So you can attach another function using another then method call. This is called method chaining. When the first Promise is fulfilled, it calls its then callback function. Whatever that function returns gets passed to the next then callback function once it’s finished, and so on down the line.
fetch(hello.txt") .then(f1) // f1 called when fetch done .then(f2) // f2 called when f1 done .then(f3) // f3 called when f2 done
As is customary when using the fetch API, the first then method specifies a function using the compact arrow function expression syntax.
response => response.text()
For most uses, the above syntax is equivalent to the following anonymous function:
function(response) { return response.text(); }
Now we can bring it all together:
fetch("hello.txt") .then(response => response.text()) .then(success)
The arrow function is called by the Promise object created in the fetch function. The arrow function receives a Response object as an argument. This object contains within it all the header and body information from the original HTTP Response. The arrow function calls the Response object’s text method to extract the text from the response body and returns it to be passed as a parameter to the success method.
Sending GET Parameters
Do it Yourself
Get the helloAjax2 folder from the Full Stack Example Pack and load it into a browser through a local server to see the above code in action. The app presents the user with a single button. When it’s pressed, the contents of the file hello.php are loaded from the server and displayed on the page. The contents of the text box are sent as a GET parameter. Try it out!
To convince yourself that the text is really coming from hello.php when you press the button, try this:
- On Chrome, open the Developer Tools and go to the Network tab. Reload the page, fill in some text, then press the button. You will see the AJAX HTTP Request appear along with the GET parameter.
- Examine the contents of hello.php to verify that it receives a parameter and echoes it into the HTTP response
Sending a GET parameter in a fetch request just means appending the parameters to the URL with the correct syntax. Here’s what that might look like (note the use of console.log for debugging purposes):
let url = "hello.php?nameparam=" + name; console.log(url); // debug fetch(url) .then(response => response.text()) .then(success)
In the above code, a variable called name is being used to construct a URL to load hello.php with a nameparam parameter encoded into it. The nameparam will be received by hello.php.
The PHP program hello.php is a little different than the other PHP programs we have looked at. This one does not have to produce a complete web page. It only echoes the text that we want to send back to the JavaScript code. Here’s what it looks like:
<?php $name = filter_input(INPUT_GET, "nameparam", FILTER_SANITIZE_SPECIAL_CHARS); echo "Hello, <strong>$name</strong>!!!";
Sending POST parameters
Do it Yourself
Get the postrequest folder from the Full Stack Example Pack and load it into a browser through a local server. The app presents the user with a single button and fields to specify the minimum and maximum values of a random integer.
When the button is pressed, the contents of the file random.php are loaded from the server, with the values from the user passed as POST parameters. The script generates the requested integer and it will be displayed on the page. Try it out!
To convince yourself that the numbers are really coming from random.php, try this:
- On Chrome, open the Developer Tools and go to the Network tab. Reload the page, fill in some numbers, then press the button. You will see the AJAX HTTP Request appear. You can find the POST parameters in the Payload tab.
- Examine the contents of random.php to verify that it receives POST parameters and echoes a random integer into the HTTP response
In practice you rarely need to use POST parameters for AJAX since the URL will never be seen by the user. But if you want to send POST parameters, you have to add a second parameter to the fetch function call. This parameter is an object literal that specifies configuration information about the HTTP Request.
You must specify:
- The method (i.e., POST)
- The content type header for the HTTP Request message (this determines the syntax of the parameters in the body)
- The contents of the body (i.e. the parameters)
There are many content types you can use for this. One option is application/x-www-form-urlencoded, which allows you to use the same syntax for POST parameters as you would for GET parameters. Here’s an example:
let params = "x=1&y=2&z=3"; let config = { method: 'POST', headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: params }; fetch("random.php", config) .then(response => response.text()) .then(success)
Coding Practice
- Modify helloajax2 to turn it into a simple translation program in which the user can enter an English word or phrase and get it translated into another language. The PHP program should receive the parameter, then respond with either the correct word in the other language (if it knows it) or “?” if it does not. Make sure it “knows” at least 10 words. The text placed on the web page should be of the form “English word = translated word”. Once it’s working, wrap the input elements in a form element and use the submit event to launch the query. Make the text box required. Use event.preventDefault() in JavaScript to prevent actual submission of the form. This will create a nicer user experience because you can now take advantage of the browser’s built-in form checking, and the user can submit by pressing enter.
- Create a password checking program. It should accept a password from the user in an input element of type “password”. On every input event, it should launch a fetch request to a PHP program. The PHP program should check the password to make sure it is at least 6 characters long, contains uppercase letters, lowercase letters, digits, and symbols. Then echo a code to indicate whether or not this is a good password. If the response shows a problem, indicate this on the page by coloring the input field red. If the response indicates that the password is ok, indicate this by coloring the input field green.
- Create a form that allows the user to enter three numeric guesses and then submit them all at once through an AJAX request to a PHP program. The correct answer is 7. If one of the guesses is correct, the PHP program should return 1, 2, or 3 to indicate which one was correct. If not, the PHP program should return -1, -2, or -3 to indicate which guess is closest to 7. If the user got it right, give them a congratulations message and show them which message was correct using CSS (e.g., changing the color or something similar). If they did not get it right, give them a “please try again” message and show them which message was closest using CSS.