> ## Content Index
> Fetch the complete content index at: https://blog.afi.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# OSRM Table API: Free and open source distance matrix API
- URL: https://blog.afi.io/blog/osrm-table-api-free-and-open-source-distance-matrix-api/
- Published: 2023-09-17T12:38:00.000Z
- Updated: 2025-08-15T18:21:36.000Z
- Description: Learn about the OSRM Table API, a free and open source alternative to the Google Maps Distance Matrix API.
- Author: Afian Anwar
- Tags: tech, osrm, mapbox

In this final installment of our series on [Open Source Routing Machine](https://project-osrm.org/?ref=blog.afi.io) (OSRM), we look at the [OSRM Table API](https://project-osrm.org/docs/v5.24.0/api/?ref=blog.afi.io#table-service), a fast and performant distance matrix API that calculates the distances and travel times between a set of points. We'll then use it to develop a restaurant search application that guides you to the closest restaurant with a desired ⭐️ star rating and 💲 price point (source code: [frontend](https://github.com/afilabs/osrm%5Ftable%5Fapi%5Ffrontend?ref=blog.afi.io) \+ [backend](https://github.com/afilabs/osrm%5Ftable%5Fapi%5Fbackend?ref=blog.afi.io)).

![Restaurant search application powered by the OSRM Table API](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2024/01/find-restaurant.png)

Restaurant search application powered by the OSRM Table API

Part 1: [Introduction to OSRM: Setting up osrm-backend using Docker](https://www.afi.io/blog/introduction-to-osrm-setting-up-osrm-backend-using-docker?ref=blog.afi.io)  
Part 2:[Hosting the OSRM API on EC2: Running osrm-backend as a web service](https://www.afi.io/blog/hosting-the-osrm-api-on-amazon-ec2-running-osrm-backend-as-a-web-service/?ref=blog.afi.io)  
Part 3: [OSRM Route API: Free turn by turn directions and polylines](https://afi.io/blog/osrm-route-api-free-directions-api-with-turn-by-turn-directions-and-polylines/?ref=blog.afi.io)  
**Part 4: OSRM Table API: Free distance matrix API (this article)**

[Hosting OSRM on your server](https://www.afi.io/blog/hosting-the-osrm-api-on-amazon-ec2-running-osrm-backend-as-a-web-service/?ref=blog.afi.io) offers a significant advantage when using the Table API - it serves as a cost-free alternative to the [Google Maps Distance Matrix API](https://developers.google.com/maps/documentation/distance-matrix?ref=blog.afi.io) and, importantly, it does not impose a limit on the number of elements per request. This lack of restriction on elements - unlike Google's Distance Matrix API, which has a [strict cap at 625 elements](https://developers.google.com/maps/documentation/distance-matrix/usage-and-billing?ref=blog.afi.io#:~:text=While%20there%20is%20no%20maximum,elements%20per%20client%2Dside%20request.) (for example, 25 origins multiplied by 25 destinations), makes the OSRM Table API particularly suitable for data-heavy tasks such as [route optimization](https://afi.io/routing%5Fengine?ref=blog.afi.io).

### What is a distance matrix API?

A distance matrix API is a web service that returns the distances (or travel times) between two or more points. This is almost always in the form of a table i.e. a 2D array *D* where the *i*\-th - *j-*th entry D\[i\]\[j\] refers to the distance between locations *i* and *j*. For example, let's say we are at location 0 (distance matrices are almost always zero-indexed) and want to generate a distance matrix that includes locations 1 and 2 that are 2 km and 6 km away respectively. Here's a graphical representation of the problem, with nodes representing the physical locations and the edges representing the distances between them.

![Distance matrix from the OSRM Table API represented as a graph](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2023/12/Screen-Shot-2023-12-08-at-3.29.58-AM.png)

Distance matrix represented as a graph

The corresponding distance matrix *D* looks like this:

| **0** | **1**        | **2**        |              |
| ----- | ------------ | ------------ | ------------ |
| 0     | 0 → 0 (0 km) | 0 → 1 (6 km) | 0 → 2 (2 km) |
| 1     | 1 → 0 (6 km) | 1 → 1 (0 km) | 1 → 2 (8 km) |
| 2     | 2 → 0 (2 km) | 2 → 1 (8 km) | 2 → 2 (0 km) |

To get the distance between locations 1 and 2, all we need to do is look up entry D\[1\]\[2\] to get the answer (8 km).

🌎

Many companies provide commercial distance matrix API services. Two of them that we've reviewed on this blog are Google Maps ([Google LMFS: ComputeRouteMatrix API](https://afi.io/blog/google-lmfs-routes-preferred-computeroutematrix-api/?ref=blog.afi.io)) and Mapbox ([Real-time traffic with the Mapbox Matrix API](https://blog.afi.io/blog/real-time-traffic-with-the-mapbox-api/)).

### OSRM Table API example

For this example, we'll use the OSRM server loaded with the map of British Columbia, Canada that we set up in [Hosting the OSRM API on EC2: Running osrm-backend as a web service](https://www.afi.io/blog/hosting-the-osrm-api-on-amazon-ec2-running-osrm-backend-as-a-web-service/?ref=blog.afi.io) to calculate the distances between an address A\* (0) in Vancouver's tony West Point Grey neighborhood:

0: [4532 West 4th Avenue, Vancouver, BC](https://maps.app.goo.gl/XfATb2UkZePSjbSaA?ref=blog.afi.io) (49.268,-123.212)

to two popular restaurants (B and C):

1: [Togo Sushi UBC, 3380 Shrum Lane, Vancouver, BC](https://maps.app.goo.gl/VUGz71PmiYmjt4JY8?ref=blog.afi.io) (49.253,-123.237)  
2: [Raisu, 2340 W 4th Ave, Vancouver, BC](https://maps.app.goo.gl/b5KGoXsoZiK2yP4U6?ref=blog.afi.io) (49.268,-123.160)

![OSRM Table API call with one origin and two destinations](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2023/12/image-1.png)

OSRM Table API call with one origin and two destinations

Once osrm-backend is up and running, take note of its URL, {osrm\_server\_url}. Alternatively, you can you can use the publicly available test server [https://routing.openstreetmap.de/routed-car](https://routing.openstreetmap.de/?ref=blog.afi.io) but this comes with strict usage limits.

**Endpoint** `GET`   
http://{osrm\_server\_url}:5000/table/v1/driving/  
\-123.2100192,49.2688067;-123.2353903,49.2539939;-123.160899,49.2680595?  
annotations=distance

The general structure of a Route API call is as follows:

`/**{service}**/**{version}**/**{profile}**/**{coordinates}**?option=value&option=value`

`:5000` is the default port OSRM comes installed with.

`/table/v1/driving/` instructs OSRM to call the [Table Service API](https://project-osrm.org/docs/v5.24.0/api/?ref=blog.afi.io#table-service) (ver 1) with the `driving` profile.

`-123.2100192,49.2688067;-123.2353903,49.2539939;-123.160899,49.2680595` are the GPS coordinates **(in longitude, latitude format)** of the locations you want to generate a distance matrix for.

`annotations=distance` specifies that you want the distance matrix to return distances (in meters) only. If you want both durations (travel times) and distances, use `annotations=distance,duration`.

**Output**

```
{
    "code": "Ok",
    "distances": [
        [
            0,
            3627.8,
            3648.2
        ],
        [
            3688.3,
            0,
            7083.6
        ],
        [
            3653.7,
            7029.9,
            0
        ]
    ],
    "destinations": [
        {
            "hint": "OHCjg5wlAoZTAQAAiAIAACcAAAAlAAAAhoGNQoXPBkPycgJBu5r0QKkAAABEAQAAFAAAABIAAACXYAAA1Pan-GTH7wLd9qf4R8jvAgEA_xDOM3Il",
            "distance": 25.254312466,
            "name": "",
            "location": [
                -123.210028,
                49.26858
            ]
        },
        {
            "hint": "YAOLgz2KoYM5AAAAFAAAAAwAAAA_AAAAYx8fQmJ0XEEe4wdBOHYuQjkAAAAUAAAADAAAAD8AAACXYAAA4ZKn-BuO7wLCk6f4ao7vAgIAvxTOM3Il",
            "distance": 18.581253201,
            "name": "Shrum Lane",
            "location": [
                -123.235615,
                49.253915
            ]
        },
        {
            "hint": "xNsAgB09Go1QAAAAOAAAAB0AAAAeAAAAk6WzQu8yd0JtZR5B6oI0QVAAAAA4AAAAHQAAAB4AAACXYAAAxbao-CrG7wK9tqj4XMXvAgMAvwXOM3Il",
            "distance": 22.917697769,
            "name": "West 4th Avenue",
            "location": [
                -123.160891,
                49.268266
            ]
        }
    ],
    "sources": [
        {
            "hint": "OHCjg5wlAoZTAQAAiAIAACcAAAAlAAAAhoGNQoXPBkPycgJBu5r0QKkAAABEAQAAFAAAABIAAACXYAAA1Pan-GTH7wLd9qf4R8jvAgEA_xDOM3Il",
            "distance": 25.254312466,
            "name": "",
            "location": [
                -123.210028,
                49.26858
            ]
        },
        {
            "hint": "YAOLgz2KoYM5AAAAFAAAAAwAAAA_AAAAYx8fQmJ0XEEe4wdBOHYuQjkAAAAUAAAADAAAAD8AAACXYAAA4ZKn-BuO7wLCk6f4ao7vAgIAvxTOM3Il",
            "distance": 18.581253201,
            "name": "Shrum Lane",
            "location": [
                -123.235615,
                49.253915
            ]
        },
        {
            "hint": "xNsAgB09Go1QAAAAOAAAAB0AAAAeAAAAk6WzQu8yd0JtZR5B6oI0QVAAAAA4AAAAHQAAAB4AAACXYAAAxbao-CrG7wK9tqj4XMXvAgMAvwXOM3Il",
            "distance": 22.917697769,
            "name": "West 4th Avenue",
            "location": [
                -123.160891,
                49.268266
            ]
        }
    ]
}
```

`distances` is a 2D array representing a matrix in row-major order, meaning that `distances[i][j]` indicates the travel distance from location *i* to location *j*. In the provided example, the following coordinates are sequentially mapped to the sources array:

0: -123.2100192,49.2688067

1: -123.2353903,49.2539939

2: -123.160899,49.2680595

Therefore, location 0, which is 4532 West 4th Avenue, corresponds to the first coordinate (-123.2100192,49.2688067), and the subsequent locations follow in the same order. To find the distance from location 0 (4532 West 4th Avenue, Vancouver, BC) to location 1 (Togo Sushi UBC, 3380 Shrum Lane, Vancouver, BC), you would use `distances[0][1]`. In this case, it shows a distance of 3627.8 meters, which is very close to the [3448 meters returned by Google Maps](https://www.google.com/maps/dir/4532+W+4th+Ave,+Vancouver,+BC+V6R+1R3/3380+Shrum+Lane,+Vancouver,+BC+V6T+1W5/@49.2613252,-123.2336335,15z/data=!4m15!4m14!1m5!1m1!1s0x548672ee489023c5:0x4abcba327a1cbd9e!2m2!1d-123.2100192!2d49.2688067!1m5!1m1!1s0x548672d1129a18a3:0x8593c0314081f5d7!2m2!1d-123.2354276!2d49.2539577!3e0!5i1?entry=ttu&ref=blog.afi.io). 

![Driving directions from the Google Maps API closely match that from OSRM](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2023/12/Screen-Shot-2023-12-07-at-3.35.28-AM.png)

Driving directions from the Google Maps API closely match that from OSRM

This small difference can be attributed to the different data sources used by Google Maps and OSRM. OSRM uses Open Street Maps (OSM) which relies heavily on volunteer contributions and open data sources while Google Maps uses a combination of satellite imagery, Street View, and data from local governments and organizations.

### Restaurant search app

To explore a practical application of the OSRM Table API, we'll build a web based restaurant search application that lets you find nearby restaurants that match your desired ⭐️ star rating and 💲 price point. The [Google Maps Nearby Search API](https://developers.google.com/maps/documentation/places/web-service/search-nearby?ref=blog.afi.io) is used to gather a list of restaurants, which are ranked based on their distance from your specified location. This ranking is done using the distance matrix of the Table API, which we discussed earlier (source code: [frontend](https://github.com/afilabs/osrm%5Ftable%5Fapi%5Ffrontend?ref=blog.afi.io) \+ [backend](https://github.com/afilabs/osrm%5Ftable%5Fapi%5Fbackend?ref=blog.afi.io)).

![](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2023/12/Screen-Shot-2023-12-30-at-1.54.03-AM.png)

Here's what we'll build - a restaurant search application that ranks nearby restaurants

To get started, install and run both [osrm\_table\_api\_frontend](https://github.com/afilabs/osrm%5Ftable%5Fapi%5Ffrontend?ref=blog.afi.io) and [osrm\_table\_api\_backend](https://github.com/afilabs/osrm%5Ftable%5Fapi%5Fbackend?ref=blog.afi.io) locally. We'll then work our way through the code and see how a call to the Google Maps Nearby Search API is used to populate restaurant locations on the map. Additionally, we will see how, when paired with the OSRM Table API, this setup allows us to sort restaurants by distance, starting from the nearest and progressing to the farthest.

### osrm\_table\_api\_frontend

[osrm\_table\_api\_frontend](https://github.com/afilabs/osrm%5Ftable%5Fapi%5Ffrontend?ref=blog.afi.io) is a simple [React](https://react.dev/?ref=blog.afi.io) app that displays the map layer, marker overlays, location search bar and restaurant listing table. After you've cloned the repository, the first order of business is to set up the .env environment variables. In [terminal](https://support.apple.com/en-sg/guide/terminal/welcome/mac?ref=blog.afi.io) (Windows users can use [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/overview?view=powershell-7.4&ref=blog.afi.io)), open up the osrm\_table\_api\_frontend folder and in the app's root directory, use a text editor like [nano](https://www.nano-editor.org/?ref=blog.afi.io) to create a .env file by running `nano .env`. Type in the following and save the file (ctrl + s).

```bash
REACT_APP_MAPBOX_TOKEN={MAPBOX_ACCESS_TOKEN}
REACT_APP_API_URL='http://localhost:3001'
REACT_APP_GOOGLE_API_KEY={GOOGLE_API_KEY}
```

`REACT_APP_API_URL` indicates that the paired "API" backend, osrm\_table\_api\_backend, runs on port 3001.

`REACT_APP_GOOGLE_KEY` and `REACT_APP_MAPBOX_ACCESS_TOKEN` are your Google Maps ([retrieving your Google API key](https://www.afi.io/blog/creating-a-route-with-the-google-maps-directions-api/?ref=blog.afi.io)) and Mapbox ([how do I set up my Mapbox access token?](https://www.afi.io/blog/real-time-traffic-with-the-mapbox-api/?ref=blog.afi.io)) API keys respectively.

Run `npm install` to install the required dependencies and `npm start` to spin up the server. Fire up localhost:3000 in your browser and you should see an empty map centered on Vancouver, Canada. The location address bar should work just fine but pressing the \[ 🔍 \] button will return a 400 bad request response.

### osrm\_table\_api\_backend

To get the app working, clone osrm\_table\_api\_backend in a new tab and set up the .env file in the root directory. Replace {GOOGLE\_API\_KEY} with the same key you used in [osrm\_table\_api\_frontend](https://github.com/afilabs/osrm%5Ftable%5Fapi%5Ffrontend?ref=blog.afi.io).

```bash
NODE_ENV=''
PORT='3001'
GOOGLE_API_KEY={GOOGLE_API_KEY}
```

Save the file and again, run `npm install` followed by `npm start`. With both apps up and running, pressing the blue search button \[ 🔍 \] button should return a list of nearby restaurants to your location.

### Nearby Search API example

The [Google Maps Nearby Search API](https://developers.google.com/maps/documentation/places/web-service/search-nearby?ref=blog.afi.io) allows users to search for nearby establishments and points of interest, such as bars, restaurants, gas stations, and ATMs, mirroring the local search capability found on Google Maps (note: in this blog post we'll be using the older [GET version of Nearby Search](https://developers.google.com/maps/documentation/places/web-service/search-nearby?ref=blog.afi.io) \- it's easier to use than the newer `POST` API).

![](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2023/12/Screen-Shot-2023-12-26-at-4.09.04-AM.png)

The local search feature on Google Maps lets you find nearby places with a single click

**Endpoint** `GET` 

https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=49.2688067,-123.2125941  
&radius=2500  
&type=restaurant  
&key=GOOGLE\_API\_KEY

`location` refers to the geographic coordinates **(in latitude, longitude format)** of your current location.

`radius` is the search radius in meters, so `radius=2500` therefore returns all valid points of interest within a 2500 m radius of the specified `location`.

`type` restricts the results to places matching the specified type. Only one type may be specified. [Supported types](https://developers.google.com/maps/documentation/places/web-service/supported%5Ftypes?ref=blog.afi.io) include "restaurant", "plumber" and "gym", among many others.

`key` is your Google Maps API key. Watch [this video](https://www.youtube.com/watch?v=2%5FHZObVbe-g&t=1s&ref=blog.afi.io) for a step by step walkthrough on how to generate an API key for your project.

**Output**

```JSON
{
    "html_attributions": [],
    "results": [
        {
            "business_status": "OPERATIONAL",
            "geometry": {
                "location": {
                    "lat": 49.2687435,
                    "lng": -123.1854349
                },
                "viewport": {
                    "northeast": {
                        "lat": 49.2700363302915,
                        "lng": -123.1840715697085
                    },
                    "southwest": {
                        "lat": 49.2673383697085,
                        "lng": -123.1867695302915
                    }
                }
            },
            "icon": "https://maps.gstatic.com/mapfiles/place_api/icons/v1/png_71/restaurant-71.png",
            "icon_background_color": "#FF9E67",
            "icon_mask_base_uri": "https://maps.gstatic.com/mapfiles/place_api/icons/v2/restaurant_pinlet",
            "name": "La Quercia",
            "opening_hours": {
                "open_now": false
            },
            "photos": [
                {
                    "height": 3072,
                    "html_attributions": [
                        "\\u003ca href=\"https://maps.google.com/maps/contrib/112924311757606934120\"\\u003eShaun Liu\\u003c/a\\u003e"
                    ],
                    "photo_reference": "AWU5eFgxYlZ5DNwbGVQbPx1JWIwk6fwZRr0akp6ItGVGUYB8ufNuLTXtJDYesN9H2YlGMPFluum3N-r7PmRRC-n6rWn4XORGLMJHDdf4IpB0N-BJn7F_6M_erpqOSBIUWHx3RY6PFhSFg60U2zuPbEnHQEx8Cs2CKYbuxbbTE8ImP7Ra0yNS",
                    "width": 4080
                }
            ],
            "place_id": "ChIJ34VbDlZyhlQRQ9iaa47Y7rI",
            "plus_code": {
                "compound_code": "7R97+FR Vancouver, BC, Canada",
                "global_code": "84XR7R97+FR"
            },
            "price_level": 3,
            "rating": 4.6,
            "reference": "ChIJ34VbDlZyhlQRQ9iaa47Y7rI",
            "scope": "GOOGLE",
            "types": [
                "restaurant",
                "food",
                "point_of_interest",
                "establishment"
            ],
            "user_ratings_total": 384,
            "vicinity": "3689 West 4th Avenue, Vancouver"
        }
    ],
    "status": "OK"
}
```

In the response returned by the Nearby Search API, the `results` array is simply a list of `place` objects that describe the `place` in more detail. Because we've deliberately restricted our search to `minprice=3`, only one restaurant, [La Quercia](https://maps.app.goo.gl/F17CWFfRGFD5CK5i6?ref=blog.afi.io) (fantastic homestyle Italian food - you should try it if you ever visit Vancouver), is returned in the search results.

![Output of the Google Maps Nearby Search API](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2023/12/image-3.png)

`rating` Contains the place's rating, from 1.0 to 5.0, based on aggregated user reviews.

`user_ratings_total` is the total number of reviews, with or without text, for this place.

`price_level` is how expensive the restaurant is relative to others in the same general location. This value ranges from 0 (free) to 4 (very expensive).

`name` contains the human-readable name for the returned result, which for many businesses is simply the business name.

`vicinity` is the simplified address for the place, including the street name, street number, and locality, but not the province/state, postal code, or country.

💡

To view a comprehensive list of the fields returned by the Nearby Search API, consult the official documentation on [Nearby Search Place Attributes](https://developers.google.com/maps/documentation/places/web-service/search-nearby?ref=blog.afi.io#Place).

### Displaying Nearby Search API results

We'll use the output to display each `place` in the `results` array as a list item, ordered by distance from the user's current location like so:

![Output from the Google Maps Nearby Search API](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2023/12/Screen-Shot-2023-12-27-at-8.31.55-AM.png)

When the user clicks on the blue search button \[ 🔍 \], the frontend `App.js` in [osrm\_table\_api\_frontend](https://github.com/afian/osrm%5Ftable%5Fapi%5Ffrontend?ref=blog.afi.io):

```JSX
/* App.js (osrm_table_api_frontend) */
<button
  onClick={handleSearchPlaces}
  disabled={loading || !yourLocation.latitude}
>
  <img width={24} alt="Search places icon" src={SearchIcon} />
</button>;

```

calls the `handleSearchPlaces()` method,

```JSX
/* App.js (osrm_table_api_frontend) */
import searchPlaces from "./api/searchPlaces";
import { RATING_OPTIONS, RESTAURANT_PLACE_TYPE } from "./constants";

const handleSearchPlaces = async () => {
  try {
    setLoading(true);
    const placesRes = await searchPlaces(RESTAURANT_PLACE_TYPE, yourLocation);
    const placesWithDistance = await fulfillDistanceToPlaces(placesRes);
    setOriginalRestaurants(placesWithDistance);
    setAndSortRestaurants(placesWithDistance);
  } finally {
    setLoading(false);
  }
};

```

which in turn calls the `searchPlaces()` method imported from `/api/searchPlaces.js`.

```javascript
/* /api/searchPlaces.js (osrm_table_api_frontend) */
import { DEFAULT_SEARCH_PLACE_RADIUS } from "../constants";

export default async function searchPlaces(placeType, coord, radius) {
  try {
    // Constructing the API URL for searching places
    const apiUrl = `${process.env.REACT_APP_API_URL}/search-places?latitude=${
      coord.latitude
    }&longitude=${coord.longitude}&radius=${
      radius || DEFAULT_SEARCH_PLACE_RADIUS
    }&placeType=${placeType}`;

    // Making a fetch request to the API
    const response = await fetch(apiUrl);

    // Parsing and returning the JSON response
    return response.json();
  } catch (error) {
    // Logging an error message if there's an issue with the API request
    console.error("There was an error while searching places:", error);
  }
}

```

The function `searchPlaces()` requires three arguments: `placeType`, `coord`, and `radius`. The `placeType` parameter is a string with the value "restaurant", which is imported from `/constants/index.js`. The `coord` parameter is a tuple containing `{latitude, longitude}`, derived from the address provided in the autocomplete text box. Lastly, `radius` specifies the search radius in meters and is also imported from `/constants/index.js`."

The last step is to call our backend ([osrm\_table\_api\_backend](https://github.com/afilabs/osrm%5Ftable%5Fapi%5Fbackend?ref=blog.afi.io)) at the `/searchPlaces` endpoint:

```javascript
/* index.js (osrm_table_api_backend) */

app.get("/search-places", async (req, res) => {
  const { latitude, longitude, radius, placeType } = req.query;

  if (!latitude || !longitude || !placeType) {
    return res.status(400).send("Bad request");
  }

  try {
    const placesRes = await axios.get(
      `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${latitude},${longitude}&radius=${radius}&type=${placeType}&key=${process.env.GOOGLE_API_KEY}`,
    );
    res.status(200).json(placesRes.data?.results);
  } catch (err) {
    res.status(500).send("System error!");
  }
});

```

The code in `index.js` uses the well known async/await design pattern to call the [https://maps.googleapis.com/maps/api/place/nearbysearch](https://maps.googleapis.com/maps/api/place/nearbysearch?ref=blog.afi.io) endpoint, and by making the actual request to the Nearby Search API in the backend, we get to keep our `GOOGLE_API_KEY` private. 

Mapping the output of the Nearby Search API to our restaurant listing on the left hand side is straightforward: restaurant `name`, `rating`, `price_level` and `vicinity` can easily be found in the `place` object returned.

![Google Maps Nearby Search API place object](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2023/12/Screen-Shot-2023-12-30-at-6.33.33-PM.png)

The only complication comes with displaying the restaurant photo for each listing. The Nearby Search API returns a `photo_reference` string in the `photos` array of the `place` object, which looks something like "AWU5eFhNiFGhXgzY89b91eLP6RCfs-\_cobnSXN7DqKlyV6mkQKS".

![photos object for La Quercia restaurant in Vancouver](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2023/12/Screen-Shot-2023-12-30-at-6.50.32-PM.png)

The [PlacePhoto API](https://developers.google.com/maps/documentation/places/web-service/photos?ref=blog.afi.io) lets you retrieve a specific photo by making a single `GET` API call using the `photo_reference` string like this:

```bash
https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photo_reference=photo_reference&key=YOUR_API_KEY

```

Unfortunately, this means exposing your Google Maps API key on the frontend which is a big security risk. The way to deal with this is to have your frontend call the backend (here, we use the `/google-image` endpoint):

```JSX
/* App.js (osrm_table_api_frontend) */
<img
  className="left-image"
  alt="Address control icon"
  src={
    photos?.length > 0
      ? `${process.env.REACT_APP_API_URL}/google-image?photo_reference=${photos[0].photo_reference}&maxwidth=400`
      : RestaurantImg
  }
/>;
```

And in the backend, make a passthrough call to the PlacePhoto API and return the canonical image URL:

```javascript
/* index.js (osrm_table_api_backend) */

app.get("/google-image", async (req, res) => {
  try {
    const { photo_reference, maxwidth } = req.query;

    if (!photo_reference || !maxwidth) {
      return res.status(400).json({ error: "Missing required parameters" });
    }

    const imageUrl = `https://maps.googleapis.com/maps/api/place/photo?maxwidth=${maxwidth}&photo_reference=${photo_reference}&key=${GOOGLE_API_KEY}`;
    const response = await axios.get(imageUrl, { responseType: "arraybuffer" });

    if (response.headers["content-type"]) {
      res.header("Content-Type", response.headers["content-type"]);
    }

    res.send(response.data);
  } catch (error) {
    console.error("Error fetching image:", error.message);
    res.status(500).json({ error: "Internal server error" });
  }
});
```

### Ordering the restaurant listing by distance

As a final step, let’s go ahead and use the Table API to sort the restaurants in our list by distance to our current location.

First, we create a list of coordinates by concatenating the the **longitude, latitude** pair of our current location with the **longitude, latitude** pairs of the restaurants returned by the Nearby Search API. In the `fulfillDistanceToPlaces()` method of `App.js`:

```javascript
/* App.js fulfillDistanceToPlaces() (osrm_table_api_frontend) */

const coordinates = `${yourLocation.longitude},${
  yourLocation.latitude
};${restaurantList
  ?.map(
    (item) =>
      `${item?.geometry?.location?.lng},${item?.geometry?.location?.lat}`,
  )
  ?.join(";")}`;
```

With the `coordinates` array (here's an example of what it looks like) now in place,

![](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2024/01/Screen-Shot-2024-01-01-at-12.16.13-PM.png)

we can use the `getDistances()` method, which in turn directly accesses the OSRM Table API, as shown below:

```javascript
/* /api/getDistances.js (osrm_table_api_frontend) */

export default async function getDistances(coordinates) {
  try {
    const response = await fetch(
      `https://router.project-osrm.org/table/v1/car/${coordinates}?sources=0&destinations=all&annotations=distance`,
    );

    return response.json();
  } catch (error) {
    console.error("There was an error while fetching directions:", error);
  }
}

```

Because we've set `sources=0&destinations=all`, OSRM returns `distances`, a 1 x (*n* \+ 1) matrix (i.e. an array) that lists the distance from the origin location to the origin location (which by definition is 0), from the origin to the first restaurant, from the origin to the second restaurant and so on.

![](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2024/01/Screen-Shot-2024-01-01-at-12.26.09-PM.png)

We use this structure to inject the `distance` value (offsetting `distances` by 1 to account for the origin location which should be left out) into each restaurant in our list.

```javascript
/* App.js fulfillDistanceToPlaces() (osrm_table_api_frontend) */

return restaurantList.map((item, idx) => ({
  ...item,
  distance: distances[idx + 1],
}));
```

And finally, we sort the list of restaurants by distance to the origin and retrieve driving directions from our current (origin) location to the nearest one.

```javascript
/* App.js getDirectionToNearestRestaurant() (osrm_table_api_frontend) */

const sortedRestaurants = await _orderBy(restaurantList || [], "distance");
const res = await getDirection(yourLocation, {
  latitude: sortedRestaurants[0]?.geometry?.location?.lat,
  longitude: sortedRestaurants[0]?.geometry?.location?.lng,
});

```

All of this is handled in the `useEffect()` hook which runs each time the user interacts with the ⭐️ star rating and 💲 price point drop down menus, or hits the the blue search button \[ 🔍 \] button.

![The final result - OSRM driving directions to the nearest restaurant!](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2024/01/Screen-Shot-2024-01-01-at-12.41.01-PM.png)

The final result - driving directions to the nearest restaurant!

If everything works, the app will display a blue polyline from your current location to the nearest restaurant. 

### Summary and conclusion

This wraps up our four part series on the Open Source Routing Machine (OSRM). In this last tutorial, we learned how to use the [Google Maps Nearby Search API](https://developers.google.com/maps/documentation/places/web-service/search-nearby?ref=blog.afi.io) to return a list of nearby restaurants, and how to sort them by distance from our current location using the [OSRM Table API](https://project-osrm.org/docs/v5.5.1/api/?ref=blog.afi.io#table-service). Using these tools, we built a simple restaurant search app that guides the user to the nearest restaurant.

The OSRM APIs offer significant advantages. They are not only highly effective but also free to use if you host them on your own. These APIs are used in the technology stack of many major logistics and ridesharing companies, and provide a robust alternative to well-known services like Google Maps and Mapbox. I hope you found these tutorials as enjoyable and informative to read as I did in writing them.

Part 1: [Introduction to OSRM: Setting up osrm-backend using Docker](https://www.afi.io/blog/introduction-to-osrm-setting-up-osrm-backend-using-docker?ref=blog.afi.io)  
Part 2:[Hosting the OSRM API on EC2: Running osrm-backend as a web service](https://www.afi.io/blog/hosting-the-osrm-api-on-amazon-ec2-running-osrm-backend-as-a-web-service/?ref=blog.afi.io)  
Part 3: [OSRM Route API: Free turn by turn directions and polylines](https://afi.io/blog/osrm-route-api-free-directions-api-with-turn-by-turn-directions-and-polylines/?ref=blog.afi.io)  
Part 4: [OSRM Table API: Free distance matrix API ](https://afi.io/blog/osrm-table-api-free-and-open-source-distance-matrix-api/?ref=blog.afi.io)

👋 **As always, if you have any questions or suggestions for me, please** [**reach out**](https://yourls.afi.io/newcontact?ref=blog.afi.io) **or** [**say hello on LinkedIn**](https://www.linkedin.com/in/afian-anwar-a023b143/?ref=the-afi-labs-blog)**.**