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.

We’ll build a web app that allows a user to input a UK location, in the form of a latitude and longitude, and to display in the browser a few details of the crimes recorded at that location.

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.

I’ll assume you’re already set up and have a bit of familiarity with using a code editor (I’m using VS Code); however, if this is all new to you, this article may help.

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:<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:

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>:

Now in app.js, we’ll start by adding some code to get access to our <form>’s submit button, as well as its <input> fields. While we’re at it, let’s also get access to our <div> with the class of crimes-container, we’ll need this later:

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, 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.

If no crimes have been recorded at the location, allCrimes will be an empty array. Let’s account for this by including the following within our getCrimes function definition, immediately after we have defined allCrimes.

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

Let’s display a simple message in the browser when an error occurs. Define a new function:

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.

We don’t know how long, following the user clicking the submit button, it might be before data is returned from the API and displayed on screen. We can let the user know that they should await the response by including a simple message in crimesContainer while awaiting the response from the API. At the top of the getCrimes function, on the line above try, include a call to a new function displayLoading, and then define that function as:

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: