Skip to content

Commit b6304bb

Browse files
authored
Merge pull request #57 from DefangLabs/fastapi-postgres
fastapi-postgres
2 parents 66816ee + 509564c commit b6304bb

File tree

9 files changed

+188
-0
lines changed

9 files changed

+188
-0
lines changed

samples/fastapi-postgres/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# FastAPI
2+
3+
This sample project demonstrates how to deploy FastAPI with Defang.
4+
5+
## Prerequisites
6+
1. Download <a href="https://github.com/defang-io/defang">Defang CLI</a>
7+
2. (optional) If you are using <a href="https://docs.defang.io/docs/concepts/defang-byoc">Defang BYOC</a>, make sure you have properly <a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html">authenticated your AWS account</a>.
8+
9+
## Deploying
10+
1. Open the terminal and type `defang login`
11+
2. Type `defang compose up` in the CLI.
12+
3. Your app will be running within a few minutes.
13+
14+
---
15+
16+
Title: FastAPI & PostgreSQL
17+
18+
Short Description: A sample project with FastAPI and PostgreSQL
19+
20+
Tags: FastAPI,PostgreSQL, Python, SQL
21+
22+
Languages: Python, SQL
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
version: '3.9'
2+
name: fastapi-postgres
3+
4+
services:
5+
fastapi:
6+
extends:
7+
file: compose.yaml
8+
service: fastapi
9+
restart: unless-stopped
10+
ports:
11+
- "8000:8000"
12+
environment:
13+
- DATABASE_URL=postgres://postgres:postgres@db:5432/postgres
14+
volumes:
15+
- ./fastapi:/app
16+
depends_on:
17+
- db
18+
command: fastapi dev main.py --host 0.0.0.0
19+
20+
db:
21+
image: postgres:15
22+
restart: unless-stopped
23+
environment:
24+
- POSTGRES_USER=postgres
25+
- POSTGRES_PASSWORD=postgres
26+
- POSTGRES_DB=postgres
27+
28+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
version: '3.9'
2+
name: fastapi-postgres
3+
services:
4+
fastapi:
5+
build:
6+
context: ./fastapi
7+
dockerfile: Dockerfile
8+
ports:
9+
- mode: ingress
10+
target: 8000
11+
environment:
12+
- DATABASE_URL
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
__pycache__/
2+
env/
3+
venv/
4+
ENV/
5+
build/
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
__pycache__/
2+
env/
3+
venv/
4+
ENV/
5+
build/
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Use an official Python runtime as a parent image
2+
FROM python:3.9-slim
3+
4+
# Set the working directory to /app
5+
WORKDIR /app
6+
7+
# Install required C++11 libraries and ca-certificates
8+
RUN apt-get update \
9+
&& apt-get install -y \
10+
build-essential \
11+
python3-dev \
12+
ca-certificates \
13+
&& rm -rf /var/lib/apt/lists/*
14+
15+
# Install any needed packages specified in requirements.txt
16+
COPY requirements.txt /app/
17+
RUN pip install --no-cache-dir -r requirements.txt
18+
19+
# Copy the current directory contents into the container at /app
20+
COPY . /app
21+
22+
# Make port 5000 available to the world outside this container
23+
EXPOSE 8000
24+
25+
# Run main when the container launches
26+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import os
2+
from fastapi import FastAPI, Request, Form
3+
from fastapi.templating import Jinja2Templates
4+
from fastapi.responses import RedirectResponse
5+
import asyncpg
6+
from contextlib import asynccontextmanager
7+
from pydantic import BaseModel
8+
from databases import Database
9+
from dotenv import load_dotenv
10+
11+
load_dotenv()
12+
13+
database_url = os.getenv("DATABASE_URL")
14+
print(f"Connecting to database at: {database_url}")
15+
database = Database(database_url)
16+
17+
18+
class TodoItem(BaseModel):
19+
id: int
20+
task: str
21+
completed: bool = False
22+
23+
@asynccontextmanager
24+
async def lifespan(app: FastAPI):
25+
await database.connect()
26+
conn = await asyncpg.connect(database_url)
27+
await conn.execute('''
28+
CREATE TABLE IF NOT EXISTS todos (
29+
id SERIAL PRIMARY KEY,
30+
task VARCHAR(255) NOT NULL,
31+
completed BOOLEAN DEFAULT FALSE
32+
);
33+
''')
34+
await conn.close()
35+
yield
36+
await database.disconnect()
37+
38+
app = FastAPI(lifespan=lifespan)
39+
templates = Jinja2Templates(directory="templates")
40+
41+
42+
@app.get("/")
43+
async def get_todos(request: Request):
44+
query = "SELECT * FROM todos"
45+
todos = await database.fetch_all(query)
46+
return templates.TemplateResponse("index.html", {"request": request, "todos": todos})
47+
48+
@app.post("/add")
49+
async def create_todo(task: str = Form(...)):
50+
query = "INSERT INTO todos(task, completed) VALUES (:task, :completed)"
51+
values = {"task": task, "completed": False}
52+
await database.execute(query, values)
53+
return RedirectResponse(url="/", status_code=303)
54+
55+
@app.post("/delete/{todo_id}")
56+
async def delete_todo(todo_id: int):
57+
query = "DELETE FROM todos WHERE id = :todo_id"
58+
await database.execute(query, {"todo_id": todo_id})
59+
return RedirectResponse(url="/", status_code=303)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
uwsgi
2+
fastapi
3+
uvicorn
4+
asyncpg
5+
databases
6+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>FastAPI &amp; Defang To-Do List</title>
7+
</head>
8+
<body>
9+
<h1>FastAPI &amp; Defang To-Do List</h1>
10+
<form action="/add" method="post">
11+
<input type="text" name="task" placeholder="New task" required>
12+
<button type="submit">Add Task</button>
13+
</form>
14+
<ul>
15+
{% for todo in todos %}
16+
<li>
17+
{{ todo.task }}
18+
<form action="/delete/{{ todo.id }}" method="post" style="display:inline;">
19+
<button type="submit">Delete</button>
20+
</form>
21+
</li>
22+
{% endfor %}
23+
</ul>
24+
</body>
25+
</html>

0 commit comments

Comments
 (0)