从“Hello World”到部署上线:FastAPI 新手实战入门指南

程序员的月亮
2025-06-22 13:04
阅读 799

开篇:一个“小项目”引发的思考

开篇:一个“小项目”引发的思考

去年年底,公司接了一个客户定制的小型 SaaS 系统,核心功能是数据采集与可视化。虽然项目规模不大,但要求接口响应快、实时性好,还要方便后续扩展。最初我们打算继续用 Django 搭建后端,毕竟大家都熟,开发效率也高。

但在这个项目的初期架构讨论中,我提了个想法:“咱们是不是可以试试 FastAPI?”

当时团队里有不少人持怀疑态度:“FastAPI 是个新东西吧?文档好不好?有没有实际案例支持?”说实话,我自己也是第一次正式用它做项目,只在空闲时做过一些小 demo。

但基于两点考虑我还是坚持了这个决定:

  1. 性能优势:Pydantic + 异步特性让接口响应更快;
  2. 现代化语法和类型提示:能让整个后端结构更清晰,团队协作更有保障。

于是,我们的 FastAPI 第一战就此展开。

初遇挑战:为什么选 FastAPI?

初遇挑战:为什么选 FastAPI?

服务器部署方案-2

坦白讲,一开始遇到的问题并不是来自技术本身,而是思维方式的转变。

面临的第一个问题是:习惯性地按照 MVC 思路来组织代码

以前写 Django 很自然就按 apps 分模块、views 写逻辑、urls 做映射。但在使用 FastAPI 的时候,我发现这种方式不太适配它的“轻量级路由+依赖注入”机制。

比如,当我们把所有 endpoint 直接塞进 main.py 时,随着模块越来越多,代码很快就变得难以维护。特别是当其他同事接手部分代码时,他们普遍反映“找不到某个接口的源码”,“依赖管理混乱”。

这让我意识到,FastAPI 虽然灵活,但也需要更好的代码结构设计

另一个棘手问题:数据库异步操作不熟悉

我们这次用了 SQLAlchemy 配合 asyncpg 做 PostgreSQL 的 ORM 查询,在写异步查询逻辑的时候,踩了好几个坑。例如:

  • 忘记 await 导致接口卡死
  • session 的生命周期控制混乱导致连接泄漏
  • 多层 async 函数嵌套调用引起的错误追踪困难

这些问题一度让进度滞后,直到我们在代码规范和依赖管理上下了不少功夫才稳定下来。

解决方案:搭建一套适合自己的 FastAPI 工程结构

为了解决上述问题,我把整个工程做了重新规划。下面是我们最终采用的一种目录结构(你可以根据项目复杂度调整):

project/
│
├── app/
│   ├── __init__.py
│   ├── main.py                 # 启动入口 & 路由注册
│   ├── config/                 # 配置文件
│   ├── api/                    # 接口模块(v1)
│   │   ├── __init__.py
│   │   ├── users/              # 用户相关接口
│   │   └── reports/            # 报表相关接口
│   ├── models/                 # 数据库模型
│   ├── schemas/                # 请求/响应模型定义
│   ├── database/               # 数据库连接与会话管理
│   └── core/                   # 核心配置/公共函数
│
├── tests/                      # 单元测试目录
└── requirements.txt

这种结构的最大好处在于,每个模块职责明确、层次清晰,而且便于后期拆分微服务或进行单元测试覆盖

接下来分享一下具体怎么用 FastAPI 实现这个结构。

实战演练:关键代码与实现细节

接口定义(schema)

我们先来看看请求/响应的数据模型。FastAPI 的一大亮点就是对 Pydantic 的集成。举个例子:

# schemas/users.py
from pydantic import BaseModel
from typing import Optional

class UserCreate(BaseModel):
    name: str
    email: str
    password: str

class UserOut(BaseModel):
    id: int
    name: str
    email: str
    is_active: bool

    class Config:
        orm_mode = True

这样我们就可以在接口函数中直接引用这些模型,同时还能自动生成文档。

接口编写示例(结合依赖注入)

# api/users/router.py
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.core.dependencies import get_db
from app.schemas.users import UserCreate, UserOut
from app.models.user import User
from app.core.database import SessionLocal

router = APIRouter(prefix="/users", tags=["users"])

@router.post("/", response_model=UserOut)
def create_user(user: UserCreate, db: Session = Depends(get_db)):
    db_user = User(**user.dict())
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user

这里有个小贴士:不要把所有的 db 操作都写在路由处理函数里!建议封装成 service 层,让业务逻辑和接口解耦。

数据库连接配置(使用异步)

我们这次采用了 SQLAlchemy 的异步连接方式,通过 sqlalchemy.ext.asyncio 模块。

# core/database.py
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker

DATABASE_URL = "postgresql+asyncpg://user:password@localhost/dbname"

