Connecting FastAPI with Astra NoSQL DB for High-Performance APIs: A Comprehensive Guide
Build High Performance APIs with FastAPI and Astra DB Datastax
In the current digital landscape, the development of APIs that exhibit swift and seamless operation while being capable of scaling in response to the demands of contemporary applications is of utmost importance. APIs that can handle vast quantities of data and requests without compromising their performance are critical for ensuring the provision of high-quality services and applications to users.
This article aims to provide an extensive guide for developers on how to integrate Astra DB with FastAPI, enabling them to create APIs that meet high-performance standards. Astra DB is a NoSQL database built on the Apache Cassandra framework and designed to manage large volumes of data while offering fast read and write performance. In contrast, FastAPI is a modern web framework that utilizes asynchronous programming to achieve high scalability and optimal performance.
By combining Astra DB with FastAPI, developers can create APIs that are not only fast and scalable but also reliable and easily maintained. The article provides detailed instructions on how to connect Astra DB with FastAPI and also offers advice and guidelines for optimizing the efficiency and dependability of your API. Whether you are an experienced developer or a novice, this guide provides the knowledge and resources necessary to develop high-performance APIs that meet the needs of contemporary applications.
Overview of Astra DB and FastAPI
Astra DB is a cloud-native NoSQL database that is designed to handle massive amounts of data and provide high-speed read and write performance. It is built on Apache Cassandra, a highly scalable and distributed database system, and provides a fully managed, serverless experience to developers, which allows them to focus on building their applications without worrying about database management.
Astra DB offers a range of key features and benefits that are useful for modern applications, including;
Horizontal scalability enables it to handle millions of requests per second and petabytes of data. This makes it ideal for applications that require high scalability and availability.
Astra DB provides low read and writes latencies, making it suitable for real-time data access and analysis.
Its multi-cloud capability also allows it to be used across multiple cloud providers, including Amazon Web Services, Microsoft Azure, and Google Cloud Platform, which makes it easy to deploy and manage your database in the cloud.
Overall, Astra DB's combination of scalability, low latency, and multi-cloud support provides developers with a powerful tool to build modern, high-performance applications that can handle large amounts of data and requests. Its fully managed, serverless experience further simplifies database management, allowing developers to focus on their core business logic and application development.
FastAPI is a contemporary web framework that facilitates the construction of APIs using Python, utilizing asynchronous programming to achieve superior performance and scalability. It offers a straightforward way for building APIs, with features like automatic validation of request data, automatic generation of API documentation, and seamless integration with databases such as Astra DB.
FastAPI has several essential features and benefits that include:
High performance: FastAPI's asynchronous programming approach ensures high performance and scalability, which makes it an ideal choice for applications that require fast and responsive APIs.
User-friendly development: FastAPI provides a simple and user-friendly API for building APIs, with features like automatic validation of request data and automatic generation of API documentation that make it simple to develop and maintain APIs.
Integration with other tools: FastAPI can easily integrate with other Python tools and libraries, such as Pydantic for data validation and SQLAlchemy for database integration. This enhances the capability of FastAPI to integrate with third-party libraries and tools to improve API functionality.
Prerequisites
To successfully connect FastAPI with Astra DB, you must first ensure that you have met the following prerequisites:
Astra DB Account: To utilize Astra DB, you must first create an Astra DB account. Fortunately, Astra DB offers a free account that provides users with 5 GB of storage and 40 million read/write operations per month. Click here to signup
Python and pip: FastAPI requires Python 3.6 or later to be installed on your system, as well as pip, the package installer for Python. You can acquire the most recent version of Python by visiting the official website.
FastAPI and Required Packages: A basic understanding of how FastAPI and its standard dependencies work will be helpful.
Once you have these prerequisites in place, you're ready to create a database, connect Astra DB with FastAPI, and start building high-performance APIs.
Create a Database with Astra DB
In this section, you're going to create your database. Jump into the Astra DB Datastax console and click on "Create Database".
Next, fill out the database creation form.
Database Name: The name of the database(blog-db in this case).
Keyspace Name: A keyspace stores your group of tables, like a schema in a relational database. This helps keep related tables of information in a single place within your database. You can have multiple keyspace names with multiple tables. This is usually not the case with SQL databases. Visit here to read more.
Provider: Select a cloud provider that you want to host your database on. Google Cloud appears to be a free provider at the time of the writing, so I'd opt for it.
Region: Based on the selected provider you will get a list of available regions to select from. Select a free region that's closer to you. you need to upgrade to have access to all available regions.
Upon filling out the form to your taste, click on the Create Database button to create your database. Afterward, you will be redirected to your databases dashboard. Wait until the pending
status turns active
.
You've successfully created an active NoSQL database with Astra DB.
Connection Bundle and Secret Token
To connect the database to your FastAPI application, you need to use the secure connection bundle and a secret token provided by Astra DB.
To obtain these, go to the "Connect" tab on your database dashboard. Here, you will find options to generate a secure token and download the secure connection bundle. These measures will allow you to establish a secure connection between your FastAPI application and your Astra DB database.
Click on the Generate Token
button,
Download or copy the generated token to a safe and secure location. You'll be using it soon
Click on the Get Bundle
button,
Select your region and download the connection bundle to your local machine.
If you scroll further below you'll see that Astra DB provides different ways and instructions on how to connect based on a specific programming language. In the next section, you'll see how to adapt the instruction for Python to function specifically for FastAPI using the Python Cassandra driver and FastAPI Lifespan Mechanism.
Configure FastAPI and ASTRA DB
Now that you have a secure connection bundle and a secret token let's see how to use it in our FastAPI application.
Setup Basic FastAPI Project
Using your preferred IDE create a project directory for a blog application. The folder should contain the following files, main.py
, database.py
, models.py
, config.py
, requirement.txt
and .env
Next, create a connect-bundle
folder and copy the connection bundle zipped file provided by ASTRA DB into it.
Install requirements
Add the following text to the requirements.txt
file and then run the command below.
fastapi[all]
uvicorn
cassandra-driver
Command;
$ pip install -r requirements.txt
Set Environment Variables
It's bad software practice to hardcode sensitive data and secret variables into your code. Hence, .env
files contain credentials in key-value format for services used by the program. They're meant to be stored locally and not uploaded to code repositories online.
Add the following text to the .env
file;
ASTRADB_KEYSPACE=<keyspace>
ASTRADB_CLIENT_SECRET=<secret>
ASTRADB_CLIENT_ID=<clientId>
ASTRADB_TOKEN=<token>
replace the following with the actual value given to you after token generation in the "Connection Bundle and Secret Token" section above.
Project Configuration
Config files are used to store key-value pairs or some configurable information that could be read or accessed in the code at some point.
Add the following text to the config.py
file;
import os
from functools import lru_cache
from pathlib import Path
from pydantic import BaseSettings, Field
os.environ['CQLENG_ALLOW_SCHEMA_MANAGEMENT'] = "1"
class Settings(BaseSettings):
base_dir: Path = Path(__file__).resolve().parent
keyspace: str = Field(..., env='ASTRADB_KEYSPACE')
db_client_id: str = Field(..., env='ASTRADB_CLIENT_ID')
db_client_secret: str = Field(..., env='ASTRADB_CLIENT_SECRET')
class Config:
env_file = '.env'
@lru_cache
def get_settings():
return Settings()
os
: provides a way of using operating system-dependent functionality.functools
: provides alru_cache
decorator to cache the function results.pathlib
: provides an object-oriented approach to work with files and directories.pydantic
: provides data validation and settings management using Python type annotations.
The code does the following:
Sets the environment variable
CQLENG_ALLOW_SCHEMA_MANAGEMENT
to1
.Defines a class
Settings
that inherits fromBaseSettings
, a class provided bypydantic
.The class has three attributes:
base_dir
: aPath
object representing the directory containing the script file.keyspace
: a required string attribute that can be set through the environment variableASTRADB_KEYSPACE
.db_client_id
: a required string attribute that can be set through the environment variableASTRADB_CLIENT_ID
.db_client_secret
: a required string attribute that can be set through the environment variableASTRADB_CLIENT_SECRET
.
The class also defines a nested
Config
class that sets the path to the environment variables file.env
.Defines a function
get_settings
decorated withlru_cache
that returns an instance of theSettings
class. Thelru_cache
decorator caches the result of the function, avoiding expensive computations if the function is called with the same arguments again.
Database Connection
Add the following code to the database.py
file;
import pathlib
from cassandra.cluster import Cluster
from cassandra.auth import PlainTextAuthProvider
from cassandra.cqlengine import connection
import config
BASE_DIR = pathlib.Path(__file__).resolve().parent
SOURCE_DIR = BASE_DIR / 'connect-bundle'
settings = config.get_settings()
ASTRADB_CONNECT_BUNDLE = BASE_DIR / SOURCE_DIR / "secure-connect-blog-db.zip"
ASTRADB_CLIENT_ID = settings.db_client_id
ASTRADB_CLIENT_SECRET = settings.db_client_secret
def get_session():
cloud_config= {
'secure_connect_bundle': ASTRADB_CONNECT_BUNDLE
}
auth_provider = PlainTextAuthProvider(ASTRADB_CLIENT_ID, ASTRADB_CLIENT_SECRET)
cluster = Cluster(cloud=cloud_config, auth_provider=auth_provider)
session = cluster.connect()
connection.register_connection(str(session), session=session)
connection.set_default_connection(str(session))
return session
This is a Python code that defines a function get_session()
and sets up a connection to the database using the following libraries:
cassandra.cluster
: provides the Cluster class to connect to a Cassandra cluster.cassandra.auth
: provides the PlainTextAuthProvider class for authentication.cassandra.cqlengine.connection
: provides the connection module for the CQL Engine.
The code does the following:
Defines a constant
BASE_DIR
that points to the directory containing the script file.Defines a constant
SOURCE_DIR
that points to a directory namedconnect-bundle
in theBASE_DIR
.Calls
config.get_settings()
to retrieve the database client ID and client secret from the environment variables.Defines a constant
ASTRADB_CONNECT_BUNDLE
that points to a file namedsecure-connect-blog-db.zip
in theSOURCE_DIR
.Defines constants
ASTRADB_CLIENT_ID
andASTRADB_CLIENT_SECRET
using the values obtained fromget_settings()
.Defines a function
get_session()
that returns a session connected to the database:Sets up a dictionary
cloud_config
containing the location of the secure connect bundle file.Creates an instance of
PlainTextAuthProvider
using the client ID and client secret.Creates a
Cluster
object passing the cloud configuration and authentication provider objects.Creates a session object using the
connect()
method of theCluster
object.Registers the session with
cqlengine.connection
module.Sets the default connection to the created session.
Returns the session object
Database Model
A database model is a way to represent the structure and relationships of data in a database. Database models provide a blueprint for creating tables and establishing relationships between them, which can help ensure data integrity and efficiency.
Add the following code to the models.py
to create a database model for your application.
from datetime import datetime
import uuid
from cassandra.cqlengine import columns
from cassandra.cqlengine.models import Model
from config import get_settings
settings = get_settings()
class Post(Model):
__keyspace__ = settings.keyspace
post_id = columns.UUID(primary_key=True, default=uuid.uuid1)
title = columns.Text()
body = columns.Text()
created_at = columns.DateTime(primary_key=True, default=datetime.utcnow())
def __str__(self) -> str:
return self.__repr__()
def __repr__(self) -> str:
return f"Post(title={self.title})"
The code defines a class Post
that inherits from cassandra.cqlengine.models.Model
and represents a model for a post object in the database.
The Post
model has the following attributes:
post_id
: a UUID field that serves as the primary key of the table.title
: a text field representing the title of the post.body
: a text field representing the body of the post.created_at
: a datetime field representing the creation time of the post.
The __keyspace__
attribute of the model specifies the name of the keyspace where the table for this model will be created.
The __repr__
method returns a string representation of the object and the __str__
method calls the __repr__
method.
Synchronize Model and Create Table
Now, you need a way to sync the Post Model and create a database table on Astra DB.
FastAPI provides a Lifespan mechanism to do so, According to FastAPI docs, "You can define logic (code) that should be executed before the application starts taking requests, and right after it finishes handling requests, it covers the whole application lifespan (the word "lifespan" will be important in a second ๐)."
Using the Lifespan mechanism you'll execute the Python Cassandra driver sync_table
functionality.
Using the sync_table
method the Cassandra CQL engine inspects the models defined in the application and creates or updates corresponding tables and columns in the database. This process is referred to as schema management and ensures that the database schema stays up-to-date with the application's model definitions. By synchronizing the database schema with the application's models, the application can reliably read and write data to the database using the CQL engine. This makes it very easy to add new database columns on the go.
Add the following code to the main.py
file,
from contextlib import asynccontextmanager
from fastapi import FastAPI
from cassandra.cqlengine.management import sync_table
from models import Post
from database import get_session
@asynccontextmanager
async def lifespan(app: FastAPI):
global DB_SEESSION
DB_SEESSION = get_session()
sync_table(Post)
yield
app = FastAPI(lifespan=lifespan)
@app.get("/")
def homepage():
return {"meaasage": "Rugged man FastAPI featuring clergey man ASTRA DB"}
asynccontextmanager
is a decorator used to create a context manager that can be used in an asynchronous (async/await) context.lifespan
is an async context manager that initializes a globalDB_SESSION
variable, callssync_table
to create or synchronize the Post table with the database and yield control to the context in which it is called.app
is an instance ofFastAPI
framework, which is initialized with thelifespan
async context manager.@app.get("/")
is a decorator that registers a route for the application to handle GET requests at the root URL.The
homepage
function is the request handler for the"/"
route. It returns a dictionary with a message key-value pair.
Start the FastAPI application
Now that you have everything set up and configured properly. It's time to start your FastAPI application.
Open your terminal and run the following command; uvicorn main:app --reload
uvicorn
is a Python ASGI (Asynchronous Server Gateway Interface) web server, used to run asynchronous web applications built with frameworks such as FastAPI.
main:app
specifies the module and the application instance to run.
--reload
specifies that the server should automatically reload the application when code changes are detected. This is useful for development purposes, as it allows you to see the changes you make to your code without having to manually restart the server.
Review the State of Your Database
Upon starting the FastAPI application. The Post Model will be synced and the database table created automatically. This process is handled by the Python Cassandra driver in the Lifespan Mechanism.
Visit your database console on Astra DB and Navigate to the CQL Console tab.
Execute the following command in the console; SELECT * FROM blog.post ;
And there you have it. A ready-to-use database solution hosted on the cloud.
Basic CRUD Operations
In this section, you're going to build a very basic CRUD API that allows you to create, read, update, and delete items in the database.
The Python Cassandra Driver ORM is a tool that offers a user-friendly interface for developers to interact with a Cassandra database. It simplifies the process of working with Cassandra data by providing an object-relational mapping layer that allows developers to use Python classes and objects to perform database operations instead of writing raw CQL queries.
The ORM provides a straightforward API that facilitates CRUD (create, read, update, delete) operations and CQL data queries. It takes care of the technicalities of establishing connections with a Cassandra cluster, managing connections, and executing CQL queries, allowing developers to focus on their application logic.
Edit the main.py
file and add the following code to the file.
from datetime import datetime
from fastapi import FastAPI, HTTPException
# Create some hard-coded data for demonstration purposes
sample_posts = [
{
"title": "First post",
"body": "This is the first post.",
"created_at": datetime(2022, 12, 25, 12, 30)
},
{
"title": "Second post",
"body": "This is the second post.",
}
]
# Define the CRUD operations
@app.post("/posts")
def create_post():
for post in sample_posts:
post_data = Post.create(**post)
return "Posts created successfully!"
@app.get("/posts")
def get_posts():
posts = [dict(x) for x in Post.objects.all()]
return posts
@app.put("/posts/{post_id}")
def update_post(post_id: str, title: str, body: str):
try:
post = Post.objects(post_id=post_id).get()
post.title = title
post.body = body
post.save()
return "Post updated successfully!"
except:
raise HTTPException(status_code=404, detail="Post not found")
@app.delete("/posts/{post_id}")
def delete_post(post_id: str):
try:
post = Post.objects(post_id=post_id).get()
post.delete()
return "Post deleted successfully!"
except:
raise HTTPException(status_code=404, detail="Post not found")
endpoints:
GET /posts
: Returns a list of all posts.POST /posts
: Creates a new post.PUT /posts/{id}
: Updates the post with the specified ID.DELETE /posts/{id}
: Deletes the post with the specified ID.
Test API Endpoints
Restart your FastAPI application if it's not running already. Using your favorite tool send HTTP requests to each API endpoint.
Visit the CQL Console tab on your Astra DB dashboard and execute the SELECT
command as shown below.
Your database is being populated with data sent via the API endpoints.
Conclusion
In conclusion, connecting Astra DB with FastAPI is a powerful combination for building high-performance APIs that can handle large volumes of data and requests. Astra DB's NoSQL database and FastAPI's asynchronous framework provide a robust and efficient solution for building scalable APIs that can meet the demands of today's digital landscape. By following the comprehensive guide outlined in this article, developers can easily set up a connection between Astra DB and FastAPI, and leverage the benefits of both technologies to create fast and reliable APIs. With the ability to handle complex queries, integrate with other services, and easily scale to meet changing requirements, Astra DB and FastAPI are a winning combination for any API project.
Source Code: https://github.com/princewilling/FastAPI-X-Astra-DB