Building a Realtime Geolocation App with Hasura GraphQL and PostGIS

01 April, 2020

Location-based apps are used across industries and have changed the way users interact with the app. For developers though, building a realtime geolocation app is complex on multiple fronts. Some of the complexities involved are:

  • Managing a large dataset in the database
  • Ease of API consumption
  • Realtime frontend rendering

In this post, we will look at building a data-heavy real-time geo-location app with PostGIS and Hasura GraphQL to plot nearby restaurants on a map. At the end, you will realize that Postgres can handle required datasets, GraphQL gives a neat API layer to consume on the client and makes it easy for frontend rendering.

To set up the backend, we will be running a Postgres database, seeding some location data from OpenStreetMap and creating a Postgres function to query nearby data. On the frontend, we will be running a React app with Apollo Client integration to query for location data using Hasura GraphQL API.

Set up Hasura & Postgres with PostGIS

Alright, let's start with the backend setup.

Step 1: Clone the sample app

git clone https://github.com/karthikvt26/postgis-hasura-map
cd postgis-hasura-map

Step 2: Run GraphQL Engine locally

cd hasura
docker-compose up -d

You can verify if Hasura is running by heading to http://localhost:8080/console and you should be able to see the console UI.

Step 3: Install Hasura CLI

Head to Hasura CLI docs to setup the CLI on your machine which we will use in the next step for applying migrations.

Step 4: Configure Hasura Endpoint in config.yaml

endpoint: http://localhost:8080

Now we are done setting up Hasura, Postgres with PostGIS extension and configured Hasura CLI as well.

Seeding Location Data

We already have location data exported from OpenStreetMap for Bangalore and nearby localities for the same. We can quickly get started by applying the migrations for the same.

hasura migrate apply

This will create the necessary tables, seed data and metadata for Hasura. Additionally this also creates a Postgres function which we will talk about a little later.

In case you are interested to export a data set for a different city, then follow the steps below. Else skip to Fetching Nearby Data: Postgres Function section below :)

Head to overpass turbo and update the query to the following:

node
  [amenity=restaurant]
  ({{bbox}});
out;

Now select the region of choice to plot the restaurant data and click on Run at the top. Once you have the locations plotted on the map, click on Export and choose raw OSM data.

osm2pgsql

We need a way to import this raw data into Postgres. We will make use of a module called osm2pgsql which reads this raw data and imports into Postgres. Head over to its github repo for installation instructions.

Once you are done setting this up, execute the following command to do the migration:

osm2pgsql --create -d postgres -H <postgres_host> -P 5432 -U postgres restaurants_data.osm

Replace <postgres_host> with the right IP (Note that you are running Postgres in docker and hence the host value is different for different platforms).

Once the import is done, head over to the Hasura Console at http://localhost:8080/console -> Data tab and Track the table.

Fetching Nearby Data: Postgres Function

Now that we have a dataset ready in Postgres, we need a way to fetch the list of restaurants nearby. The frontend that renders the location map gives the lat/long co-ordinates as input. We will make use of PostGIS, a spatial database extender for Postgres to query nearby data directly in Postgres.

Our dataset is available on planet_osm_point table. We will make use of the following PostGIS's functions:

  • ST_TRANSFORM - changes the coordinates of a geometry from one spatial reference system to another
  • ST_DWITHIN - Returns true if the geometries are within the specified distance of one another.
  • ST_X and ST_Y - Returns the x and y co-ordinates respectively.

We will create a Postgres function which makes use of these PostGIS functions:

CREATE OR REPLACE FUNCTION public.get_nearby_restaurants(lat double precision, long double precision, bound integer)
 RETURNS SETOF nearby_restaurant
 LANGUAGE sql
 STABLE
AS $function$
    SELECT name, ST_X(ST_Transform(way, 4326)) as long, ST_Y(ST_Transform(way, 4326)) as lat 
        FROM planet_osm_point
        WHERE ST_DWITHIN(way, ST_TRANSFORM(ST_SetSRID(ST_Point(long, lat),4326), 3857), bound);
$function$

This function accepts lat, long and a bound range arguments to calculate the locations nearby that bound. The input for these comes from the GraphQL query made from the React app. We will look at this in the next section.

React App Integration

The react app can be started with the Hasura endpoint env.

REACT_APP_HASURA_ENDPOINT=localhost:8080 npm start

If you had successfully applied the data migration, your app should look something similar to this:

Alright, now let's see how this is done.

We need to setup a GraphQL client for React. We chose Apollo Client for this example.

import ApolloClient from 'apollo-boost';

const httpUrl = `http://${ process.env.REACT_APP_HASURA_ENDPOINT }`;

const client = new ApolloClient({
  uri: `${httpUrl}/v1/graphql`,
});

export {
  client
};

The GraphQL query to fetch the restaurants is pretty straightforward.

query fetchNearbyRestaurants (
  $bound: Int!,
  $lat: float8!,
  $long: float8!
) {
  get_nearby_restaurants (args: {
    bound: $bound,
    lat: $lat,
    long: $long,
  }) {
    name
    lat
    long
  }
}

You can notice that, get_nearby_restaurants is the Postgres function and name, lat and long are the fields returned by the function. We are passing in the necessary variables for the function arguments.

To render the Map on React, we are using React Leaflet - https://react-leaflet.js.org.

Summary

We looked at the capabilities of Postgres via functions and it's extensibility via PostGIS. Hasura being a layer over Postgres, leverages all the power of the database and gives a neat GraphQL API for the client to consume.

This app uses OpenStreetMap, although you can make use of Google Places API too. You can read more about integrating with Google Places API with Hasura GraphQL.

search icon

About Hasura

Hasura allows you to mobilize & federate your organisation’s data by building a powerful, secure & flexible GraphQL API, that can query data in your databases, HTTP services, serverless functions as well as third party APIs.
Like what you read? Join our team! We’re hiring

Praveen Durairaju

Praveen Durairaju

Application Engineer. Tech Enthusiast. Follow on Twitter - https://twitter.com/@praveenweb

Read More