Skip to content

Commit a08ac73

Browse files
authored
Flask example (#1251)
### Description Adds Flask zero-config example Also updates issue with FastAPI example, it should not mount public folder ### Demo URL https://vercel-plus-flask.vercel.app/ ### Type of Change - [x] New Example - [x] Example updates (Bug fixes, new features, etc.) - [ ] Other (changes to the codebase, but not to examples) ### New Example Checklist - [ ] 🛫 `npm run new-example` was used to create the example - [x] 📚 The template wasn't used but I carefuly read the [Adding a new example](https://github.com/vercel/examples#adding-a-new-example) steps and implemented them in the example - [x] 📱 Is it responsive? Are mobile and tablets considered?
1 parent 128b3ac commit a08ac73

File tree

6 files changed

+176
-0
lines changed

6 files changed

+176
-0
lines changed

python/flask/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fvercel%2Ftree%2Fmain%2Fexamples%2Fflask&demo-title=Flask%20API&demo-description=Use%20Flask%20API%20on%20Vercel%20with%20Serverless%20Functions%20using%20the%20Python%20Runtime.&demo-url=https%3A%2F%2Fvercel-plus-flask.vercel.app%2F&demo-image=https://assets.vercel.com/image/upload/v1669994600/random/python.png)
2+
3+
# Flask + Vercel
4+
5+
This example shows how to use Flask on Vercel with Serverless Functions using the [Python Runtime](https://vercel.com/docs/concepts/functions/serverless-functions/runtimes/python).
6+
7+
## Demo
8+
9+
https://vercel-plus-flask.vercel.app/
10+
11+
## How it Works
12+
13+
This example uses the Web Server Gateway Interface (WSGI) with Flask to handle requests on Vercel with Serverless Functions.
14+
15+
## Running Locally
16+
17+
```bash
18+
npm i -g vercel
19+
python -m venv .venv
20+
source .venv/bin/activate
21+
uv sync # or alternatively pip install flask gunicorn
22+
gunicorn main:app
23+
```
24+
25+
Your Flask application is now available at `http://localhost:3000`.
26+
27+
## One-Click Deploy
28+
29+
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=vercel-examples):
30+
31+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fvercel%2Ftree%2Fmain%2Fexamples%2Fflask&demo-title=Flask%20API&demo-description=Use%20Flask%20API%20on%20Vercel%20with%20Serverless%20Functions%20using%20the%20Python%20Runtime.&demo-url=https%3A%2F%2Fvercel-plus-flask.vercel.app%2F&demo-image=https://assets.vercel.com/image/upload/v1669994600/random/python.png)

python/flask/endpoints/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .routes import api_bp
2+
3+
__all__ = ["api_bp"]

python/flask/endpoints/routes.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from flask import Blueprint, jsonify
2+
3+
4+
api_bp = Blueprint("api", __name__)
5+
6+
7+
@api_bp.get("/api/data")
8+
def get_sample_data():
9+
return jsonify(
10+
{
11+
"data": [
12+
{"id": 1, "name": "Sample Item 1", "value": 100},
13+
{"id": 2, "name": "Sample Item 2", "value": 200},
14+
{"id": 3, "name": "Sample Item 3", "value": 300},
15+
],
16+
"total": 3,
17+
"timestamp": "2024-01-01T00:00:00Z",
18+
}
19+
)
20+
21+
22+
@api_bp.get("/api/items/<int:item_id>")
23+
def get_item(item_id: int):
24+
return jsonify(
25+
{
26+
"item": {
27+
"id": item_id,
28+
"name": f"Sample Item {item_id}",
29+
"value": item_id * 100,
30+
},
31+
"timestamp": "2024-01-01T00:00:00Z",
32+
}
33+
)

python/flask/main.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
from flask import Flask
2+
from endpoints import api_bp
3+
4+
5+
app = Flask(__name__)
6+
7+
8+
app.register_blueprint(api_bp)
9+
10+
11+
@app.get("/")
12+
def read_root():
13+
return """
14+
<!DOCTYPE html>
15+
<html lang="en">
16+
<head>
17+
<meta charset="UTF-8">
18+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
19+
<title>Vercel + Flask</title>
20+
<link rel="icon" type="image/svg+xml" href="/favicon.ico">
21+
<style>
22+
* { margin: 0; padding: 0; box-sizing: border-box; }
23+
body {
24+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
25+
background-color: #000000; color: #ffffff; line-height: 1.6; min-height: 100vh;
26+
display: flex; flex-direction: column;
27+
}
28+
header { border-bottom: 1px solid #333333; padding: 0; }
29+
nav { max-width: 1200px; margin: 0 auto; display: flex; align-items: center; padding: 1rem 2rem; gap: 2rem; }
30+
.logo { font-size: 1.25rem; font-weight: 600; color: #ffffff; text-decoration: none; }
31+
.nav-links { display: flex; gap: 1.5rem; margin-left: auto; }
32+
.nav-links a { text-decoration: none; color: #888888; padding: 0.5rem 1rem; border-radius: 6px; transition: all 0.2s ease; font-size: 0.875rem; font-weight: 500; }
33+
.nav-links a:hover { color: #ffffff; background-color: #111111; }
34+
main { flex: 1; max-width: 1200px; margin: 0 auto; padding: 4rem 2rem; display: flex; flex-direction: column; align-items: center; text-align: center; }
35+
.hero { margin-bottom: 3rem; }
36+
.hero-code { margin-top: 2rem; width: 100%; max-width: 900px; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
37+
.hero-code pre { background-color: #0a0a0a; border: 1px solid #333333; border-radius: 8px; padding: 1.5rem; text-align: left; grid-column: 1 / -1; }
38+
h1 { font-size: 3rem; font-weight: 700; margin-bottom: 1rem; background: linear-gradient(to right, #ffffff, #888888); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
39+
.subtitle { font-size: 1.25rem; color: #888888; margin-bottom: 2rem; max-width: 600px; }
40+
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; width: 100%; max-width: 900px; }
41+
.card { background-color: #111111; border: 1px solid #333333; border-radius: 8px; padding: 1.5rem; transition: all 0.2s ease; text-align: left; }
42+
.card:hover { border-color: #555555; transform: translateY(-2px); }
43+
.card h3 { font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: #ffffff; }
44+
.card p { color: #888888; font-size: 0.875rem; margin-bottom: 1rem; }
45+
.card a { display: inline-flex; align-items: center; color: #ffffff; text-decoration: none; font-size: 0.875rem; font-weight: 500; padding: 0.5rem 1rem; background-color: #222222; border-radius: 6px; border: 1px solid #333333; transition: all 0.2s ease; }
46+
.card a:hover { background-color: #333333; border-color: #555555; }
47+
.status-badge { display: inline-flex; align-items: center; gap: 0.5rem; background-color: #0070f3; color: #ffffff; padding: 0.25rem 0.75rem; border-radius: 20px; font-size: 0.75rem; font-weight: 500; margin-bottom: 2rem; }
48+
.status-dot { width: 6px; height: 6px; background-color: #00ff88; border-radius: 50%; }
49+
pre { background-color: #0a0a0a; border: 1px solid #333333; border-radius: 6px; padding: 1rem; overflow-x: auto; margin: 0; }
50+
code { font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace; font-size: 0.85rem; line-height: 1.5; color: #ffffff; }
51+
.keyword { color: #ff79c6; }
52+
.string { color: #f1fa8c; }
53+
.function { color: #50fa7b; }
54+
.class { color: #8be9fd; }
55+
.module { color: #8be9fd; }
56+
.variable { color: #f8f8f2; }
57+
.decorator { color: #ffb86c; }
58+
@media (max-width: 768px) {
59+
nav { padding: 1rem; flex-direction: column; gap: 1rem; }
60+
.nav-links { margin-left: 0; }
61+
main { padding: 2rem 1rem; }
62+
h1 { font-size: 2rem; }
63+
.hero-code { grid-template-columns: 1fr; }
64+
.cards { grid-template-columns: 1fr; }
65+
}
66+
</style>
67+
</head>
68+
<body>
69+
<header>
70+
<nav>
71+
<a href="/" class="logo">Vercel + Flask</a>
72+
<div class="nav-links">
73+
<a href="/api/data">API</a>
74+
</div>
75+
</nav>
76+
</header>
77+
<main>
78+
<div class="hero">
79+
<h1>Vercel + Flask</h1>
80+
<div class="hero-code">
81+
<pre><code><span class="keyword">from</span> <span class="module">flask</span> <span class="keyword">import</span> <span class="class">Flask</span>
82+
83+
<span class="variable">app</span> = <span class="class">Flask</span>(<span class="string">__name__</span>)
84+
85+
<span class="decorator">@app.get</span>(<span class="string">"/"</span>)
86+
<span class="keyword">def</span> <span class="function">read_root</span>():
87+
<span class="keyword">return</span> {<span class="string">"Python"</span>: <span class="string">"on Vercel"</span>}</code></pre>
88+
</div>
89+
</div>
90+
91+
<div class="cards">
92+
<div class="card">
93+
<h3>Sample Data</h3>
94+
<p>Access sample JSON data through our REST API. Perfect for testing and development purposes.</p>
95+
<a href="/api/data">Get Data →</a>
96+
</div>
97+
</div>
98+
</main>
99+
</body>
100+
</html>
101+
"""

python/flask/public/favicon.ico

66.1 KB
Binary file not shown.

python/flask/pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[project]
2+
name = "vercel-flask-starter"
3+
version = "0.1.0"
4+
requires-python = ">=3.9"
5+
dependencies = [
6+
"flask~=3.1",
7+
"gunicorn~=23.0.0
8+
]

0 commit comments

Comments
 (0)