You're probably familiar with http:// (and https://) appearing in web addresses. HTTP stands for hypertext transfer protocol, and it lives in the application layer of the TCP/IP stack (which we learn more about in section 5). When we go to a web page, our browser makes an HTTP request to a server, receiving back an HTML page as a response. For example, if we go to https://www.mathsuniverse.com/lmc our browser requests /lmc from the server of www.mathsuniverse.com (which it finds via DNS - the domain name system). In addition, more HTTP requests may be made for CSS, JS, images, JSON data, videos etc when our browser finds eg <img src="/blah.png"> tags in our HTML - in this case, making a request to the server for /blah.png. We can inspect these request/response pairs via the 'Network' tab of developer tools in our browsers.
In a JavaScript file, add the following:
let data = await (await fetch('https://api.compsci.me/hello')).text()
console.log(data)
Run it with Bun, and you should see 'Hello world' output to the console. Change /hello in the above to /now and you should see the current date and time instead.
I've set up api.compsci.me to handle your requests by sending back appropriate responses.
When you want to make a request from JavaScript, use fetch(). Because it takes a relatively long time (vs CPU cycles) to receive a response, calling fetch() returns a promise which we need to await to resolve. Once that has resolved, we also need to await the .text() method which turns the resulting stream into a string. The double await is rather ugly, but necessary.
Our requests to /hello and /now just responded with plain text. Often, we want to work with structured data instead, such as arrays and objects. For that, we can use JSON, which we learnt about briefly before.
In a JavaScript file, add the following:
let data = await (await fetch('https://api.compsci.me/food')).json()
console.log(data)
console.log('3rd item', data[2])
... very similar to before, but this time we're doing a request to the /food path, and we're using .json() instead of .text(). You should see an array of food output to the console, then the 3rd item ('carrot') on a line by itself (if you incorrectly .text() instead of .json() the '3rd item' would be 'a', which is the 3rd character in the string ["apple", "b...).
You could also access this directly in your web browser - just go to https://api.compsci.me/food and you'll see the same array. Although it is just text, HTTP requests and responses have both a 'body' and a 'header'. The array is in the body, but there's also a header the server has set saying that the response is of type application/json instead of plain/text.
Here the JSON response is just an array. Often, JSON will be an object with lots of key/value pairs, where some of those values might in turn by arrays or objects or just numbers or strings.
When we do these requests between systems, we're using an API (Application Programming Interface), a kind of bridge between systems. You don't need to know the internals of the code I run on api.compsci.me to be able to use it. The API defines how programs can request and exchange data. There are different kinds of APIs, but here we're using an HTTP API over a network.
Try opening developer tools in your browser when you're on your website, and put the same code in the developer tools console that you just ran with Bun. You'll notice it should work exactly the same in your browser, because fetch() is a web API (there's the word API again, but this time I'm referring to a function built-in to your browser's JavaScript engine, a function you haven't seen the internal code for but can still use by understanding its parameters and return value).
We're going to make a simple page that gets the list of food from the server and shows it on the page. First, we'll start by not getting the data from the server, and just use some disgusting food stored in an array:
<!doctype html>
<title>Food</title>
<h1>Food</h1>
<ul id="list">loading...</ul>
<script>
showFood()
async function showFood () {
let food = ['snails', 'frogs legs']
let html = ''
for (let item of food) {
html += `<li>${item}</li>`
}
list.innerHTML = html
}
</script>
Run the file with Bun or Vite and you'll see two horrible food items on the page.
Now we're going to get the data from the API server instead. Do this by replacing one line of code:
// replace this line
let food = ['snails', 'frogs legs']
// with this
let food = await (await fetch('https://api.compsci.me/food')).json()
Refresh the page in your browser and you'll see the list of food that's come from the server.
There is a slight difference though. If you keep refreshing the page you'll notice there's a short delay between the heading 'Food' appearing and the list appearing. That's because once the HTML page has loaded it needs to do another request to the server to get the list of items before showing them.
So far you've been doing requests to a server I set up at api.compsci.me. Now, we're going to try getting data from some public API endpoints.
In a web browser, go to https://api.thedogapi.com/v1/breeds and you'll see JSON data for a bunch of dog breeds. We're going to use this on our web page.
While on your web page, put the following in the web developer console:
let data = await (await fetch('https://api.thedogapi.com/v1/breeds')).json()
... then type data in the console and click on the arrow next to the shown array to start exploring it. You'll see its an array with 172 items, each one being an object with details of a different dog breed, with properties such as name and life_span.
Let's display that in an HTML page:
<!doctype html>
<title>Dogs</title>
<h1>Dog breeds</h1>
<ul id="list">loading...</ul>
<script>
showDogs()
async function showDogs () {
let breeds = await (await fetch('https://api.thedogapi.com/v1/breeds')).json()
let html = ``
for (let item of breeds) {
html += `<li>${item.name} - ${item.life_span}</li>`
}
list.innerHTML = html
}
</script>
This could easily be expanded so that when you click on a dog, it shows you lots of other facts about that breed.
Here are some other data sources for you to explore and incorporate into your web page:
Here's a bigger list of APIs that you could get data from - make sure to just try those that say 'No' under 'Auth'. You'll notice that some of them have more than one option for what data you get, by playing around with the path you provide in the URL.
GET'ing data vs POST'ing dataSo far we've been doing HTTP requests that simply get some data from a server - these are called GET requests. When we want to change something on the server - such as adding a record to a database or uploading an image - we should use POST requests instead.
The server at api.compsci.me has a GET endpoint /things that will return as JSON an array of things. But it also allows you to add a new thing by doing a POST to /things. This is multiplayer... there's one global array of things, which anyone can add to.
We can GET things with let things = await (await fetch('https://api.compsci.me/things')).json(). If no one has added a thing to it yet, you'll just see an array with one item ['default thing']. You're going to add a thing to it:
let thingToAdd = prompt('What thing would you like to add?')
await fetch('https://api.compsci.me/things', {
method: 'POST',
body: thingToAdd
})
let things = await (await fetch('https://api.compsci.me/things')).json()
console.log('Here are the current things:', things)
... run the above with Bun and you'll be prompted for what 'thing' to add. Type a word and you'll then be shown the current items in the things array. If you enter an empty thing (ie leave it blank when prompted) then the array of things will be reset to an empty array.
The above code will also work fine on a web page. Note that when we do the POST fetch() we await for a response from the server but we're not actually doing anything with that response. Usually you'd add some code to check the response is ok before continuing.
In this guide we explored how to make HTTP requests either from Bun or from a web browser. We saw that servers send back different responses, which could be eg HTML, CSS, an image, plain text or JSON. JSON responses are particularly useful, as we can populate JavaScript objects with them and use them in various ways on our web pages. We then learnt that as well as just GET'ing data from a server, we can also send data to a server with POST.
This guide has been about creating client-side code that runs in either Bun or in a web browser and sends requests to a server. Soon, you'll be learning how to create server-side code that receives those requests and sends back responses.