How Tinder Built Their Own API Gateway

Hey Everyone!

Today we’ll be talking about

  • How Tinder Built Their Own API Gateway

    • What is an API Gateway

    • The Design of Tinder's API Gateway TAG

    • How a Request Flows Through TAG and the Middleware Involved

  • Tech Snippets

    • Mobile Developer Experience at Slack

    • Rust for JavaScript Devs

    • What Makes a Great Engineering Manager

    • How to Review Code as a Junior Developer

Plus, we have a solution to our last coding interview question on dynamic programming

How Tinder Built Their Own API Gateway

Tinder is the most popular dating app in the world with over 75 million monthly active users in over 190 countries. The app is owned by the Match Group, a conglomerate that also owns Match.com, OkCupid, Hinge and over 40 other dating apps.

Tinder’s backend consists of over 500 microservices, which talk to each other using a service mesh built with Envoy. Envoy is an open source service proxy, so an Envoy process runs alongside every microservice and the service does all inbound/outbound communication through that process.

For the entry point to their backend, Tinder needed an API gateway. They tried several third party solutions like AWS Gateway, APIgee, Kong and others but none were able to meet all of their needs.

Instead, they built Tinder Application Gateway (TAG), a highly scalable and configurable solution. It’s JVM-based and is built on top of Spring Cloud Gateway

Tinder Engineering published a great blog post that delves into why they built TAG and how TAG works under the hood.

We’ll be summarizing this post and adding more context.

What is an API Gateway

The API Gateway is the “front door” to your application and it sits between your users and all your backend services. When a client sends a request to your backend, it’s sent to your API gateway (it’s a reverse proxy).

The gateway service will handle things like

  • Authenticating the request and handling Session Management

  • Checking Authorization (making sure the client is allowed to do whatever he’s requesting)

  • Rate Limiting

  • Load balancing

  • Keeping track of the backend services and routing the request to whichever service handles it (this may involve converting an HTTP request from the client to a gRPC call to the backend service)

  • Caching (to speed up future requests for the same resource)

  • Logging

And much more.

The Gateway applies filters and middleware to the request to handle the tasks listed above. Then, it makes calls to the internal backend services to execute the request.

After getting the response, the gateway applies another set of filters (for adding response headers, monitoring, logging, etc.) and replies back to the client phone/tablet/computer.

Tinder’s Prior Challenges with API Gateways

Prior to building TAG, the Tinder team used multiple API Gateway solutions with each application team picking their own service.

Each gateway was built on a different tech stack, so this led to challenges in managing all the different services. It also led to compatibility issues with sharing reusable components across gateways. This had downstream effects with inconsistent use for things like Session Management (managing user sign ins) across APIs.

 Therefore, the Tinder team had the goal of finding a solution to bring all these services under one umbrella.

They were looking for something that

  • Supports easy modification of backend service routes

  • Allows for Tinder to add custom middleware logic for features like bot detection, schema registry and more

  • Allows easy Request/Response transformations (adding/modifying headers for the request/response)

The engineering team considered existing solutions like Amazon AWS Gateway, APIgee, Tyk.io, Kong, Express API Gateway and others. However, they couldn’t find one that met all of their needs and easily integrated into their system.

Some of the solutions were not well integrated with Envoy, the service proxy that Tinder uses for their service mesh. Others required too much configuration and a steep learning curve. The team wanted more flexibility to build their own plugins and filters quickly.

Tinder Application Gateway

The Tinder team decided to build their own API Gateway on top of Spring Cloud Gateway, which is part of the Java Spring framework.

Here’s an overview of the architecture of Tinder Application Gateway (TAG)

The components are

  • Routes - Developers can list their API endpoints in a YAML file. TAG will parse that YAML file and use it to preconfigure all the routes in the API.

  • Service Discovery - Tinder has a bunch of different microservices in their backend, as they use Envoy to manage the service mesh. The Envoy proxy can be run on every single microservice and it handles the inbound/outbound communications for that microservice. Envoy also has a control plane that manages all these microservices and keeps track of them. TAG uses this Envoy control plane to look for the backend services for each route.

  • Pre Filters - Filters that you can configure in TAG to be applied on the request before it’s sent to the backend service. You can create filters to do things like modify request headers, convert from HTTP to gRPC, authentication and more.

  • Post Filters - Filters that can be applied on the response before it’s sent back to the client. You might configure filters to look at any errors (from the backend services) and store them in Elasticsearch, modify response headers and more.

  • Custom/Global Filters - These Pre and Post filters can be custom or global. Custom filters can be written by application teams if they need their own special logic and are applied at the route level. Global filters are applied to all routes automatically.

Real World Usage of TAG at Tinder

