Modal

Modal is a serverless compute platform that supports a wide range of applications, from web services to AI models. Its user-friendly approach allows developers to write standard Python code with minimal additional annotations and deploy it to the cloud. Importantly, Modal seamlessly integrates with any Python Postgres client, making it fully compatible with Nile.

By combining Modal's serverless compute capabilities with Nile's serverless database, developers can create entirely serverless AI applications, streamlining their development process and infrastructure management.

Using Modal and Nile together

We'll demonstrate a simple example of using Modal's serverless compute capabilities with Nile's serverless database. All the setup steps and line-by-line explanation are shown below. The entire script is available here.

To get started, you'll need a Modal account and a Nile database. You can sign up for Modal here and for Nile here.

Setting Up Nile

Once you've signed up for Nile, you'll be promoted to create your first database. Go ahead and do so. You'll be redirected to the "Query Editor" page of your new database. This is a good time to create the table we'll be using in this example:

create table todos (
    id uuid DEFAULT (gen_random_uuid()),
    tenant_id uuid,
    title varchar(256),
    complete boolean);

Once you've created the table, you'll see it on the left-hand side of the screen. You'll also see the tenants table that is built-in to Nile.

Next, you'll want to pick up your database connection string: Navigate to the "Settings" page, select "Connections" and click "Generate credentials". Copy the connection string and keep it in a secure location. You'll need it to connect to your database from Modal.

Setting Up Modal

Once you've signed up for Modal, you'll be ready to start building your application.

In order to connect from Modal to your Nile database, you'll need to create a secret in Modal. Click on the "Secrets" tab and then "Create Secret". We recommend choosing "Custom" secret type (although you can use "Postgres" as well).

In the secret key, enter DATABASE_URL. In the secret value, enter the connection string you picked up in the previous step. Make sure you leave out the psql and replace postgres:// with postgresql://. When you save the secret, call it my-nile-secret.

Next, you'll want to install modal and set it up:

pip install modal

modal setup

Quickstart

Lets try creating and running a simple example. Just one function that runs on Modal and queries the Nile database.

First, create a new file called nile_modal_quickstart.py.

We'll start by setting up the Modal app and function. You can see that we are using a pre-existing image that Modal provides, and we are installing psycopg2 which is a Postgres client for Python. Then we create the modal app.

import os

import modal
import psycopg2

image = modal.Image.debian_slim(python_version="3.10").pip_install(
    "psycopg2-binary",
)

app = modal.App("quickstart-nile-modal")

Next, we'll create a function that will run on Modal. You can see that we are initializing it with our image and the secret we created earlier.

@app.function(image=image, secrets=[modal.Secret.from_name("my-nile-secret")])
def nile_quickstart():

        conn = psycopg2.connect(os.getenv("DATABASE_URL"))
        conn.set_session(autocommit=True)
        cur = conn.cursor()

        tenant_id = create_tenant("My first tenant", cur)

        create_todo(tenant_id, "My first todo", cur)
        print(f"Created todo for first tenant. Showing all todos:\n {show_todos(cur)}\n\n")

        tenant_id = create_tenant("Another tenant", cur)
        create_todo(tenant_id, "My second todo", cur)
        print(f"Created second tenant and their todo. Showing all todos:\n {show_todos(cur)}\n\n")

        cur.execute("SET nile.tenant_id = %s", (tenant_id,) )
        # No need to change the query, since we are connected to a specific virtual tenant database
        print(f"Connected to virtual tenant database for tenant {tenant_id}. Showing all todos:\n {show_todos(cur)}\n")

        cur.close()
        conn.close()

This function uses the connection string from the Modal secret to connect to the Nile database. It then creates two tenants and two todos. Each todo will be associated with one of the tenants.

show_todos is a helper function that will query simply return all todos in the database with their respective tenant name. You can see that once we connect to the virtual tenant database, the same query will only show the todos for the tenant we connected to.

Create the helper functions create_tenant, create_todo and show_todos and you're ready to run the function:

def create_tenant(name: str, cur: psycopg2.extensions.cursor):
    cur.execute("INSERT INTO tenants (name) VALUES (%s) RETURNING id;", (name,))
    return cur.fetchone()[0]

def create_todo(tenant_id: int, title: str, cur: psycopg2.extensions.cursor):
    cur.execute("INSERT INTO todos (tenant_id, title) VALUES (%s, %s);", (tenant_id, title))
    return True

def show_todos(cur: psycopg2.extensions.cursor):
    # Note the lack of filter on the query - we always show all todos in the database
    cur.execute("SELECT tenants.name, title FROM todos join tenants on tenants.id = todos.tenant_id;")
    return cur.fetchall()

Running on Modal

To run the function on Modal, you can use the following command:

modal run nile_modal_quickstart.py

Full application

Now that you've successfully deployed your first function to Modal and used your first Nile database, let's put them together in a full application.

We'll create a Sales Assistant application that allows you to embed and summarize sales conversations. The entire application - frontend, backend and LLM - all run on Modal. All the data and embeddings are stored in Nile.

Get Started with Sales Assistant