From bc344c024c5882646e2f52cb95fa58b05a547dad Mon Sep 17 00:00:00 2001 From: "Hebert F. Barros" Date: Thu, 28 Dec 2023 17:30:33 +0000 Subject: [PATCH 1/4] Initial commit --- .dockerignore | 4 ++ .github/script/test.ps1 | 27 ++++++++ .github/script/test.sh | 22 +++++++ .github/workflows/docker-image.yml | 18 ++++++ .github/workflows/python-app.yml | 29 +++++++++ .gitignore | 4 ++ .gitpod.yml | 4 ++ Dockerfile | 12 ++++ README.md | 98 ++++++++++++++++++++++++++++++ api/index.py | 1 + docker-compose.yml | 0 main.py | 1 + requirements.txt | 5 ++ src/__main__.py | 0 src/dtos/ISayHelloDto.py | 4 ++ src/index.py | 20 ++++++ test_index.http | 20 ++++++ test_index.py | 23 +++++++ vercel.json | 15 +++++ 19 files changed, 307 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/script/test.ps1 create mode 100644 .github/script/test.sh create mode 100644 .github/workflows/docker-image.yml create mode 100644 .github/workflows/python-app.yml create mode 100644 .gitignore create mode 100644 .gitpod.yml create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 api/index.py create mode 100644 docker-compose.yml create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 src/__main__.py create mode 100644 src/dtos/ISayHelloDto.py create mode 100644 src/index.py create mode 100644 test_index.http create mode 100644 test_index.py create mode 100644 vercel.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f5f4229 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.vercel +venv +.idea +__pycache__ diff --git a/.github/script/test.ps1 b/.github/script/test.ps1 new file mode 100644 index 0000000..fcd0167 --- /dev/null +++ b/.github/script/test.ps1 @@ -0,0 +1,27 @@ +#!/bin/pwsh + +if (Get-Command pip -ErrorAction SilentlyContinue) { + Write-Host "Installing Python dependencies..." + pip install -r requirements.txt +} +else { + Write-Host "pip is not installed" + Write-Host "Installing pip" + Invoke-WebRequest -Uri https://bootstrap.pypa.io/get-pip.py -OutFile get-pip.py + python get-pip.py + Write-Host "Installing Python dependencies..." + pip install -r requirements.txt +} + +if (Get-Command pytest -ErrorAction SilentlyContinue) { + Write-Host "Running tests..." + pytest --version + pytest +} +else { + Write-Host "pytest is not installed" + Write-Host "installing pytest" + pip install pytest + pytest --version + pytest +} \ No newline at end of file diff --git a/.github/script/test.sh b/.github/script/test.sh new file mode 100644 index 0000000..12f3022 --- /dev/null +++ b/.github/script/test.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +if ! [ -x "$(command -v pip)" ]; then + echo 'Error: pip is not installed.' >&2 + echo 'Installing pip...' + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py + python get-pip.py + rm get-pip.py + echo 'pip installed.' +fi + +pip install -r requirements.txt + +if ! [ -x "$(command -v pytest)" ]; then + echo 'Error: pytest is not installed.' >&2 + echo 'Installing pytest' + pip install pytest + echo 'pytest installed.' +fi + + +pytest \ No newline at end of file diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..d657a63 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,18 @@ +name: Docker Image CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 0000000..b528a9e --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,29 @@ +name: Python testing + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +permissions: + contents: read + +jobs: + run_tests_on_ubuntu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Run Bash script file + run: | + chmod +x ./.github/script/test.sh + ./.github/script/test.sh + shell: bash + run_tests_on_windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Run PowerShell script file + run: | + .\.github/script/test.ps1 + shell: pwsh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e6eda4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vercel +venv +.idea +__pycache__ \ No newline at end of file diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000..3c0c831 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,4 @@ +tasks: + - init: pip install -r requirements.txt + + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1373048 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.9 + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --upgrade pip && \ + pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..7c6b468 --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ +

+ :package: deploy-python-fastapi-in-vercel +

:package: deploy-python-fastapi-in-vercel

+

This example shows how to use FastApi on Vercel with Serverless Functions using the Python Runtime.

+

+ +

+ + Issues + + + GitHub pull requests + +
+ Report Bug + Request Feature +

+

Systems on which it has been tested.

+

+ + Ubuntu + + + Windows + +

+

Did you like the project? Please, considerate a donation to help improve!

+ +

+ +# Getting started + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fhebertcisco%2Fdeploy-python-fastapi-in-vercel%2Ftree%2Fmain%2Fpython%2FFastApi&demo-title=FastApi%20%2B%20Vercel&demo-description=Use%20FastApi%202%20on%20Vercel%20with%20Serverless%20Functions%20using%20the%20Python%20Runtime.&demo-url=https%3A%2F%2FFastApi-python-template.vercel.app%2F&demo-image=https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png) + +# FastApi + Vercel + +This example shows how to use FastApi 0.88.0 on Vercel with Serverless Functions using the [Python Runtime](https://vercel.com/docs/concepts/functions/serverless-functions/runtimes/python). + +[![Python testing](https://github.com/hebertcisco/deploy-python-fastapi-in-vercel/actions/workflows/python-app.yml/badge.svg?branch=main)](https://github.com/hebertcisco/deploy-python-fastapi-in-vercel/actions/workflows/python-app.yml) +[![Docker Image CI](https://github.com/hebertcisco/deploy-python-fastapi-in-vercel/actions/workflows/docker-image.yml/badge.svg)](https://github.com/hebertcisco/deploy-python-fastapi-in-vercel/actions/workflows/docker-image.yml) + +## Demo + +[deploy-python-fastapi-in-vercel.vercel.app](https://deploy-python-fastapi-in-vercel.vercel.app) + +## How it Works + +This example uses the Web Server Gateway Interface (WSGI) with FastApi to enable handling requests on Vercel with Serverless Functions. + +## Running Locally + +### With Docker +```bash +# Build the Docker image +docker build -t deploy-python-fastapi-in-vercel . + +# Run the Docker container +docker run -p 8000:8000 deploy-python-fastapi-in-vercel + +``` + +### With uvicorn + +#### Install dependencies + +```bash +pip install -r requirements.txt +``` +```bash +uvicorn main:app --host 0.0.0.0 --port 8000 +``` + +Your FastApi application is now available at `http://localhost:8000`. + +## One-Click Deploy + +Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=vercel-examples): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fhebertcisco%2Fdeploy-python-fastapi-in-vercel%2Ftree%2Fmain%2Fpython%2FFastApi&demo-title=FastApi%20%2B%20Vercel&demo-description=Use%20FastApi%202%20on%20Vercel%20with%20Serverless%20Functions%20using%20the%20Python%20Runtime.&demo-url=https%3A%2F%2FFastApi-python-template.vercel.app%2F&demo-image=https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png) + +## 🤝 Contributing + +Contributions, issues and feature requests are welcome!
Feel free to check [issues page](issues). + +## Show your support + +Give a ⭐️ if this project helped you! + +Or buy me a coffee 🙌🏾 + + + + + +## 📝 License + +Copyright © 2023 [Hebert F Barros](https://github.com/hebertcisco).
+This project is [MIT](LICENSE) licensed. diff --git a/api/index.py b/api/index.py new file mode 100644 index 0000000..227406a --- /dev/null +++ b/api/index.py @@ -0,0 +1 @@ +from src.index import app \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e69de29 diff --git a/main.py b/main.py new file mode 100644 index 0000000..f850a29 --- /dev/null +++ b/main.py @@ -0,0 +1 @@ +from api.index import app \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..34fc9cb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +fastapi==0.88.0 +pytest==7.1.3 +pytest-asyncio==0.20.3 +uvicorn==0.20.0 +pydantic~=1.10.4 diff --git a/src/__main__.py b/src/__main__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/dtos/ISayHelloDto.py b/src/dtos/ISayHelloDto.py new file mode 100644 index 0000000..f81f0aa --- /dev/null +++ b/src/dtos/ISayHelloDto.py @@ -0,0 +1,4 @@ +from pydantic import BaseModel + +class ISayHelloDto(BaseModel): + message: str \ No newline at end of file diff --git a/src/index.py b/src/index.py new file mode 100644 index 0000000..246f9fb --- /dev/null +++ b/src/index.py @@ -0,0 +1,20 @@ +from fastapi import FastAPI + +from src.dtos.ISayHelloDto import ISayHelloDto + +app = FastAPI() + + +@app.get("/") +async def root(): + return {"message": "Hello World"} + + +@app.get("/hello/{name}") +async def say_hello(name: str): + return {"message": f"Hello {name}"} + + +@app.post("/hello") +async def hello_message(dto: ISayHelloDto): + return {"message": f"Hello {dto.message}"} diff --git a/test_index.http b/test_index.http new file mode 100644 index 0000000..9053072 --- /dev/null +++ b/test_index.http @@ -0,0 +1,20 @@ +# Test your FastAPI endpoints + +GET http://127.0.0.1:8000/ +Accept: application/json + +### + +GET http://127.0.0.1:8000/hello/User +Accept: application/json + +### + +POST /hello HTTP/1.1 +Host: 127.0.0.1:8000 +Content-Type: application/json +Content-Length: 28 + +{ + "message": "Fulano!" +} \ No newline at end of file diff --git a/test_index.py b/test_index.py new file mode 100644 index 0000000..99258ad --- /dev/null +++ b/test_index.py @@ -0,0 +1,23 @@ +import pytest + +from src.dtos.ISayHelloDto import ISayHelloDto +from src.index import root, say_hello, hello_message + + +@pytest.mark.asyncio +async def test_root(): + result = await root() + assert result == {'message': 'Hello World'} + + +@pytest.mark.asyncio +async def test_say_hello(): + result = await say_hello("John") + assert result == {'message': 'Hello John'} + + +@pytest.mark.asyncio +async def test_hello_message(): + dto = ISayHelloDto(message="Alice") + result = await hello_message(dto) + assert result == {'message': 'Hello Alice'} diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..6172529 --- /dev/null +++ b/vercel.json @@ -0,0 +1,15 @@ +{ + "devCommand": "uvicorn main:app --host 0.0.0.0 --port 3000", + "builds": [ + { + "src": "api/index.py", + "use": "@vercel/python" + } + ], + "routes": [ + { + "src": "/(.*)", + "dest": "api/index.py" + } + ] +} \ No newline at end of file From 0a98111da23bb6decf7d7790080229941b23bc4f Mon Sep 17 00:00:00 2001 From: "Hebert F. Barros" Date: Thu, 28 Dec 2023 17:36:24 +0000 Subject: [PATCH 2/4] Add Docker setup for web service Introduced a docker-compose configuration to build and run the web service, mapping its internal port 8000 to the same port on the host, facilitating containerization and local development workflow. --- docker-compose.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index e69de29..677df3b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -0,0 +1,5 @@ +services: + web: + build: . + ports: + - "8000:8000" \ No newline at end of file From 0e6c4a03f11ddad4077791f088a22d6313357ed4 Mon Sep 17 00:00:00 2001 From: hebertcisco Date: Thu, 28 Dec 2023 17:59:23 +0000 Subject: [PATCH 3/4] Refactor initialization task in .gitpod.yml Replace pip install with docker-compose for improved environment setup. --- .gitpod.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitpod.yml b/.gitpod.yml index 3c0c831..57df11a 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,4 +1,2 @@ tasks: - - init: pip install -r requirements.txt - - + - init: docker-compose up From ddbf40d8593b448702dc3abdc134a95e18b77a55 Mon Sep 17 00:00:00 2001 From: hebertcisco Date: Thu, 28 Dec 2023 17:59:30 +0000 Subject: [PATCH 4/4] Refactor README for Docker instructions Consolidated Docker setup instructions in README for clarity and ease of use. Now users can easily choose between Docker Compose and traditional Docker methods when running the application locally. This streamlining improves the user experience. Issue #123 --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 7c6b468..9b013e5 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,14 @@ This example uses the Web Server Gateway Interface (WSGI) with FastApi to enable ## Running Locally +### With Docker Compose + +```bash +docker-compose up +``` + ### With Docker + ```bash # Build the Docker image docker build -t deploy-python-fastapi-in-vercel . @@ -66,6 +73,7 @@ docker run -p 8000:8000 deploy-python-fastapi-in-vercel ```bash pip install -r requirements.txt ``` + ```bash uvicorn main:app --host 0.0.0.0 --port 8000 ```