APIs and CORS
Learning objectives
- Knows how to use the Fetch API in the browser to retrieve data from an API.
- Knows the concept Cross-Origin Resource Sharing (CORS) and can implement an API that allows CORS.
Fetch API
Querying resources (e.g. an API) can be done using the Fetch API. Fetch API provides basic functionality for making requests to the server -- in the most simple form, a request is done simply by fetch("address")
, where address
stands for the queried address. The function fetch
is asynchronous, so when working with it, we need to wait for its completion.
The following example shows basic code for making a request with fetch
to the path at /api
.
const response = await fetch("/api");
console.log(response);
When the above code is run on page, the following output appears to the browser console. Here, we assume that the web application responds to request made to /api
.
Response {type: "basic", url: "http://address/api", redirected: false, status: 200, ok: true, ...}
body: (...)
bodyUsed: true
headers: Headers {}
ok: true
redirected: false
status: 200
statusText: "OK"
type: "basic"
url: "http://address/api"
The response object contains specific details from the response, including the response status and response headers. If the API responds with JSON content, we need to explicitly parse the JSON content from the response. This is done with an asynchronous method json
that is associated with the response
object.
In the example below, we make a request to the path /api
and read the response as a JSON document. The Function json()
returns a JavaScript object, which we then print.
const response = await fetch("/api");
const json = await response.json();
console.log(json);
In the example below, the server has responded with a JSON document {"hello", "world"}
, which has then been transformed into a JavaScript object.
{hello: "world"}
In the above examples, we've looked into the basic use of the Fetch API, where we have made GET requests to the server, expecting that the response is formatted as JSON. The function fetch
supports also making POST requests -- these are done by specifying a method and the body of the request. In the example below, we post a JavaScript object to the API -- note that we have to transform the object into a JSON document before sending. This is done using the JSON.stringify
function. Then, we read in the response assuming that it contains a JSON document and print the JSON document as a JavaScript object to the console.
const data = { hello: "again" };
const response = await fetch("/api", {
method: "POST",
body: JSON.stringify(data),
});
const json = await response.json();
console.log(json);
Other parameters can be also added to the request, including header information. For more details, visit the Using Fetch API page on the Mozilla Developer Network.
Cross-origin resource sharing
Services offered by a web server can be accessed practically from anywhere unless explicitly denied. It is possible to create applications that mostly consist of JavaScript code executed on the client, while the web server code offers APIs that the JavaScript code on the client uses.
By default, queries made using JavaScript are limited to the current domain. That is, for example, JavaScript code retrieved as a part of a page from aalto.fi
cannot be used to make queries to another domain, unless the server responding to requests in the other domain explicitly allows this.
Allowing requests outside the current domain is done using a Cross-Origin Resource Sharing (CORS) policy. In practice, the server adds information to the response headers indicating whether cross-origin requests (i.e. requests from other domains) are allowed.
If we wish to allow cross-origin requests in our applications, perhaps the easiest approach is taking a CORS library into use. One such library is available at https://deno.land/x/cors. In the following example, we have taken the library into use -- the library effectively provides a middleware that we can use -- and thus cross-origin requests are allowed by the server.
import {
Application,
Router,
} from "https://deno.land/x/oak@v12.6.1/mod.ts";
import { oakCors } from "https://deno.land/x/cors@v1.2.2/mod.ts";
const app = new Application();
const router = new Router();
const jsonData = ({ response }) => {
response.body = { hello: "world" };
};
router.get("/api", jsonData);
app.use(oakCors());
app.use(router.routes());
app.listen({ port: 7777 });
Now, when we make a request to the server, we see a header access-control-allow-origin
that is used to policy cross-origin requests. In this case, requests from browsers accessing sites in any domain can be made to the server.
$ curl -v http://localhost:7777/api
// ...
< HTTP/1.1 200 OK
< content-length: 17
< access-control-allow-origin: *
< content-type: application/json; charset=utf-8
// ...
{"hello":"world"}%
$