Skip to content

Merge pull request #17 from FasterApiWeb/stage #16

Merge pull request #17 from FasterApiWeb/stage

Merge pull request #17 from FasterApiWeb/stage #16

Workflow file for this run

name: Release
on:
push:
branches:
- master
- stage
- dev
tags:
- "v*"
permissions:
contents: write
id-token: write
packages: write
jobs:
# ── Publish channel builds to TestPyPI on branch merges ───────────────
# Channel mapping:
# - dev -> 0.0.0.devN
# - stage -> 0.0.0aN
# - master -> 0.0.0rcN
# Stable PyPI releases remain tag-based (vX.Y.Z).
publish-testpypi-dev:
if: github.ref == 'refs/heads/dev'
runs-on: ubuntu-latest
environment: testpypi
permissions:
contents: read
steps:
- name: Verify TestPyPI token is configured
run: |
if [ -z "${{ secrets.TEST_PYPI_API_TOKEN }}" ]; then
echo "::error::Missing secret TEST_PYPI_API_TOKEN (Settings → Secrets and variables → Actions)."
exit 1
fi
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install build tools
run: pip install build
- name: Build channel package (dev)
env:
SETUPTOOLS_SCM_PRETEND_VERSION: 0.0.0.dev${{ github.run_number }}
run: python -m build
- name: Upload package artifact (dev channel)
uses: actions/upload-artifact@v4
with:
name: dist-dev
path: dist/
- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
user: __token__
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
skip-existing: true
publish-testpypi-stage:
if: github.ref == 'refs/heads/stage'
runs-on: ubuntu-latest
environment: testpypi
permissions:
contents: read
steps:
- name: Verify TestPyPI token is configured
run: |
if [ -z "${{ secrets.TEST_PYPI_API_TOKEN }}" ]; then
echo "::error::Missing secret TEST_PYPI_API_TOKEN (Settings → Secrets and variables → Actions)."
exit 1
fi
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install build tools
run: pip install build
- name: Build channel package (stage)
env:
SETUPTOOLS_SCM_PRETEND_VERSION: 0.0.0a${{ github.run_number }}
run: python -m build
- name: Upload package artifact (stage channel)
uses: actions/upload-artifact@v4
with:
name: dist-stage
path: dist/
- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
user: __token__
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
skip-existing: true
publish-testpypi-master:
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
environment: testpypi
permissions:
contents: read
steps:
- name: Verify TestPyPI token is configured
run: |
if [ -z "${{ secrets.TEST_PYPI_API_TOKEN }}" ]; then
echo "::error::Missing secret TEST_PYPI_API_TOKEN (Settings → Secrets and variables → Actions)."
echo "Create an API token on TestPyPI for the `faster-api-web` project and add it as TEST_PYPI_API_TOKEN."
exit 1
fi
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install build tools
run: pip install build
- name: Build channel package (master)
env:
SETUPTOOLS_SCM_PRETEND_VERSION: 0.0.0rc${{ github.run_number }}
run: python -m build
- name: Upload package artifact (master channel)
uses: actions/upload-artifact@v4
with:
name: dist-master
path: dist/
- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
user: __token__
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
skip-existing: true
# ── Gate: only release from master ──────────────────────────────────
check-branch:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Verify tag is on master
run: |
TAG_COMMIT=$(git rev-list -n 1 "$GITHUB_REF")
if ! git branch -r --contains "$TAG_COMMIT" | grep -q 'origin/master'; then
echo "::error::Tag $GITHUB_REF_NAME is not on the master branch. Releases must be tagged on master."
exit 1
fi
# ── Step 1: Run tests ──────────────────────────────────────────────
test:
needs: check-branch
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pip install -e ".[dev]"
- run: pytest --cov=FasterAPI --cov-report=term-missing --cov-fail-under=85
# ── Step 2: Build wheel + sdist ────────────────────────────────────
build:
needs: test
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install build tools
run: pip install build
- name: Build wheel and sdist
run: python -m build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
# ── Step 3: Publish to PyPI via trusted publishing (OIDC) ──────────
publish-pypi:
needs: build
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
skip-existing: true
# ── Step 4: Publish Docker image to GitHub Packages (ghcr.io) ──────
publish-docker:
needs: test
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract version from tag
id: version
run: echo "tag=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
- name: Normalize owner to lowercase
id: owner
run: echo "owner_lc=${GITHUB_REPOSITORY_OWNER,,}" >> "$GITHUB_OUTPUT"
- name: Normalize repo name to lowercase
id: repo
run: echo "repo_lc=$(echo "${GITHUB_REPOSITORY#*/}" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
ghcr.io/${{ steps.owner.outputs.owner_lc }}/${{ steps.repo.outputs.repo_lc }}:${{ steps.version.outputs.tag }}
ghcr.io/${{ steps.owner.outputs.owner_lc }}/${{ steps.repo.outputs.repo_lc }}:latest
# ── Step 5: Create GitHub Release with artifacts ───────────────────
github-release:
needs: [build, publish-pypi, publish-docker]
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: dist/*
body: |
# FasterAPI ${{ github.ref_name }}
**A high-performance ASGI web framework — drop-in FastAPI replacement.**
```bash
pip install faster-api-web
```
---
## 5 Key Innovations
### 1. Radix Tree Router — 7.6x faster routing
O(k) lookups regardless of route count. **1,104,318 ops/s vs 144,822 ops/s** (regex).
### 2. msgspec (Rust-backed) — 9.4x faster JSON encoding
Dict → bytes in a single C call. **6,317,891 ops/s vs 670,234 ops/s**.
### 3. Compiled Dependency Injection — zero per-request introspection
Handler signatures compiled once at startup. Eliminates ~80% of per-request framework overhead.
### 4. uvloop — C-powered event loop
libuv-backed (same engine as Node.js). All I/O scheduling in C.
### 5. Lazy Request Parsing — only parse what you use
Headers, query params, cookies parsed on first access only.
---
## Benchmarks (Python 3.13.7)
| Endpoint | FasterAPI | FastAPI | Speedup |
|---|---|---|---|
| `GET /health` | **335,612 req/s** | 49,005 req/s | **6.85x** |
| `GET /users/{id}` | **282,835 req/s** | 32,391 req/s | **8.73x** |
| `POST /users` | **193,225 req/s** | 27,031 req/s | **7.15x** |
[Full benchmarks →](https://github.com/FasterApiWeb/FasterAPI#benchmarks)
---
## Install
```bash
pip install faster-api-web # core
pip install faster-api-web[all] # + uvloop
```
## Docker
```bash
docker pull ghcr.io/fasterapiweb/fasterapi:latest
```