Build a simple crime-info web app using JavaScript

JavaScript beginner (like me)?

Looking for something quick and simple to build that touches on a few important concepts?

Hopefully this will tickle your fancy.

I’ll run through how to build a very simple web app using JavaScript that consumes data from the UK police API, to display crimes recorded at a specified location.

Ok, so police records might be kind of a heavy topic, but the API in question is super easy to use and doesn’t require an API key — it would be a crime not to play around with it.

Before we start, a disclaimer, or, to really milk the crime theme, a confession: this is just a bit of basic fun and, as always, there are probably better ways to build what I cover here. If you have any suggestions, please do shout out in the comments.

With that out of the way, let’s do this.

What we’ll build

We’ll implement this using the crimes-at-location method from the UK police API.

The full code is provided at the very end of this article.

The web app

In a new directory, make three files: index.html, app.js and main.css.

We’ll mostly be working in app.js (and we’ll do very little in CSS), but first, let’s start with our HTML file: Add some boilerplate, including <link> and <script> tags with relative paths to the main.css and app.js files, respectively.

We’re going to request data from the police API based on coordinates of latitude and longitude. Let’s take a quick look at the API docs to help us plan our approach in getting data from it: Here we can see that the API endpoint has the following format: https://data.police.uk/api/crimes-at-location?lat=<our_latitude>&lng=<our_longitude>

Just to see what the data returned from the API looks like, let’s make a request to the API using some actual coordinates. For this, you could use an API testing tool, such as Insomnia or Postman; However, for our simple example, I’ll just paste the following into the browser search bar: https://data.police.uk/api/crimes-at-location?lat=51.513103&lng=-0.124563

This uses the latitude (51.513103) and longitude (-0.124563) for Covent Garden, London, and gets back an array of objects containing crime data:

For our web app, we want to allow a user to specify their own coordinates for latitude and longitude, and submit a request to the API using these coordinates as request parameters. So, back in the html, inside the <body> element, let’s include a <form> element with <input>s for coordinates, as well as a submit <button> to kick-off the request to the API. At this stage we’ll also add a <div> element, where data from the API will ultimately be displayed. We’ll include an <h1> header element too, just to give the app some heading text. Here’s the snippet of the html to add inside of the <body>:

The JavaScript bit

Next, add an event listener on the submit button that will execute a callback function handleSubmit when the button is clicked.

submitButton.addEventListener('click', handleSubmit)

And let’s define the function handleSubmit:

Above, we first useevent.preventDefault() to prevent the page re-loading when the submit button is clicked. Then, we incorporate the values of latitude and longitude in a string, userEndpoint, representing the API’s endpoint. Finally, we call another function, getCrimes that takes userEndpoint as an argument.

getCrimes is where the sausage really gets made — this is where we’ll make the request to the API and determine what to do with the response that comes back. Let’s start defining this function as follows:

Above, we use fetch() to start our request to get data from the endpoint. However, we don’t know how long this process will take, and we want the browser to continue working while we’re waiting for the data to be received. To achieve this, we use async/await syntax. Here’s a good article covering the syntax, along with the use of response.json() for extracting the JSON object from a fetch response.

To summarise, on line 3 of the snippet above, we await the response from fetch. Then, using the json() method, we extract the JSON object from response and assign it to the variable allCrimes. In a moment, we’ll add in a call to another function, to display the data in the browser window. But for now, we just log the result, using console.log(allCrimes).

If we’re using valid latitude and longitude coordinates, we should get a successful response from the API. But what if, for whatever reason, the API returns an error rather that the data we had hoped for? To add some simple error handling, we use try…catch. This way, if an error occurs in the try block, the catch block runs (for now, this just results in a console.error).

Here’s the code so far:

And here’s what it looks like using the console in Chrome DevTools, when we submit our form with valid latitude and longitude coordinates:

Great. We have data being logged to the console. Now try with some invalid coordinates (e.g., 1234, 5678) and see the error message that gets logged.

Instead of logging all of the data to the console, we want to display selected parts of it in the browser window. To do this, replace the console.log in getCrimes with a call to a new function displayCrimes, taking allCrimes as an argument.

We’ll define displayCrimes as follows:

In the above function, we map over allCrimes and for each crime in the allCrimes array, we return a template literal that incorporates selected parts of the crime data, accessed using dot notation; for example crime.id, and crime.category. The result is stored in htmlArray , which is an array of strings. Note also that each string resembles html, as we’ve included <div> and <p> tags in the template literal.

Finally on line 10, we join() the elements of htmlArray together, and use innerHTML to inject the resulting string as HTML into crimesContainer.

We’re almost done. Let’s just make a few small additions to account for the following possibilities:

  1. We get a successful response from the API (no errors), but no crime records exist at the location specified.
  2. The API returns an error. For example, if the user inputs invalid coordinates.
  3. The API takes a long time to return data, leaving the user wondering what’s going on.

1. Successful response, but no crimes recorded at the location

if (allCrimes.length === 0){  crimesContainer.innerHTML = 'No crimes found'  return}

2. The API returns an error.

And add a call to this function in the catch block of getCrimes (replacing console.error). In this case the errorMsg displayed won’t be particularly useful, but it at least lets the user know that there was a problem.

3. The API takes time to return data

Finally, add a tiny bit of CSS into your main.css file, just to separate out the information on screen:

.crime-container{border: 1px solid black;margin: 5px;padding: 5px;}

And that’s it! We now have a simple web app that displays information on crimes at a user-specified location. The app also does some basic error handling and displays a simple loading message.

Here’s the final code in CodePen: