Skip to content

Small project to demonstrate best practice external API integration + SSR in a web application.

Notifications You must be signed in to change notification settings

ryanmohamed/restaurant-roulette

Repository files navigation

This is a Next.js project bootstrapped with create-next-app.

Restaurant Roulette

Stuck on what to order? Use Restaurant Roulette for a quick recommendation based on your area's local reviews.

Getting Started

Install dependencies:

npm install

First, run the development server:

npm run dev
# or
yarn dev
# or
pnpm dev

Open http://localhost:3000 with your browser to see the result.

Notes

Client-side API requests.

  1. ipify public API is used in our case for simple, and unlimited retrieval of client IPs. (As opposed to extracting client IP from requests to the web server. This method is more robust and utilizes the services provided by ipify)
  2. ip-api public API is a great alternative to paid IP serives. All we need is latitude and longitude (for future location based API calls), however ip-api provides some extra data for zero cost and with zero limitation. (This is an alternative to navigator.geolocation to emphasize API use)

Server-side API requests.

  1. yelp public API provides the basis of our restuarant data with location directly influencing it. However it's use is limited and authenticated with an API key. This communication should be kept away from the client entirely to ensure no requests to yelp in ill intent.
    1. Client requests restaurants from Server.
    2. Server requests yelp API.
    3. Server parses and returns cleaned restuarant data as response to Client.
    4. Client renders data as props on Page.
  2. Google geocode API provides latitude and longitude conversions for real addresses.
    1. Client initiates requests to web server.
    2. Web server makes requests to Google API.
    3. Web server returns data for client to render and update UI.
  3. Google Map JavaScript API used in @react-google-maps/api, not much work done by except for event listeners and creating an API key.
    1. IMPORTANT NOTE: Because the service is only accessible with a billing account, I had to add one. However the second it goes over 10 cents, I have set up a Pub-Sub topic which triggers a cloud function to disable billing entirely. Big thank you to the resources provided by (this video)[https://www.youtube.com/watch?v=KiTg8RPpGG4&ab_channel=DataSlayer]

API Request Optimizations.

  1. yelp Naturally the free tier of the yelp API employs a daily 500 request limit. We are mainly performing 2 kinds of API requests to the yelp API.
    1. Initial search request (SSR): yields a SearchResponseType JSON object with an array of BusinessType objects. The provides enough data to render business information on the server side and send it to the client. 1 request per search.
    2. Supplemental Information Request: (Proxied API request): Because the client renders one "page" of information from some BusinessType[] in a component at a time, we need only request the current business's supplemental information. 1 request per next page click.
      1. Because we are requesting whenever the component's page state changes, we need to consider when we navigate backwards with prev page clicks.
        • Solution: Check for cached restaurant data before making request on state change.
      2. Any user would naturally rapidly click through the next page button causing a ton of requests from our server to yelp.
        • Solution: Delay/debounce the API request by n milliseconds and clear any previous requests that were waiting to be made. Requests are only made when the component has "sat" for n milliseconds.

Further Considerations.

  1. When we perform our search we gather top-level information about many restaurants.
  2. We have the data both server side and client side, but only need to render one restaurant at a time.
  3. Each restaurant should asynchronously fetch supplementary information about itself like reviews, menu items, etc. We do not want to fetch supplementary information all at once, only fetch when needed.
  4. Since our application uses one mounted component with some restaurant data state, we only need to fetch the data when the state is changed. However an issue lies in refetching data when we navigate to restaurants we've already seen, cache the supplementary information and only retrieve the data from API for newly seen Restaurants.
  5. After performing a search, the user has the ability to navigate through

Thought Process

Before we start requesting from the Yelp API, whether that be directly on the client-side or proxied through the server, we need the user's location.

Many APIs provide this service. Our main concern is how do we manage this data? Some points:

It's client specific information so we can maintain some latititude and longitude state in React Context. Since location can be found with some great free APIs we'll keep that logic neatly coupled client side.

Yelp requires our application API Key, so we'll use the web app's server as a proxy when retrieving data. This also aids in keeping our dynamic routes rendered on the server side.

Vulnerabilities

  1. A recently found vulnerability stemming from semver. Since this is a small project I will overlook this since it's a recently posted issue found here.
  2. jest brings along 23 vulnerabilities of it's own, keep this in mind when cloning.

Learn More

To learn more about Next.js, take a look at the following resources:

You can check out the Next.js GitHub repository.