# Building Python applications with Postgres and FastAPI

In this guide, we will walk through the process of creating a Python application using [FastAPI](https://fastapi.tiangolo.com/) together with Postgres using Neurelo.

{% hint style="info" %}
This guide can be easily adapted to other web frameworks, such as [Flask](https://flask.palletsprojects.com/en/3.0.x/).
{% endhint %}

Our goal is to create a "Fun Facts" application. This app will display a random animal fact to the user upon each site refresh. The facts, stored in a database on a remote server, will be managed through Python, with FastAPI serving as our chosen web framework.

Central to our application is Neurelo which will significantly simplify the database interactions by leveraging Neurelo’s auto-generated API endpoints.

Before we dive into building this application, let us lay the groundwork. This involves creating a new project, setting up a data source, and formulating a database schema suitable for our app. For this, we will use Postgres.

The first step is to [create a new project in Neurelo](/getting-started/starting-your-neurelo-project.md). Following the project creation, [add a data source to this project](/getting-started/starting-your-neurelo-project/step-1-add-a-data-source.md).&#x20;

Next, a database schema must be defined. For the sake of simplicity, our schema will include the following fields: `id`, `animal_name`, `fact`, and `created_at`. With this, we can [create our schema](/definitions/neurelo-schema-editor.md) as:

```json
{
  "objects": {
    "animal_facts": {
      "properties": {
        "animal_name": {
          "type": "string",
          "sourceType": [
            "VarChar",
            255
          ]
        },
        "created_at": {
          "type": "string",
          "format": "date-time",
          "sourceType": [
            "Timestamptz",
            6
          ],
          "default": {
            "function": "now"
          },
          "nullable": true
        },
        "fact": {
          "type": "string"
        },
        "id": {
          "type": "integer",
          "default": {
            "function": "autoincrement"
          },
          "identifier": true
        }
      }
    }
  }
}
```

For more information on Neurelo’s Schema Language, see the [Neurelo Schema Language (NSL) documentation](/neurelo-schema-language-nsl.md).

Once your schema is ready, [commit it](/definitions/branches-and-commits.md) and then [create an environment attached to this commit](/environments/creating-a-new-environment.md).

After the environment is set up, applying a migration to your data source is straightforward. Just follow [these steps to migrate your schema](/environments/migrations.md).

Once migrations are completed, your database will be updated to match your schema in Neurelo. At this point, you are ready to use Neurelo's auto-generated API endpoints for your project. You can view these in the environment’s **APIs** tab.

<figure><img src="/files/nfCMOrpnNyVc2FbKL4lu" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
We provide an [API Playground](/environments/api-playground.md) to easily run and test these auto-generated APIs!
{% endhint %}

It's now time to develop the application code in Python. We will begin by creating a class named `Neurelo` to facilitate interaction with Neurelo’s API endpoints, and consequently, our database. The code for this is straightforward:

```python
# neurelo.py
import os
import argparse
import json

import requests
from dotenv import load_dotenv


class Neurelo:
    def __init__(self):
        load_dotenv()
        self.headers = {
            "X-API-KEY": os.getenv("X_API_KEY"),
            "Content-Type": "application/json",
        }
        # For an object named `animal_facts`, the following GET MANY endpoint is auto-generated.
        # Details on these endpoints can be found in the API reference under the APIs tab.      
        self.api_url = "https://us-west-2.aws.neurelo.com/rest/animal_facts/"

    def store(self):
        """
        Stores the initial animal facts in the database.
        The 'facts.json' file should follow this format:
        [ {"id": 1, "fact": "Fact 1", "animal_name": "Animal 1"}, ..... ]
        """
        with open("./facts.json") as f:
            facts = json.load(f)
        response = requests.post(self.api_url, headers=self.headers, json=facts)
        print(f"Stored: {response}")

    def fetch(self):
        """
        Fetches all the animal facts from the database
        """
        response = requests.get(self.api_url, headers=self.headers)
        return response.json()["data"]

    def delete_all(self):
        """
        Deletes all the animal facts whose id is greater than or equal to 1
        """
        req = self.api_url + "?filter={%22id%22%3A%20{%22gte%22%3A%201}}"
        response = requests.delete(req, headers=self.headers)
        print(f"Deleted: {response}")

if __name__ == "__main__":
    neurelo = Neurelo()

    parser = argparse.ArgumentParser(
        prog="Animal Fun Facts", description="Shows animal-based fun facts"
    )
    parser.add_argument(
        "-c",
        "--cmd",
        default="f",
        help="f/s/d (fetches/stores/deletes) all data using Neurelo's API.",
    )
    args = parser.parse_args()
    actions = {"f": neurelo.fetch, "s": neurelo.store, "d": neurelo.delete_all}
    result = actions.get(args.cmd, lambda: "Wrong cmd. It should be one of (f/s/d)")()
    print(result)
```

Before running this code, it is essential to [generate an API Key in the environments tab](/environments/api-keys.md#creating-an-api-key-in-neurelo). Subsequently, store this key in an `.env` file as `X_API_KEY="<KEY>"`. Ensure the `.env` file is located in the same directory as your Python script.

And that's it! We have now established an interface with our database via Neurelo. This setup helps us to integrate this class with our preferred web framework to build our application. As previously mentioned, we will be using **FastAPI** to illustrate this process,

```python
import random

from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from neurelo import Neurelo

app = FastAPI()
api = Neurelo()

templates = Jinja2Templates(directory="templates")


@app.get("/random-fact/", response_class=HTMLResponse)
async def read_random_fact(request: Request):
    random_fact = random.choice(api.fetch())
    if random_fact is None:
        raise HTTPException(status_code=404, detail="No facts available")
    return templates.TemplateResponse(
        "fact_page.html",
        {
            "request": request,
            "fact": random_fact["fact"],
            "animal": random_fact["animal_name"],
        },
    )

```

The server mentioned above includes an endpoint (`/random-fact/`) that retrieves a random fact from the `animal_facts` table via the `Neurelo` class, returning an HTML response rendered with a Jinja2 template. This code is assumed to be in a `main.py` file. The template, `fact_page.html`, can be straightforward:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Random Animal Fact</title>
</head>
<body>
    <div>
        <h2>Random Animal Fact</h2>
        <p><strong>Animal:</strong> {{ animal }}</p>
        <p><strong>Fact:</strong> {{ fact }}</p>
    </div>
</body>
</html>

```

This template uses Jinja2 syntax to display the random fact. That's all there is to it! You can launch your FastAPI application with `uvicorn main:app --reload` and go to `http://127.0.0.1:8000/random-fact/` to see your application powered by Neurelo in action.

<figure><img src="/files/CDfQALVC3m7EO55k5nR2" alt="" width="563"><figcaption></figcaption></figure>

As can be imagined, this code can be easily adapted to other web frameworks, such as [Flask](https://flask.palletsprojects.com/en/3.0.x/).

Now, imagine your database holds thousands of random animal facts, and you wish to avoid fetching all to select one on the server side. While you can use filters and other capabilities in Neurelo's auto-generated APIs for such queries, we will use [Neurelo’s Custom Queries](/definitions/custom-apis-for-complex-queries.md) feature to create custom endpoints as an example here.&#x20;

To begin, go to the Definitions view and click on the Custom Queries tab.

<figure><img src="/files/31HoeGNJYNEUsMeIK9yy" alt=""><figcaption></figcaption></figure>

Next, create a new custom query endpoint as shown below:

<figure><img src="/files/igwyR5jzKK8KT5XHdYh4" alt="" width="563"><figcaption></figcaption></figure>

You can use Neurelo's [AI assistant](/definitions/custom-apis-for-complex-queries/ai-assisted-query-generation.md) for swift construction of a custom query. For this scenario, the prompt we will use is "fetch a random animal fact".

<figure><img src="/files/nFu2bIH5kEyMJqfXvPH5" alt="" width="563"><figcaption></figcaption></figure>

And there you have it! The endpoint and its corresponding query are ready in just a few seconds. Our [custom query testing playground](/definitions/custom-apis-for-complex-queries/write-and-commit-custom-queries.md#writing-custom-queries-in-neurelo) is also available for convenient query testing and iterations, if needed. The remaining step is to commit this new custom query definition, update our environment to incorporate this change, and then proceed with using the new endpoint.&#x20;

<figure><img src="/files/X7klEBfPUq75QdgrBw28" alt=""><figcaption></figcaption></figure>

You can read more on [Custom Query Endpoints and AI-Assisted Query Generation](/definitions/custom-apis-for-complex-queries.md) in our documentation. Do not forget to update the `self.api_url` in the `Neurelo` class with your new custom endpoint, which, in this example, is `/custom/random_animal`.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.neurelo.com/guides/building-python-applications-with-postgres-and-fastapi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
