Skip to content

Commit 7f65377

Browse files
authored
Merge pull request #336 from DefangLabs/linda-nounly-go
Nounly Sample
0 parents  commit 7f65377

File tree

10 files changed

+268
-0
lines changed

10 files changed

+268
-0
lines changed

.github/workflows/deploy.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Deploy
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
deploy:
10+
environment: playground
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: read
14+
id-token: write
15+
16+
steps:
17+
- name: Checkout Repo
18+
uses: actions/checkout@v4
19+
20+
- name: Deploy
21+
uses: DefangLabs/[email protected]

README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# FastAPI & PostgreSQL
2+
3+
[![1-click-deploy](https://defang.io/deploy-with-defang.svg)](https://portal.defang.dev/redirect?url=https%3A%2F%2Fgithub.com%2Fnew%3Ftemplate_name%3Dsample-fastapi-postgres-template%26template_owner%3DDefangSamples)
4+
5+
This sample project demonstrates how to deploy FastAPI with PostgreSQL with Defang.
6+
7+
## Prerequisites
8+
9+
1. Download <a href="https://github.com/defang-io/defang">Defang CLI</a>
10+
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>.
11+
12+
## Development
13+
14+
To run the development container(s) locally, do:
15+
16+
```bash
17+
docker compose -f compose.dev.yaml up --build
18+
```
19+
20+
Or to run the production container(s) locally, do:
21+
22+
```bash
23+
POSTGRES_PASSWORD=postgres docker compose up --build
24+
```
25+
26+
## Configuration
27+
28+
For this sample, you will need to provide the following [configuration](https://docs.defang.io/docs/concepts/configuration):
29+
30+
> Note that if you are using the 1-click deploy option, you can set these values as secrets in your GitHub repository and the action will automatically deploy them for you.
31+
32+
### `POSTGRES_PASSWORD`
33+
``` bash
34+
defang config set POSTGRES_PASSWORD
35+
```
36+
37+
## Deployment
38+
39+
> [!NOTE]
40+
> Download [Defang CLI](https://github.com/DefangLabs/defang)
41+
42+
### Defang Playground
43+
44+
Deploy your application to the Defang Playground by opening up your terminal and typing:
45+
```bash
46+
defang compose up
47+
```
48+
49+
### BYOC (AWS)
50+
51+
If you want to deploy to your own cloud account, you can use Defang BYOC:
52+
53+
1. [Authenticate your AWS account](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html), and check that you have properly set your environment variables like `AWS_PROFILE`, `AWS_REGION`, `AWS_ACCESS_KEY_ID`, and `AWS_SECRET_ACCESS_KEY`.
54+
2. Run in a terminal that has access to your AWS environment variables:
55+
```bash
56+
defang --provider=aws compose up
57+
```
58+
59+
---
60+
61+
Title: FastAPI & PostgreSQL
62+
63+
Short Description: A sample project with FastAPI and PostgreSQL.
64+
65+
Tags: FastAPI, PostgreSQL, Python, SQL
66+
67+
Languages: python

compose.dev.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
services:
2+
fastapi:
3+
extends:
4+
file: compose.yaml
5+
service: fastapi
6+
environment:
7+
- DATABASE_URL=postgres://postgres:postgres@db:5432/postgres
8+
volumes:
9+
- ./fastapi:/app
10+
depends_on:
11+
- db
12+
command: fastapi dev main.py --host 0.0.0.0
13+
14+
db:
15+
extends:
16+
file: compose.yaml
17+
service: db
18+
environment:
19+
- POSTGRES_USER=postgres
20+
- POSTGRES_PASSWORD=postgres
21+
- POSTGRES_DB=postgres
22+

compose.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
services:
2+
fastapi:
3+
restart: unless-stopped
4+
build:
5+
context: ./fastapi
6+
dockerfile: Dockerfile
7+
ports:
8+
- mode: ingress
9+
target: 8000
10+
published: 8000
11+
environment:
12+
- DB_URL=postgres://postgres:${POSTGRES_PASSWORD}@db:5432/postgres
13+
depends_on:
14+
- db
15+
#deploy:
16+
# resources:
17+
# reservations:
18+
# memory: 256M
19+
20+
db:
21+
image: postgres:15
22+
restart: unless-stopped
23+
environment:
24+
- POSTGRES_USER=postgres
25+
- POSTGRES_PASSWORD
26+
- POSTGRES_DB=postgres
27+
ports:
28+
- mode: host
29+
target: 5432

fastapi/.dockerignore

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/

fastapi/.gitignore

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/

fastapi/Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use an official Python runtime as a parent image
2+
FROM python:3.11-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 -qq \
9+
&& apt-get install -y \
10+
build-essential \
11+
python3-dev \
12+
ca-certificates \
13+
&& apt-get clean \
14+
&& rm -rf /var/lib/apt/lists/*
15+
16+
# Install any needed packages specified in requirements.txt
17+
COPY requirements.txt /app/
18+
RUN pip install --no-cache-dir -r requirements.txt
19+
20+
# Copy the current directory contents into the container at /app
21+
COPY . /app
22+
23+
# Make port 5000 available to the world outside this container
24+
EXPOSE 8000
25+
26+
# Run main when the container launches
27+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

fastapi/main.py

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("DB_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)

fastapi/requirements.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
uwsgi
2+
fastapi[standard]
3+
uvicorn
4+
asyncpg
5+
databases
6+
python-dotenv
7+
jinja2
8+
python-multipart

fastapi/templates/index.html

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)