engine = create_async_engine(DATABASE_URL, echo=True)
AsyncSessionLocal = sessionmaker(
    bind=engine,
    class_=AsyncSession,
    expire_on_commit=False
)

async def get_db():
    async with AsyncSessionLocal() as session:
        yield session

在使用时,只需:

from app.core.database import get_db

@router.get("/{user_id}", response_model=UserOut)
async def read_user(user_id: int, db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalars().first()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user

📌 小技巧:使用 select()await scalars() 是 SQLAlchemy 2.x 的推荐写法,避免老版本中同步方法带来的兼容问题。

踩坑经验:那些让我们抓狂的瞬间

在整个开发过程中,我们也遇到不少“翻车”情况,这里挑几个典型说说。

1. “异步没真正用起来”——慢接口之谜

一开始我们写的接口确实用了 async def,但内部调用的方法还是传统的 sync 方式,比如:

async def some_api(db: Session = Depends(get_db)):
    data = slow_sync_query(db)  # ❌ 这里没有 await,且是个阻塞操作

这个问题导致协程并没有发挥应有效果,反而因为事件循环调度带来了额外开销。

解决办法:要么改造成异步调用,要么就别加 async 关键字,避免误导。

2. “生成环境下的 Uvicorn 配置太随意”

本地开发我们直接跑 uvicorn 主进程就行了:

uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

但生产环境可不能这么简单粗暴。我们开始直接用了默认 workers 数量(也就是 1),结果压力测试时 QPS 上不去。

后来换成了 Gunicorn + Uvicorn Worker:

gunicorn -w 4 -k uvicorn.workers.UvicornWorker app.main:app

并通过 Nginx 做反向代理,这才真正发挥了并发能力。

✅ 小提醒:

生产环境下一定记得开启多个 worker,利用多核 CPU; 如果是纯异步应用,可以用 --workers-per-core 2~3 这种方式自动分配; 不要忘记日志收集、健康检查等运维必备项。

3. “Swagger UI 显示不出来”——跨域设置遗漏

我们前端同学反馈说,在访问 /docs 页面时报错,无法显示接口文档。

排查发现是因为 FastAPI 默认不允许跨域访问。而前端是用 Vue + devServer,跑在 localhost:3000,后端在 localhost:8000

解决方案很简单,添加中间件允许 CORS:

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 可以换成具体域名
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

不过一定要注意,线上环境不要直接 allow_origins=["*"]

效果总结:FastAPI 带来的收益

现在回头看看这个项目,已经上线运行超过半年,整体表现非常稳定。

下面是几个具体的收益点:

  • 开发效率提升明显:类型校验 + 自动生成文档大大减少了接口联调时间;
  • 系统响应速度有显著优化:相比之前用 Flask/Django 同类接口,FastAPI 平均响应速度快了 30%以上;
  • 代码结构更清晰:Pydantic schema + 分层设计让前后端沟通更顺畅,自动化测试覆盖率也提高了不少;
  • 易于水平扩展:我们后来又加了一个数据分析服务,用 FastAPI 构建的子服务无缝对接,几乎没有额外成本。

服务器部署方案-1

最关键的是,团队成员现在都很愿意在新项目中继续用 FastAPI,甚至把它作为了默认框架之一。

给新手的一些真心建议

如果你是刚接触 FastAPI 的开发者,或者准备用它做你的第一个后端项目,这里有几点经验想跟你分享:

1. 从基础起步,别一开始就追求大而全

FastAPI 学习曲线并不陡峭,完全可以从一个简单的 CRUD 应用开始练手。比如做个博客后台、任务管理系统之类的。

2. 养成良好的工程习惯

  • 接口参数统一用 schema 定义
  • 把 db 操作封装到 service 层,不要挤在路由函数里
  • 使用 logging 替代 print,方便定位问题
  • 加入自动化测试,哪怕只是个简单的 GET 接口

3. 不要盲目上 async,除非你真的需要

有些同学看到 FastAPI 支持异步就马上写一堆 async def,但如果内部全是同步调用,反而可能会降低性能。异步是为 I/O 密集型任务准备的,不是用来炫技的。

4. 多关注社区生态和最佳实践

FastAPI 社区虽然比不上 Django,但已经非常活跃了。比如:

另外 GitHub 上也有很多高质量的开源项目可以参考学习。

结语:关于选择与成长

写这篇文章的时候,我一直在想,其实 FastAPI 对我们来说更像是一个“催化剂”。它没有改变我们写后端的根本逻辑,但它让整个流程更优雅、更高效了。

在软件开发这条路上,工具始终是为了更好地解决问题。选对工具固然重要,但更重要的是我们如何使用它。希望这篇文章能帮你在入门 FastAPI 的过程中少走些弯路,找到属于自己的那条“快速路径”。

最后送一句我常对自己说的话:

“不要害怕尝试新技术,真正的成长往往发生在‘不知道’的地方。”

祝你写出更漂亮的 Python 后端代码。💡

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