How I Built a Data Discovery API for AWS Data Lake
Tags: aws, serverless, bigdata, api
Published: 2021-04-13 14:22:09

This simple FastAPI service will help you find data in a data lake

![data discovery](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/discovery-image.jpg)

Photo by [nine koepfer](https://unsplash.com/@enka80?utm_source=medium&utm_medium=referral)

Data lakes provide a myriad of benefits. They are data agnostic and don't require you to define a schema upfront. However, without a proper structure, it may be challenging to find the data that you need. In this article, we'll address this problem by creating a FastAPI abstraction allowing us to query the AWS Glue metadata catalog.

* * * * *

### **Why FastAPI for a data discovery service?**

If we would implement it as a Python package instead, we would assume that everyone knows enough Python to use it. However, your company may have other developers (Java, C#, JavaScript, Go, ...) that need access to data from a data lake. By building a REST-ful service for data discovery, you are providing a **programming-language agnostic** interface for everyone. This **API-first** approach has further **benefits**:

-   If you later decide that you want to build a **UI** for business users, your task will become much easier since you already have a REST API that provides data as JSON.
-   You can combine all the functionality for this purpose in a** single service**.
-   If you need to add new methods or change the existing ones, you can deploy **changes** easier by creating a **new API version **(ie, api_url/v3) without breaking any workloads that depend on the current version.
-   You can **extend the functionality** to include real-time data streams, unstructured data, as well as to allow querying access logs to identify which data is used most frequently and by which users.

* * * * *

### **How do we start?**

We can start by listing all the methods that can be potentially useful for data discovery:

-   listing all databases or **schemas**,
-   listing all **tables** for a specific schema,
-   filtering for table names with a specific **prefix** or **suffix**,
-   doing a **full-text search** on table names (say, all tables that have anything to do with orders or customers),
-   doing a **full-text search** on table comments (ex., if we want to find any column related to *logistics*),
-   showing a **full table definition** (*all columns, data types, and column comments*),
-   showing **partitions** for a specific table,
-   showing **all files** from S3 below a specific partition,
-   showing all columns with specific **comment**,
-   showing all tables that contain a specific **column name**,
-   finally, the best way for data discovery is to just **look at the first X rows** of a full table.

* * * * *

### **How can we translate this to Python code?**

We have three choices: either using lower-level AWS SDKs, doing queries on Athena's information_schema, or leveraging awswrangler. In the end, we may combine all of them to satisfy the requirements from the previous section.

1 - To list all databases (*i.e., schemas*) and tables, we can use the **awswrangler** package, especially the module wr.catalog.

![awswrangler package setup](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/awswrangler-package.png)

2 - To filter for table names with a specific **prefix** or **suffix**, we can also use wr.catalog.tables. The same is true for retrieving **table definition** and doing a **full-text search** on those definitions:

![wr catalog tables](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/wrcatalog.png)

3 - List all **partitions** of a specific table:

![listing partitions](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/listing-partitions.png)

4 - Show table's DDL:

![show table ddl](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/table-ddl.png)

5 - **List S3 **directory **path** or show specific objects according to file type (*ex. parquet files*) and last modified date:

![](https://lh4.googleusercontent.com/5n_OHLPCcU-6d_Ui2FQzH7HXURZRDiu85qFJsRIt6dFDA8PtH8OgfJxBPgqnUDfy-kN8gZFQQCeLDFIOI6JGJ9EfJ4xt77gSvcBeUL4OJPhMQbqSS-xjlFPio9mFOYlnXLCWPLom)

6 - Query **column comments** to find the dataset that you need:

![](https://lh5.googleusercontent.com/qsmT_wya5Td_j7FgPJvBodfJbSqJCNYfg19NZ6EcjSqGpSLNTYYD-tchygiK16ID1UiGLJIc-61v1u2WHqtLq7f1bmjUZc3WYoEipjinQWOP-bgCMSMZ_j4uxcPtx6jrsrKujCAr)

7 - Describe Athena **table definition**:

![](https://lh4.googleusercontent.com/kk_b6MYUOxsyO-2ToZeLchz5vpcHkDK2cnhyP8EL_RwUq3QhINziHd91g1qhQPSE6DqPjJarN11PdnJyYnRiG9TkQaXeVP3IUc-wJ5kUOJ-ZnuP-allFTq6MKBKjDXvuqUHI7GQr)

8 - Search for specific **column names**:

![search column names](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/search-column-names.png)

The above methods constitute a basic MVP for a data discovery service. It allows querying schemas, tables, columns, column comments (*aka data dictionary*), showing a preview of the data, as well as exploring the underlying files, directories, and partitions. 

By leveraging services such as AWS X-Ray and Lake Formation, we could add methods to query usage access patterns and identify the most and the least used datasets.

**Side note:** If you don't want to attach your boto3_session separately in each API method, you can attach a global session using:

![aws attach global session in API method](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/aws-attach-global-session.png)

* * * * *

### **Implementation: FastAPI MVP**

The full code for this MVP can be found in [this Github repository](https://github.com/anna-anisienia/data-discovery-api). Let's briefly discuss some of the details.

In the following demo, we can see all the endpoints applied on a [Brazilian E-commerce dataset from Kaggle](https://www.kaggle.com/olistbr/brazilian-ecommerce).

The screencast above demonstrates how we can use this API to discover and query e-commerce data stored in an S3 data lake. The end users can identify tables or schemas that they need. By drilling down into the underlying files, data scientists and analysts can explore this data in an interactive notebook or other tools of their choice. Reading a specific parquet, CSV, Excel, or JSON file from S3 with awswrangler is as simple as:

![awswrangler request](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/awswrangler-request.png)

* * * * *

### **How can we serve this API at scale?**

There are several options to deploy a REST API. If you already use AWS, you may find it useful to leverage serverless services built specifically for building resilient and scalable APIs: [**AWS Lambda**](https://dashbird.io/knowledge-base/aws-lambda/introduction-to-aws-lambda/) and [**API Gateway**](https://dashbird.io/knowledge-base/api-gateway/pros-and-cons-of-using-an-api-gateway/). 

With the excellent package called mangum, converting our FastAPI to a Lambda handler is as simple as importing this package (*from mangum import Mangum*) and adding a single line of code: handler = Mangum(app=app).

The Dockerfile to deploy the API to AWS Lambda looks as follows:

![installing packages](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/install-python-packages.png)

We use the official Python Lambda image with Python 3.8 and **install** all required **packages**. Then, we copy our remaining API code and specify the **Lambda handler** that will be used as an entry point to our container.

Finally, we have to push the container to ECR --- AWS container registry. To replicate this demo, replace 123456789 with your AWS account ID, and adjust your AWS region name. In case you're wondering: dda is my abbreviation for data-discovery-api.

![pushing container to ecr](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/push-container-ecr.png)

#### **AWS Lambda**

The container image is deployed to ECR. Now, we need to **create a Lambda function**. We choose the container image option and select our ECR image.

![create lambda function from container image](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/create-lambda-function-from-image.png)

Since querying some tables can take longer than Lambda's default **timeout** of just three seconds, we can extend it to three minutes.

![lambda timeout settings](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/awslambda-basic-function-settings.png)

Finally, we need to attach IAM policies to grant our Lambda function permissions to retrieve data from AWS Glue, Athena, and S3 (*of course, we should use more granular permissions for production*).

![lambda permission policies](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/lambda-permission-setting.png)

#### **API Gateway**

After configuring the Lambda function with our container image, we can create a new API in the API Gateway console. We need to choose REST API and specify a name.

![aws create new api gateway](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/aws-create-new-api.png)

Then, we can add a method and resource to configure Lambda Proxy integration.

![lambda proxy setting](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/awslambda-proxy-setting.png)

Once all that is set up, we can deploy the API and start serving it. 

To see it step by step, here is a screencast that demonstrates API Gateway configuration, deployment, and testing the API:

* * * * *

### **How can we monitor our data discovery service?**

Imagine that you deployed a similar service at your company and it turned out to be a resounding success. Now you need to ensure that it stays **healthy**, can be easily **maintained**, and runs **without errors**. At this point, you can either:

1.  Keep an eye on it manually (*not recommended*),
2.  Build an **observability dashboard** yourself by using AWS CloudWatch and related services,
3.  Make use of [observability tools such as Dashbird](https://dashbird.io/).

**The implications of each option:**

-   With option #1, you will likely go mad. 
-   With option #2, you would have to spend extra time building CloudWatch dashboards, jump between several CloudWatch logs and multiple AWS console tabs to fix any potential bugs. 
-   With option #3, you only have to sign in to your [Dashbird](https://dashbird.io/) account --- it automatically scans all serverless resources and provides you with visualizations you need to track the health of your APIs and microservices. You can then optionally (*highly recommended!*) build a **project dashboard** by combining API Gateway and Lambda resources, and **set alerts **on failure. 

![dashbird identifying cold starts](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/dashbird-monitoring-coldstarts.png)

In the image above, we can see how Dashbird pointed out the task which timed out after 3 seconds (*default Lambda timeout*). It also reveals when the **Lambda** function experienced a [cold start](https://dashbird.io/knowledge-base/aws-lambda/cold-starts/) and shows the duration for each API call. 

For **API Gateway**, we can see the average latency, number of API requests, and different types of errors that we encountered in the initial testing of the API.

![monitoring api gateway issues](https://mk0dashbirdioprthk8x.kinstacdn.com/wp-content/uploads/2021/04/dashbird-detecting-apigateway-issues.png)

* * * * *

### **Conclusion**

In this article, we investigated how we can build a REST-ful service for data discovery. By leveraging Python libraries such as FastAPI, awswrangler, boto3, and Mangum, we can build useful APIs in just a few hours rather than weeks. Additionally, by deploying this service to API Gateway and Lambda, we can serve this API at scale with no operational headaches. Lastly, by leveraging [Dashbird](https://dashbird.io/), you can add observability to your serverless resources without having to install any CloudWatch agents, pulling logs, or building any dashboards. 

**Thank you for reading! If this article was useful, have a look at related articles:**

-   [Deploying AWS Lambda with Docker Containers: I Gave it a Try and Here's My Review](https://dashbird.io/blog/deploying-aws-lambda-with-docker/)
-   [7 Reasons Why You Should Consider a Data Lake (and Event-Driven ETL)](https://dashbird.io/blog/7-reasons-why-you-should-consider-a-data-lake/)