
Fba
- 347 installs
- 11 repo stars
- Updated July 12, 2026
- fastapi-practices/skills
fba is a Skillselion skill that helps developers apply FastAPI backend API practices when building Python services.
About
fba is a Skillselion skill from the fastapi-practices/skills repository that targets FastAPI-oriented backend work for Python developers. fba is designed to be invoked when a developer is implementing or refactoring an API service and wants concrete guidance on FastAPI patterns such as route organization, request/response validation, and OpenAPI-friendly design. fba fits best during day-to-day backend development tasks where code structure and framework conventions matter, especially in projects that use Pydantic models and async endpoints. fba is most useful when you need consistent API behavior and maintainable FastAPI code across routers, dependencies, and service layers.
- fba
- AI & Agent Building
- AI-coding skill
Fba by the numbers
- 347 all-time installs (skills.sh)
- Ranked #2,146 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/fastapi-practices/skills --skill fbaAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 347 |
|---|---|
| repo stars | ★ 11 |
| Last updated | July 12, 2026 |
| Repository | fastapi-practices/skills ↗ |
How do I structure a FastAPI backend cleanly?
Helps with ai & agent building tasks.
Who is it for?
fba is best for Python developers building or refactoring FastAPI HTTP APIs who want framework-aligned conventions.
Skip if: fba is not for teams that are not using FastAPI or are only doing frontend-only work.
When should I use this skill?
Invoke fba when a task mentions FastAPI routes, routers, dependencies, Pydantic models, OpenAPI, or async endpoint patterns.
What you get
A clearer FastAPI project structure, consistent route and dependency patterns, and improved request/response model usage.
Files
FastAPI Best Architecture
Official documentation: https://fastapi-practices.github.io/fastapi_best_architecture_docs/
Core Architecture
Project adopts Three-tier architecture:
| Layer | Responsibility |
|---|---|
| API | Route processing, parameter validation, and response return |
| Schema | Data transfer objects, request/response data structure definitions |
| Service | Business logic, data processing, exception handling |
| CRUD | Database operations (inherits CRUDPlus) |
| Model | ORM models (inherits Base) |
Development Workflow
1. Define database models (model) 2. Define data validation models (schema) 3. Define routes (router) 4. Write business logic (service) 5. Write database operations (crud)
Detailed Guides
| Module | Document |
|---|---|
| API | references/api.md |
| Schema | references/schema.md |
| Model | references/model.md |
| Naming | references/naming.md |
| Plugin | references/plugin.md |
| Coding Style | references/coding-style.md |
| Config | references/config.md |
CLI
Execute fba -h for more details.
API Reference
Route Structure
Routes in fba follow RESTful API conventions
backend
├── app
│ ├── xxx # Custom app (Contains sub-packages).
│ │ └── api
│ │ ├── v1
│ │ │ └── xxx # Sub-package
│ │ │ ├── __init__.py # Routes in the xxx.py file within this file are registered within the subpack.
│ │ │ ├── xxx.py
│ │ │ └── ...
│ │ ├── __init__.py
│ │ └── router.py # Register the routes in the __init__.py files of all sub-packages within this file.
│ └── xxx # Custom app (No sub-packages are included).
│ └── api
│ ├── v1
│ │ ├── __init__.py # Do nothing.
│ │ ├── xxx.py
│ │ └── ...
│ ├── __init__.py
│ └── router.py # Register all routes from the xxx.py files within this file.
├── __init__.py
└── router.py # Register all routes in the router.py file under the app directory within this file.Route Import Rules
All API route parameters should be uniformly named router. When importing, always use as aliases to avoid conflicts:
from backend.app.admin.api.v1.sys.user import router as user_routerRESTful Route Conventions
GET /api/v1/resources/all # All (non-paginated)
GET /api/v1/resources # List (paginated)
GET /api/v1/resources/{pk} # Details
POST /api/v1/resources # Create
PUT /api/v1/resources/{pk} # Update
DELETE /api/v1/resources/{pk} # Delete
DELETE /api/v1/resources # Batch deleteDatabase Transaction
CurrentSession (Read-only Session)
Used for query operations:
@router.get('/users')
async def get_all_users(db: CurrentSession) -> ResponseModel:
data = await user_service.get_all(db=db)
return response_base.success(data=data)CurrentSessionTransaction (Transaction Session)
Used for create/update/delete operations:
@router.post('/users')
async def create_user(db: CurrentSessionTransaction, obj: CreateApiParam) -> ResponseModel:
await user_service.create(db=db, obj=obj)
return response_base.success()Manual Transaction (begin)
Used for scenarios that need to start a transaction at any point:
async with async_db_session.begin() as db:
...---
Response Standards
Response Models
No data response
@router.create('/users')
async def create_user(db: CurrentSessionTransaction, obj: CreateApiParam) -> ResponseModel:
await user_service.create(db=db, obj=obj)
return response_base.success()With data response
@router.get('/{pk}')
async def get_user(db: CurrentSession, pk: int) -> ResponseSchemaModel[GetApiDetail]:
data = await user_service.get(db=db, pk=pk)
return response_base.success(data=data)Response Methods
| Method | Purpose | Default Response |
|---|---|---|
response_base.success() | Success response | {"code": 200, "msg": "Request successful", "data": null} |
response_base.fail() | Failure response | {"code": 400, "msg": "Request error", "data": null} |
response_base.fast_success() | High-performance response (large JSON) | Same as success, but skips Pydantic validation |
Camel Case Response
To automatically convert response data to lowerCamelCase (e.g., created_time → createdTime), modify backend/common/schema.py:
from pydantic.alias_generators import to_camel
class SchemaBase(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
alias_generator=to_camel,
)After configuration, response data will be automatically converted.
JWT Authentication
API Authentication
@router.get('/users', summary='获取 API 列表', dependencies=[DependsJwtAuth])
async def get_users(db: CurrentSession) -> ResponseModel:
...Token Authorization Methods
The built-in token authorization in fba follows RFC 6750:
- Swagger Login: Quick authorization method, used for debugging only
- Captcha Login: Login authorization implemented with the frontend
---
RBAC Permissions
Role-Menu Mode (Default)
@router.post(
'/users',
summary='创建 API',
dependencies=[
Depends(RequestPermission('sys:user:add')),
DependsRBAC,
],
)
async def create_user(db: CurrentSessionTransaction, obj: CreateApiParam) -> ResponseModel:
...Permission Identifier Format
module:resource:action, for example:
sys:user:add- Add usersys:user:edit- Edit usersys:user:del- Delete user
Rate Limiting
Single rule: max 60 requests per minute
from pyrate_limiter import Duration, Rate
from backend.utils.limiter import RateLimiter
@app.get(
"/example",
dependencies=[Depends(RateLimiter(Rate(5, Duration.MINUTE)))]
)
async def example():
...Multi-rule compound rate limiting: 10 per second + 100 per minute
from pyrate_limiter import Duration, Rate
from backend.utils.limiter import RateLimiter
@app.post(
"/heavy",
dependencies=[
Depends(
RateLimiter(
Rate(10, Duration.SECOND),
Rate(100, Duration.MINUTE),
)
)
]
)
async def heavy_endpoint():
...I18n
Usage Syntax
Chain-style access to get field values from the language pack
msg = t('response.success')Language Pack Location
backend/locale directory, supports .json and .yaml/.yml files
Dynamic Switching
Automatically retrieves the Accept-Language parameter from the request header
Coding Style Reference
Fully comply with PEP 8 and follow the requirements below.
Import Rules
- Each import statement should import only one module.
- Do not use
from xxx import *. - Use absolute imports and avoid relative imports.
- Do not add shebangs such as
#!/usr/bin/env python3. - Do not add encoding declarations such as
# -*- coding: utf-8 -*-. - Do not add file-level description comments.
- Do not define
__all__. - Do not add anything to
__init__.pyunless explicitly required.
Typing
- All function parameters and return values must be typed.
- Use
Annotatedwhen framework metadata is needed. - Use
|for union types.
from typing import Annotated
from fastapi import Path, Query
async def get_user(
db: CurrentSession,
pk: Annotated[int, Path(description='用户 ID')],
username: Annotated[str | None, Query(description='用户名')] = None,
) -> ResponseSchemaModel[GetUserDetail]:
...Async Handling
- Use
async/awaitfor all I/O operations in API, service, and CRUD layers.
class UserService:
"""用户服务类"""
@staticmethod
async def get(*, db: AsyncSession, pk: int) -> User:
"""
获取用户详情
:param db: 数据库会话
:param pk: 用户 ID
:return:
"""
user = await user_dao.get(db, pk)
if not user:
raise errors.NotFoundError(msg='用户不存在')
return userKeyword Arguments
- Service-layer methods must use keyword-only arguments.
- Use
*to force keyword arguments where appropriate.
class UserService:
"""用户服务类"""
@staticmethod
async def get(*, db: AsyncSession, pk: int) -> User:
...
@staticmethod
async def create(*, db: AsyncSession, obj: CreateUserParam) -> None:
...Function Definitions
- Prefer module-level functions: Logic that does not belong to any class should be defined as module-level functions rather than inside a class.
- Choose method type based on actual binding semantics (not habit):
- If a method calls another method in the same class, define it as an instance method and call it through
self. - If a method does not call sibling methods and does not depend on instance state, keep it as
@staticmethod. - If a method uses class-level behavior or class state, define it as
@classmethod. - If a method uses instance state, define it as an instance method with
self. - Avoid misleading definitions: Every method inside a class must explicitly use
self,cls, or@staticmethod. Do not omit them. - Private function extraction rules:
- Extract a private function only when it creates a clear responsibility boundary, provides real reusable value, or significantly improves readability and maintainability.
- Do not add private functions mechanically just because a function feels long.
- Do not wrap logic in a helper if it is called only once and does not create a clearer boundary.
- Avoid helpers that merely rename short code blocks, forward parameters, or hide obvious control flow.
- Avoid abstraction as decoration: Eliminate over-encapsulation, unnecessary helper layers, and utility dumping.
Documentation and Comments
Comments
- Use Chinese comments in project code.
- Only add comments when they provide real value.
- Do not add comments that merely restate the code.
if not user.status:
raise errors.AuthorizationError(msg='用户已被锁定')Docstring Format
- Use
reStructuredTextstyle. - Do not use
:raise:,:rtype:, or similar tags. - Use
:return:with no trailing description.
class UserService:
"""用户服务类"""
@staticmethod
async def get() -> User:
"""获取用户详情"""
...
@staticmethod
async def get(*, db: AsyncSession, pk: int) -> User:
"""
获取用户详情
:param db: 数据库会话
:param pk: 用户 ID
:return:
"""
...API Route Documentation
summaryis required.descriptionis optional.
@router.get(
'/{pk}',
summary='获取用户详情',
description='通过 ID 获取用户详细信息,包括角色和部门',
dependencies=[DependsJwtAuth],
)
async def get_user(
db: CurrentSession,
pk: Annotated[int, Path(description='用户 ID')],
) -> ResponseSchemaModel[GetUserDetail]:
...Function Body Spacing
Core Rule
Blank lines inside a function body are only allowed to mark a logical phase transition.
Blank lines are not visual decoration. They are part of code structure.
Rules
- Only use blank lines to separate logical phases.
- Do not insert blank lines inside the same phase.
- Short functions with a single linear flow may contain no blank lines.
- Use at most one blank line between phases.
- Do not use multiple consecutive blank lines.
Logical Phases
Typical logical phases include:
- input parsing
- parameter validation
- permission validation
- state validation
- intermediate data preparation
- core business execution
- persistence or side effects
- return value assembly
Only add a blank line when moving from one phase to another.
Additional Requirements
- Consecutive existence checks, permission checks, type checks, and state checks belong to the same phase and must not be split by blank lines.
- Multiple lines that prepare the same target, such as
payload,run_input,query, orpersistence, belong to the same phase and must not be split by blank lines. - Do not add blank lines inside
if,else,try,except,with,for, orwhileblocks unless there is a real phase transition inside the block. - A blank line before
returnis only allowed whenreturnstarts a distinct final phase. - Comments do not justify blank lines by themselves. A blank line is only valid if the comment marks a real new phase.
Examples
Correct:
class UserService:
"""用户服务类"""
@staticmethod
async def get(*, db: AsyncSession, pk: int) -> User:
"""
获取用户详情
:param db: 数据库会话
:param pk: 用户 ID
:return:
"""
user = await user_dao.get(db, pk)
if not user:
raise errors.NotFoundError(msg='用户不存在')
if not user.status:
raise errors.AuthorizationError(msg='用户已被锁定')
data = await user_dao.get_detail(db, pk)
return dataIncorrect:
class UserService:
"""用户服务类"""
@staticmethod
async def get(*, db: AsyncSession, pk: int) -> User:
"""
获取用户详情
:param db: 数据库会话
:param pk: 用户 ID
:return:
"""
user = await user_dao.get(db, pk)
if not user:
raise errors.NotFoundError(msg='用户不存在')
if not user.status:
raise errors.AuthorizationError(msg='用户已被锁定')
data = await user_dao.get_detail(db, pk)
return dataRecommended Density
- Single linear short function:
0blank lines - Typical function:
1to3blank lines - Complex function: avoid more than
4blank lines - If a function needs
5+internal phase splits, reconsider its responsibility and split the function instead
Code Formatting
Ruff
- The project uses
Rufffor formatting and linting. Ruffconfiguration is defined inpyproject.toml.Ruffcan enforce structural blank-line rules, but it cannot enforce logical phase spacing inside function bodies.- Function body spacing must be maintained through code review and disciplined implementation.
Pre-commit
Built-in CLI:
fba formatGeneric commands:
ruff formatruff check --fix --unsafe-fixesConfiguration Reference
Configuration file location: backend/core/conf.py
Configurations marked with env are environment variable configurations
Database Model Standards
Model Base Class
- Explicitly specify table name (
__tablename__) - Primary key must be explicitly defined
from sqlalchemy.orm import Mapped, mapped_column
from backend.common.model import Base, id_key
class MyModel(Base):
"""模型表"""
__tablename__ = 'my_model'
id: Mapped[id_key] = mapped_column(init=False)
name: Mapped[str] = mapped_column(comment='名称')
status: Mapped[int] = mapped_column(default=1, comment='状态')Field Types
import sqlalchemy as sa
from backend.common.model import TimeZone, UniversalTextString (common lengths: 32, 64, 128, 256, 512)
name: Mapped[str] = mapped_column(sa.String(64), comment='名称')Nullable string
email: Mapped[str | None] = mapped_column(sa.String(256), default=None, comment='邮箱')Integer
status: Mapped[int] = mapped_column(default=1, comment='状态')Boolean
is_active: Mapped[bool] = mapped_column(default=True, comment='是否激活')Datetime (timezone compatible)
event_time: Mapped[datetime] = mapped_column(TimeZone, comment='事件时间')Long text (MySQL/PostgreSQL compatible)
content: Mapped[str] = mapped_column(UniversalText, comment='内容')Unique index
username: Mapped[str] = mapped_column(sa.String(64), unique=True, index=True, comment='用户名')Primary Key Modes
Configured via DATABASE_PK_MODE:
- autoincrement: Auto-increment ID (default)
- snowflake: Snowflake algorithm ID
⚠️ Warning: Do not arbitrarily switch primary key modes, otherwise it will cause fatal issues!
Database Migration
Generate migration script
fba alembic revision --autogenerate -m "描述信息"Execute migration
fba alembic upgrade headRollback
fba alembic downgrade -1Complete Example
import sqlalchemy as sa
from sqlalchemy.orm import Mapped, mapped_column
from backend.common.model import Base, id_key, TimeZone, UniversalText
class Article(Base):
"""文章表"""
__tablename__ = 'sys_article'
id: Mapped[id_key] = mapped_column(init=False)
title: Mapped[str] = mapped_column(sa.String(256), comment='标题')
content: Mapped[str] = mapped_column(UniversalText, comment='内容')
author_id: Mapped[int] = mapped_column(sa.BigInteger, index=True, comment='作者ID')
status: Mapped[int] = mapped_column(default=1, comment='状态(0草稿 1发布)')
published_at: Mapped[datetime | None] = mapped_column(TimeZone, default=None, comment='发布时间')
view_count: Mapped[int] = mapped_column(default=0, comment='浏览次数')Naming Conventions
File and Directory Naming
All lowercase, separated by underscores.
crud_user.pyuser_service.py
Class Naming
All PascalCase:
class UserService:
...
class CRUDUser:
...
class User:
...Schema Naming And Definition Order
Following these naming conventions:
| Type | Naming Pattern | Example |
|---|---|---|
| Base Schema | XxxSchemaBase(SchemaBase) | UserSchemaBase |
| API param | XxxParam() | UserParam |
| Create param | CreateXxxParam() | CreateUserParam |
| Update param | UpdateXxxParam() | UpdateUserParam |
| Batch delete param | DeleteXxxParam() | DeleteUserParam |
| Get details | GetXxxDetail() | GetUserDetail |
| Get details (join) | GetXxxWithJoinDetail() | GetUserWithJoinDetail |
| Get details (relation) | GetXxxWithRelationDetail() | GetUserWithRelationDetail |
| Get tree | GetXxxTree() | GetMenuTree |
API Function Naming And Definition Order
Lowercase with underscores, paginated lists use _paginated suffix:
| Operation | Naming Pattern | Example |
|---|---|---|
| Get all | get_all_xxxs | get_all_users |
| Paginated list | get_xxxs_paginated | get_users_paginated |
| Get details | get_xxx | get_user |
| Create | create_xxx | create_user |
| Update | update_xxx | update_user |
| Delete | delete_xxx | delete_user |
| Batch delete | delete_xxxs | delete_users |
Service Method Naming And Definition Order
Following these naming conventions:
| Method | Purpose |
|---|---|
get_all() | Get all |
get() | Get details |
get_list() | Get list (paginated) |
create() | Create |
update() | Update |
delete() | Delete |
CRUD Method Naming And Definition Order
Following these naming conventions:
| Method | Purpose |
|---|---|
get() | Get/query details |
get_by_xxx() | Get/query details by xxx |
get_select() | Get/query list expression |
get_list() | Get/query list |
get_all() | Get/query all |
get_with_join() | Join query (join) |
get_with_relation() | Relation query (relationship) |
get_children() | Sub-query |
create() | Create |
update() | Update |
delete() | Delete |
Plugin Development Standards
Plugin Types
App-level Plugin
An app-level plugin is injected into the system like a normal application. In fba, first-level folders under app are treated as applications, and the same rule applies to app-level plugins.
App-level plugins must follow the normal route structure completely.
[app]
router = ['v1']Extend-level Plugin
An extend-level plugin is injected into an existing application under the app directory.
Extend-level plugins must copy the target application's api directory structure 1:1.
[app]
extend = 'admin'Plugin Route Injection
If a plugin satisfies the plugin development requirements, all routes in the plugin are automatically injected into the FastAPI application.
Startup time can increase as the number of plugins grows because fba parses all plugins in real time before each startup.
App-level Routes
Develop routes according to the standard fba route structure.
Extend-level Routes
Replicate the existing application's api directory structure 1:1. For example, the built-in notice plugin extends an existing application by mirroring its API layout.
Database Compatibility
Official fba implementations support both MySQL and PostgreSQL.
Third-party plugins are not required to support both databases, but plugin authors should declare supported databases in plugin.toml.
For cross-database SQLAlchemy compatibility, use SQLAlchemy 2.0 mechanisms such as TypeDecorator and with_variant.
Backend Plugin Directory Structure
Plugins are placed under backend/plugin.
xxx # Plugin name
├── api # API routes
├── crud # CRUD
├── model # Models
│ ├── __init__.py # Import all model classes here
│ └── ...
├── schema # Data transfer schemas
├── service # Services
├── sql # Recommended when the plugin executes SQL
│ ├── mysql
│ │ ├── destroy.sql # Auto-increment ID cleanup, executed on uninstall
│ │ ├── destroy_snowflake.sql # Snowflake ID cleanup
│ │ ├── init.sql # Auto-increment ID initialization, executed on install
│ │ └── init_snowflake.sql # Snowflake ID initialization
│ └── postgresql
│ └── ... # Same file names as mysql
├── utils # Utilities
├── .env.example # Environment variables
├── __init__.py # Kept as a Python package
├── ... # More content, e.g. enums.py
├── hooks.py # Optional plugin hook functions
├── plugin.toml # Plugin configuration file
├── README.md # Usage instructions and contact information
└── requirements.txt # Dependency packagesplugin.toml Configuration
Every plugin must contain plugin.toml.
Common Plugin Metadata
[plugin]
# Icon path inside the plugin repository or an icon URL
icon = 'assets/icon.svg'
# Short summary
summary = ''
# Version
version = ''
# Description
description = ''
# Author
author = ''
# Supported tags: ai, mcp, agent, auth, storage, notification, task, payment, other
tags = ['']
# Supported databases: mysql, postgresql
database = ['']App-level Plugin Configuration
# Plugin metadata
[plugin]
icon = 'assets/icon.svg'
summary = ''
version = ''
description = ''
author = ''
tags = ['']
database = ['']
# Application configuration
[app]
# Final router instance names.
# See backend/app/admin/api/router.py; usually named v1.
router = ['v1']
# Code-level configuration keys in uppercase.
# Optional. See Hot-pluggable Configuration.
[settings]
XXX = 'value'Extend-level Plugin Configuration
# Plugin metadata
[plugin]
icon = 'assets/icon.svg'
summary = ''
version = ''
description = ''
author = ''
tags = ['']
database = ['']
# Application configuration
[app]
# Target application folder name
extend = 'application_folder_name'
# API configuration
[api.xxx]
# xxx is the file name under the plugin api directory without extension.
# Example: for notice.py, use [api.notice].
# Multiple API files require multiple [api.xxx] sections.
# Route prefix, must start with '/'.
prefix = ''
# Tags for Swagger documentation
tags = ''
# Code-level configuration keys in uppercase.
# Optional. See Hot-pluggable Configuration.
[settings]
XXX = 'value'Global Configuration
fba uses one global configuration file, similar to Django.
During development, add plugin global configuration to backend/core/conf.py for typing hints and explicit configuration management.
##################################################
# [ Plugin ] email
##################################################
# .env
EMAIL_USERNAME: str
EMAIL_PASSWORD: str
# Basic configuration
EMAIL_HOST: str
EMAIL_PORT: int
EMAIL_SSL: bool
EMAIL_CAPTCHA_REDIS_PREFIX: str
EMAIL_CAPTCHA_EXPIRE_SECONDS: intThe structure should contain:
1. Plugin configuration comment block. 2. Plugin environment variable declarations and comments. 3. Plugin basic configuration declarations and comments.
Published plugins cannot modify the user's backend/core/conf.py directly. Document required global configuration in the plugin README.md.
Hot-pluggable Configuration
Since fba v1.13.0, plugins can adapt to hot-pluggable installation when configured correctly.
Plugin Environment Variables
If the plugin requires environment variables, add .env.example in the plugin root directory.
# [ Plugin ] email
EMAIL_USERNAME: str
EMAIL_PASSWORD: strPlugin Basic Configuration
If the plugin requires basic configuration, add uppercase configuration keys under [settings] in plugin.toml.
Do not confuse plugin.toml settings with backend/core/conf.py declarations. Their formats are different.
[settings]
EMAIL_HOST = 'smtp.qq.com'
EMAIL_PORT = 465
EMAIL_SSL = true
EMAIL_CAPTCHA_REDIS_PREFIX = 'fba:email:captcha'
EMAIL_CAPTCHA_EXPIRE_SECONDS = 180After .env.example and [settings] are configured, plugins installed through CLI or Git can adapt to hot-pluggable behavior without extra manual changes, provided the plugin has no additional integration requirements.
Global Configuration Priority
Configuration priority flows in this order:
System environment variables -> .env -> conf.py -> plugin [settings]Development recommendation:
- Add global configuration declarations in
backend/core/conf.pyduring development. - Document those declarations in the published plugin
README.md. - Use this approach when IDE typing hints are important for plugin developers or users.
Hook Functions
Since fba v1.13.3, plugins support hook functions for more flexible configuration and reduced manual adaptation.
Hook functions must be defined in hooks.py at the plugin root.
fba also provides helper functions in backend/plugin/patching.py for plugin configuration.
lifespan
Defines a FastAPI lifespan function. It is automatically registered before application startup.
setup
Defines startup logic. Both synchronous and asynchronous setup functions are supported. The function is automatically executed before application startup.
Frontend Plugin Directory Structure
Frontend plugins are placed under apps/web-antd/src/plugins.
xxx # Plugin name
├── api # API client code
│ └── index.ts
├── langs # I18n resources
│ ├── en-US
│ │ └── plugin_name.json
│ └── zh-CN
│ └── plugin_name.json
├── public
│ └── images # Page preview images
├── routes # Routes
│ └── index.ts
├── views # Views
│ ├── index.vue
│ └── ...
├── ... # More content
└── plugin.toml # Plugin configuration fileFrontend plugin.toml Configuration
Every frontend plugin must contain plugin.toml.
[plugin]
# Icon path inside the plugin repository or an icon URL
icon = 'assets/icon.svg'
# Short summary
summary = ''
# Version
version = ''
# Description
description = ''
# Author
author = ''
# Supported tags: ai, mcp, agent, auth, storage, notification, task, payment, other
tags = ['']Plugin README Convention
When creating, reviewing, or updating a plugin README.md, follow these rules strictly.
Use the canonical plugin README style represented by the ai plugin README.
Required Structure
A plugin README.md must contain only the following content, in this order:
1. Title 2. Description 3. Plugin type 4. Configuration 5. Usage 6. Uninstall 7. Contact
Use the exact section headings and fixed labels from the canonical output contract below.
Canonical Output Contract
The generated plugin README.md must use this exact localized structure and fixed labels:
````md
<Plugin display name>
<Short description>
插件类型
- <应用级插件 or 扩展级插件>
配置说明
在 backend/.env 中添加以下内容:
<backend/.env variables>插件目录下 plugin.toml 的 [settings] 中包含以下内容:
[settings]
<plugin settings>在 backend/core/conf.py 中添加以下内容:
##################################################
# [ Plugin ] <plugin_name>
##################################################
# .env
<env field definitions>
# 基础配置(in plugin.toml)
<plugin.toml setting field definitions>当前项目的 backend/core/conf.py 已包含以下字段:
<existing plugin field definitions>使用方式
1. <core usage step>
卸载说明
- <cleanup item>
联系方式
- 作者:
<author> - 反馈方式:提交 Issue 或 PR
````
Use only the configuration lead-in lines that apply to real content.
Use either the backend/core/conf.py add-content lead-in or the already-present-fields lead-in, not both.
Every included configuration source must follow this exact pattern: lead-in sentence, one blank line, fenced code block, one blank line before the next configuration source.
Section Rules
Title
Use the plugin display name as the H1 title.
Example:
# OAuth2Description
Place a short description immediately below the title.
Keep it concise and use this part to explain the plugin capabilities.
Capability summaries may be written as short paragraphs or short bullet lists directly under the title.
Do not create a separate feature section for plugin capabilities.
Plugin Type
Use the exact heading from the canonical output contract.
Only describe the plugin type.
Use a short bullet list with the exact canonical plugin type wording.
For extend-level plugins, include the target app name such as admin when useful.
Do not include route prefixes, API mount paths, or endpoint information.
Configuration
Use the exact heading from the canonical output contract.
The configuration section must contain only canonical configuration blocks.
Do not add an overview sentence before the first configuration block.
Always present configuration in this order:
1. What to add in backend/.env 2. What is contained in [settings] of the plugin directory plugin.toml 3. What to add in backend/core/conf.py, or which plugin fields are already present there
Only include configuration sources that actually have meaningful content.
Do not add no-op placeholder lines for omitted configuration sources.
Do not add explanatory prose before or after any configuration code block.
Do not add per-key descriptions, usage notes, defaults explanations, or conditional instructions in this section.
Use the exact configuration lead-in sentences from the canonical output contract for .env, plugin.toml, and backend/core/conf.py blocks.
Each configuration block must use the exact fenced code language from the canonical output contract:
envforbackend/.envtomlforplugin.tomlpythonforbackend/core/conf.py
The backend/.env code block must contain only the variables that should be added to backend/.env.
The plugin.toml code block must include the [settings] header and only the settings from the plugin directory plugin.toml.
If the current project already contains the required plugin fields in backend/core/conf.py, state that directly before the code block.
When the plugin has corresponding fields in backend/core/conf.py, include the exact field definitions or explain that they are already present in the current project.
When you show backend/core/conf.py content, keep the actual plugin grouping style consistent with the real file, including separator comments, plugin-name comments, .env comments, and plugin basic-configuration comments when they exist.
Show only fields that belong to the current plugin in the backend/core/conf.py block.
Do not include unrelated global plugin fields such as installer, Redis prefix, or package-index settings in a plugin README.
Use direct instruction wording.
Avoid conditional phrasing such as if needed, when enabled, or localized equivalents in the configuration section.
For plugin.toml, use the canonical lead-in that says the plugin directory plugin.toml contains the following [settings] content rather than instructing the reader to add that content.
Usage
Use the exact heading from the canonical output contract.
Describe only the core usage flow in plain language.
Keep this section short and focused.
Do not list API endpoints, route prefixes, request paths, or interface details.
Uninstall
Use the exact heading from the canonical output contract.
Describe which related configuration should be removed and what integrations should be cleaned up.
Use high-level cleanup wording by default.
Do not enumerate specific configuration keys in the uninstall section unless the user explicitly asks for them.
Use short bullet lists for multiple cleanup items.
Contact
Use the exact heading from the canonical output contract.
Provide author and feedback entries with the exact fixed labels from the canonical output contract.
Forbidden Content
Do not include the following in plugin README.md files:
- Route prefixes
- API endpoint lists
- Interface descriptions
- Feature sections
- Warning sections
- Note sections
- FAQ sections
- Extra headings outside the required structure
- Non-canonical English section headings such as
Plugin Type,Configuration,Usage,Uninstall, orContact - Non-canonical English contact labels such as
Author:
Punctuation Rule
Do not end prose lines or list items with CJK full stop punctuation.
This rule does not apply to code blocks.
Style Rule
Keep wording concise, direct, and operational.
Prefer short paragraphs and short numbered lists.
Important Notes
Unless necessary, avoid referencing existing architecture methods from plugin code.
If existing architecture methods change, plugins that depend on those methods must be updated, otherwise they can break.
Schema Reference
Base Class Usage
All Schemas should inherit from SchemaBase:
class UserSchemaBase(SchemaBase):
"""用户基础模型"""
username: str
email: str | None = NoneField Definition
Required Fields
It is not recommended to set the default value of required fields to ....
username: str = Field(description='用户名')description Parameter
It is recommended to add a description parameter to all fields, which is very useful for API documentation:
class CreateUserParam(SchemaBase):
username: str = Field(description='用户名')
email: str | None = Field(None, description='邮箱')
status: int = Field(default=1, description='状态(0禁用 1启用)')Optional Fields
Update params typically have all fields as optional:
class UpdateUserParam(SchemaBase):
username: str | None = Field(None, description='用户名')
email: str | None = Field(None, description='邮箱')
status: int | None = Field(None, description='状态')Complete Example
from datetime import datetime
from pydantic import Field
from backend.common.schema import SchemaBase
class ArticleSchemaBase(SchemaBase):
"""文章基础模型"""
title: str = Field(description='标题')
content: str = Field(description='内容')
status: StatusType = Field(description='状态')
class CreateArticleParam(ArticleSchemaBase):
"""创建文章参数"""
class UpdateArticleParam(ArticleSchemaBase):
"""更新文章参数"""
class DeleteArticleParam(SchemaBase):
"""批量删除文章参数"""
ids: list[int] = Field(description='文章 ID 列表')
class GetArticleDetail(ArticleSchemaBase):
"""文章详情响应"""
id: int = Field(description='文章ID')
created_time: datetime = Field(description='创建时间')
updated_time: datetime | None = Field(None, description='更新时间')
class GetArticleWithAuthorDetail(GetArticleDetail):
"""文章详情响应(含作者信息)"""
author_name: str = Field(description='作者名称')Camel Case Response
See the api reference guide for details.
Related skills
How it compares
Pick this when your task explicitly targets FastAPI conventions and OpenAPI/Pydantic-driven API design rather than general Python architecture guidance.
FAQ
What does fba help with in FastAPI?
fba helps developers apply FastAPI-oriented backend practices, typically around route organization, dependency patterns, and request/response validation that plays well with OpenAPI. fba is most relevant when a task explicitly mentions FastAPI endpoints, routers, or Pydantic mode
When should I invoke fba during development?
fba should be invoked when you are implementing or refactoring a Python HTTP API built with FastAPI and you need consistent conventions for routers, dependencies, and schemas. fba is a fit when your prompt includes FastAPI, Pydantic, OpenAPI, or async endpoint behavior.
Is fba a good fit for non-FastAPI Python services?
fba is primarily a FastAPI-practices skill, so it is not the best fit for Django-only, Flask-only, or non-web Python codebases. fba is most useful when the framework-specific details of FastAPI, Pydantic schemas, and OpenAPI output matter.