The concept behind the project that we will build in this chapter is a simple one: we want users to be able to generate random recommendations for things to do in specific geographical locations based on a predefined set of journey types that we will expose through the API. We will give our project the codename Meander.
Often on projects in the real world, you are responsible for the full stack; somebody else builds the website, a different person still might write the iOS app, and maybe an outsourced company builds the desktop version. On more successful API projects, you might not even know who the consumers of your API are, especially if it's a public API.
In this chapter, we will simulate this reality by designing and agreeing a minimal API design with a fictional partner up front before going on to implement the API. Once we have finished our side of the project, we will download a user interface built by our teammates to see the two work together to produce the final application.
In this chapter, you will:
http.Get
to make external API requests, specifically to the Google Places API, with no code bloatmath/rand
package makes it easy to select an item from a slice at randomhttp.Request
typeFollowing Agile methodologies, let's write two user stories that describe the functionality of our project. User stories shouldn't be comprehensive documents describing the entire set of features of an application; rather small cards are perfect for not only describing what the user is trying to do, but why. Also, we should do this without trying to design the whole system up front or delve too deep into implementation details.
First we need a story about seeing the different journey types from which our users may select:
As a |
traveler |
I want |
to see the different types of journeys I can get recommendations for |
So that |
I can decide what kind of evening to take my partner on |
Secondly, we need a story about providing random recommendations for a selected journey type:
As a |
traveler |
I want |
to see a random recommendation for my selected journey type |
So that |
I know where to go, and what the evening will entail |
These two stories represent the two core capabilities that our API needs to provide, and actually ends up representing two endpoints.
In order to discover places around specified locations, we are going to make use of the Google Places API, which allows us to search for listings of businesses with given types, such as bar
, café
, or movie_theater
. We will then use Go's math/rand
package to pick from those places at random, building up a complete journey for our users.
The Google Places API supports many business types; see https://developers.google.com/places/documentation/supported_types for the complete list.
In order to turn our stories into an interactive application, we are going to provide two JSON endpoints; one to deliver the kinds of journeys users will be able to select in the application, and another to actually generate the random recommendations for the selected journey type.
GET /journeys
The above call should return a list such as the following:
[ { name: "Romantic", journey: "park|bar|movie_theater|restaurant|florist" }, { name: "Shopping", journey: "department_store|clothing_store|jewelry_store" } ]
The name
field is a human-readable label for the type of recommendations the app generates, and the journey
field is a pipe-separated list of supported journey types. It is the journey value that we will pass, as a URL parameter, into our other endpoint, which generates the actual recommendations:
GET /recommendations? lat=1&lng=2&journey=bar|cafe&radius=10&cost=$...$$$$$
This endpoint is responsible for querying the Google Places API and generating the recommendations before returning an array of place objects. We will use the parameters in the URL to control the kind of query to make as per the HTTP specification. The lat
and lng
parameters, representing latitude and longitude, respectively, tell our API where in the world we want recommendations from, and the radius
parameter represents the distance in meters around the point in which we are interested in. The cost
value is a human-readable way of representing the price range for places that the API returns. It is made up of two values: a lower and upper range separated by three dots. The number of dollar characters represents the price level, with $
being the most affordable and $$$$$
being the most expensive. Using this pattern, a value of $...$$
would represent very low cost recommendations, where $$$$...$$$$$
would represent a pretty expensive experience.
An example payload for this call might look something like this:
[ { icon: "http://maps.gstatic.com/mapfiles/place_api/icons/cafe-71.png", lat: 51.519583, lng: -0.146251, vicinity: "63 New Cavendish St, London", name: "Asia House", photos: [{ url: "https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference=CnRnAAAAyLRN" }] }, ... ]
The array returned contains a place object representing a random recommendation for each segment in the journey, in the appropriate order. The preceding example is a café in London. The data fields are fairly self-explanatory; the lat
and lng
fields represent the location of the place (they're short for latitude and longitude), the name
and vicinity
fields tell us what and where the business is, and the photos
array gives us a list of relevant photographs from Google's servers. The vicinity
and icon
fields will help us deliver a richer experience to our users.