Here’s an example of how TAG handles a request for reverse geo IP lookup (where the IP address of a user is mapped to his country).

  1. The client sends an HTTP Request to Tinder’s backend calling the reverse geo IP lookup route.

  2. A global filter captures the request semantics (IP address, route, User-Agent, etc.) and that data is streamed through Amazon MSK (Amazon Managed Kafka Stream). It can be consumed by applications downstream for things like bot detection, logging, etc.

  3. Another global filter will authenticate the request and handle session management

  4. The path of the request is matched with one of the deployed routes in the API. The path might be /v1/geoip and that path will get matched with one of the routes.

  5. The service discovery module in TAG will use Envoy to look up the backend service for the matched API route.

  6. Once the backend service is identified, the request goes through a chain of pre-filters configured for that route. These filters will handle things like HTTP to gRPC conversion, trimming request headers and more.

  7. The request is sent to the backend service and executed. A backend service will send a response to the API gateway.

  8. The response will go through a chain of post-filters configured for that route. Post filters handle things like checking for any errors and logging them to Elasticsearch, adding/trimming response headers and more.

  9. The final response is returned to the client.

The Match Group owns other apps like Hinge, OkCupid, PlentyOfFish and others. All these brands also use TAG in production. 

For more details, you can read the full blog post here.

How did you like this summary?

Your feedback really helps me improve curation for future emails.

Login or Subscribe to participate in polls.

Build Better Software with Continuous Delivery

Continuous delivery is a defining principle in modern app development. Rapidly shipping new features safely is a massive advantage, and getting real-time user feedback makes it possible to double down on impactful features.

Iterative development is changing the way companies operate, and industry leaders have invested tens of millions of dollars trying to find the best solution for A/B testing, feature flags, rollback controls, canary releases and more.

LaunchDarkly does all of the above. Quickly integrate continuous delivery best practices into your own software development process, easily deploy to production, and run focused, effective experiments to better understand how users are interacting with your software.

Discover a universe of continuous delivery and feature management content with LaunchDarkly or schedule today to see our platform in action..

sponsored

Tech Snippets

  • Mobile Developer Experience at Slack - This is a great blog post by Slack Engineering on how they designed a fantastic developer experience for Mobile devs. Not having a great developer experience means wasting a lot of engineering time on CI, testing, building, and more. This lost time can translate to millions of dollars lost if you have a large engineering team. Slack details how they minimized CI time, flaky tests, merge conflicts and more in this post.

  • From JavaScript to Rust - If you’re a JavaScript developer and are interested in learning Rust, this is a really awesome free book that teaches Rust from the perspective of JavaScript. It maps common JavaScript and Nodejs workflows to the Rust ecosystem. You can download the book here.

  • What Makes a Great Engineering Manager - Dave Rensin is a former Engineering Director at Google. In this interview, he talks about what makes a great EM (engineering manager), how to interview for an EM position and tips for people who are transitioning into EM.

  • How to Review Code as a Junior Developer - This is an awesome post on the Pinterest Engineering blog about how junior developers should do code reviews. Many junior devs may think their feedback (especially when given to developers who are more senior) is a waste of time, but that’s not the case! Several benefits from reviewing code as a junior developer are

    • You learn the code base more quickly

    • You build a feedback circle

    • You take more ownership over the codebase

Interview Question

Implement a BST Iterator class that represents an iterator over the in-order traversal of a Binary Search Tree.

Implement the following methods

  • BSTIterator - constructor. The root of the BST will be passed in as a parameter. The pointer should be initialized to a non-existent number small than any number in the BST.

  • hasNext - Returns true if there exists a number in the traversal to the right of the pointer, otherwise returns false.

  • next - Moves the pointer to the right, then returns the number at the pointer.

Previous Question

As a reminder, here’s our last question

You’re given an integer N as input.

Write a function that determines the fewest number of perfect squares that sum up to N.

Examples

Input - 16

Output - 1 (16 is a perfect square)

Input - 21

Output - 3 (1 + 4 + 16 = 21)

Solution

Your first instinct might be to go with the greedy approach.

Find the largest perfect square that is smaller than N and subtract that. Then, recursively do the same on whatever number is left.

The base case is when N = 0.

However, the greedy approach doesn’t work.

A counter-example is 18.

The largest square that is smaller than 18 is 16.

Then, you’re left with 2 so that would be 2 ones.

The greedy approach will give you an answer of 3, from 16 + 1 + 1.

But, 3 is incorrect. The correct answer is 2, from 9 + 9.

So, we’ll have to try every possible combination of squares.

We’ll subtract every perfect square that is smaller than N and call our function recursively on the difference.

Every function call will return the fewest number of perfect squares needed to reach 0 (our base case) and we’ll keep track of the minimum.

Then, we’ll return the minimum + 1 (since we subtracted by one perfect square in our function).

This approach has a ton of repeated computations, so we can use dynamic programming to improve our time complexity.

One way is top down DP with memoization.

We’ll implement a bottom up DP solution by iteratively building up a table that contains the fewest number of perfect squares to sum all the numbers from 1 to N.