Skip to content

项目模板

快速启动基于 OpenAPI 的项目开发

项目模板概览

我们提供了多种技术栈的项目模板,帮助你快速开始 API-First 开发:

使用项目生成器

安装项目生成器

bash
# 安装全局命令行工具
npm install -g @api-first/project-generator

# 或使用 npx(无需安装)
npx @api-first/project-generator create my-api

交互式创建项目

bash
$ api-first create my-awesome-api

? 选择项目类型 (Use arrow keys)
 API 服务端
  API 客户端
  全栈应用
  
? 选择后端技术栈
 Java Spring Boot
  Python FastAPI
  Node.js Express
  Go Gin
  
? 选择数据库
 PostgreSQL
  MySQL
  MongoDB
  SQLite
  
? 需要哪些功能? (Press <space> to select)
 用户认证 (JWT)
 API 文档 (Swagger/ReDoc)
 数据验证
 错误处理
 日志记录
 单元测试
 Docker 支持
  
? 初始化 Git 仓库? (Y/n) Y

正在创建项目...
 下载模板
 安装依赖
 初始化数据库
 生成示例代码
 配置开发环境

项目创建成功!

开始使用:
  cd my-awesome-api
  npm run dev

Java Spring Boot 模板

项目结构

spring-boot-api-template/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/example/api/
│   │   │       ├── config/         # 配置类
│   │   │       ├── controller/     # REST 控制器
│   │   │       ├── service/        # 业务逻辑
│   │   │       ├── repository/     # 数据访问
│   │   │       ├── model/          # 数据模型
│   │   │       ├── dto/            # 数据传输对象
│   │   │       ├── mapper/         # 对象映射
│   │   │       ├── exception/      # 异常处理
│   │   │       ├── security/       # 安全配置
│   │   │       └── Application.java
│   │   └── resources/
│   │       ├── api/               # OpenAPI 规范
│   │       │   └── openapi.yaml
│   │       ├── application.yml    # 应用配置
│   │       ├── application-dev.yml
│   │       └── application-prod.yml
│   └── test/
│       └── java/
│           └── com/example/api/
│               ├── integration/   # 集成测试
│               ├── unit/         # 单元测试
│               └── contract/     # 契约测试
├── pom.xml
├── Dockerfile
├── docker-compose.yml
├── .gitlab-ci.yml
└── README.md

核心依赖配置

xml
<!-- pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
    </parent>
    
    <groupId>com.example</groupId>
    <artifactId>api-template</artifactId>
    <version>1.0.0</version>
    
    <properties>
        <java.version>17</java.version>
        <openapi-generator.version>7.1.0</openapi-generator.version>
        <springdoc.version>2.2.0</springdoc.version>
        <mapstruct.version>1.5.5.Final</mapstruct.version>
    </properties>
    
    <dependencies>
        <!-- Spring Boot Starters -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        
        <!-- OpenAPI -->
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>${springdoc.version}</version>
        </dependency>
        
        <!-- Database -->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-core</artifactId>
        </dependency>
        
        <!-- Utilities -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>
        
        <!-- Testing -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>testcontainers</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>postgresql</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <!-- OpenAPI Generator Plugin -->
            <plugin>
                <groupId>org.openapitools</groupId>
                <artifactId>openapi-generator-maven-plugin</artifactId>
                <version>${openapi-generator.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <inputSpec>${project.basedir}/src/main/resources/api/openapi.yaml</inputSpec>
                            <generatorName>spring</generatorName>
                            <apiPackage>com.example.api.controller</apiPackage>
                            <modelPackage>com.example.api.model</modelPackage>
                            <configOptions>
                                <delegatePattern>true</delegatePattern>
                                <useSpringBoot3>true</useSpringBoot3>
                            </configOptions>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

示例代码

java
// Application.java
@SpringBootApplication
@EnableConfigurationProperties
@EnableJpaAuditing
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// SecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/v1/auth/**").permitAll()
                .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
                .requestMatchers("/actuator/health").permitAll()
                .anyRequest().authenticated()
            )
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
            .exceptionHandling(exception -> exception
                .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
            );
            
        return http.build();
    }
}

// GlobalExceptionHandler.java
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {
        ErrorResponse error = ErrorResponse.builder()
            .code("RESOURCE_NOT_FOUND")
            .message(ex.getMessage())
            .timestamp(LocalDateTime.now())
            .build();
            
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }
    
    @ExceptionHandler(ValidationException.class)
    public ResponseEntity<ErrorResponse> handleValidation(ValidationException ex) {
        ErrorResponse error = ErrorResponse.builder()
            .code("VALIDATION_ERROR")
            .message("请求参数验证失败")
            .details(ex.getErrors())
            .timestamp(LocalDateTime.now())
            .build();
            
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
    }
}

// BaseEntity.java
@MappedSuperclass
@Getter
@Setter
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
    
    @Id
    @GeneratedValue(generator = "uuid2")
    @GenericGenerator(name = "uuid2", strategy = "uuid2")
    private String id;
    
    @CreatedDate
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdAt;
    
    @LastModifiedDate
    @Column(nullable = false)
    private LocalDateTime updatedAt;
    
    @Version
    private Long version;
}

Python FastAPI 模板

项目结构

fastapi-template/
├── app/
│   ├── api/
│   │   ├── __init__.py
│   │   ├── deps.py           # 依赖注入
│   │   └── v1/
│   │       ├── __init__.py
│   │       ├── endpoints/    # API 端点
│   │       │   ├── __init__.py
│   │       │   ├── auth.py
│   │       │   └── users.py
│   │       └── router.py
│   ├── core/
│   │   ├── __init__.py
│   │   ├── config.py        # 配置
│   │   ├── security.py      # 安全工具
│   │   └── database.py      # 数据库配置
│   ├── models/              # SQLAlchemy 模型
│   │   ├── __init__.py
│   │   ├── base.py
│   │   └── user.py
│   ├── schemas/             # Pydantic 模式
│   │   ├── __init__.py
│   │   └── user.py
│   ├── services/            # 业务逻辑
│   │   ├── __init__.py
│   │   └── user.py
│   ├── db/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   └── session.py
│   ├── tests/               # 测试
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   ├── unit/
│   │   └── integration/
│   ├── alembic/             # 数据库迁移
│   │   └── versions/
│   ├── __init__.py
│   └── main.py             # 应用入口
├── openapi/
│   └── openapi.yaml        # OpenAPI 规范
├── scripts/
│   ├── generate_client.py
│   └── init_db.py
├── requirements.txt
├── requirements-dev.txt
├── Dockerfile
├── docker-compose.yml
├── .env.example
├── alembic.ini
├── pyproject.toml
└── README.md

核心代码示例

python
# app/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
import logging

from app.core.config import settings
from app.api.v1.router import api_router
from app.db.session import engine
from app.models.base import Base

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 启动时执行
    logger.info("Starting up...")
    # 创建数据库表
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    yield
    # 关闭时执行
    logger.info("Shutting down...")

app = FastAPI(
    title=settings.PROJECT_NAME,
    version=settings.VERSION,
    openapi_url=f"{settings.API_V1_PREFIX}/openapi.json",
    docs_url="/docs",
    redoc_url="/redoc",
    lifespan=lifespan
)

# CORS 配置
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.BACKEND_CORS_ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 路由
app.include_router(api_router, prefix=settings.API_V1_PREFIX)

# 健康检查
@app.get("/health")
async def health_check():
    return {"status": "healthy", "version": settings.VERSION}

# app/core/config.py
from pydantic_settings import BaseSettings
from typing import List, Optional
from functools import lru_cache

class Settings(BaseSettings):
    PROJECT_NAME: str = "FastAPI Template"
    VERSION: str = "1.0.0"
    API_V1_PREFIX: str = "/api/v1"
    
    # 数据库配置
    DATABASE_URL: str = "postgresql+asyncpg://user:pass@localhost/db"
    
    # 安全配置
    SECRET_KEY: str = "your-secret-key"
    ALGORITHM: str = "HS256"
    ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
    
    # CORS
    BACKEND_CORS_ORIGINS: List[str] = ["http://localhost:3000"]
    
    # Redis
    REDIS_URL: Optional[str] = None
    
    class Config:
        env_file = ".env"
        case_sensitive = True

@lru_cache()
def get_settings():
    return Settings()

settings = get_settings()

# app/api/deps.py
from typing import Generator, Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.ext.asyncio import AsyncSession
from jose import jwt, JWTError

from app.db.session import async_session
from app.core.config import settings
from app.models.user import User
from app.services.user import UserService

oauth2_scheme = OAuth2PasswordBearer(tokenUrl=f"{settings.API_V1_PREFIX}/auth/login")

async def get_db() -> Generator:
    async with async_session() as session:
        try:
            yield session
        finally:
            await session.close()

async def get_current_user(
    db: AsyncSession = Depends(get_db),
    token: str = Depends(oauth2_scheme)
) -> User:
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
        user_id: str = payload.get("sub")
        if user_id is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
        
    user_service = UserService(db)
    user = await user_service.get_by_id(user_id)
    if user is None:
        raise credentials_exception
        
    return user

# app/models/base.py
from sqlalchemy import Column, DateTime, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import func
import uuid

Base = declarative_base()

class BaseModel(Base):
    __abstract__ = True
    
    id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    updated_at = Column(DateTime(timezone=True), onupdate=func.now(), server_default=func.now())

# app/schemas/base.py
from pydantic import BaseModel, ConfigDict
from datetime import datetime
from typing import Optional

class BaseSchema(BaseModel):
    model_config = ConfigDict(from_attributes=True)

class BaseResponseSchema(BaseSchema):
    id: str
    created_at: datetime
    updated_at: datetime

Docker 配置

dockerfile
# Dockerfile
FROM python:3.11-slim

WORKDIR /app

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# 安装 Python 依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 运行应用
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
yaml
# docker-compose.yml
version: '3.8'

services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql+asyncpg://postgres:postgres@db:5432/api_db
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
    volumes:
      - ./app:/app/app
    command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=api_db
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

  pgadmin:
    image: dpage/pgadmin4
    environment:
      - PGADMIN_DEFAULT_EMAIL=admin@example.com
      - PGADMIN_DEFAULT_PASSWORD=admin
    ports:
      - "5050:80"
    depends_on:
      - db

volumes:
  postgres_data:

Node.js Express 模板

项目结构

express-api-template/
├── src/
│   ├── api/
│   │   ├── controllers/    # 控制器
│   │   ├── middlewares/    # 中间件
│   │   ├── routes/         # 路由
│   │   └── validators/     # 验证器
│   ├── config/             # 配置
│   │   ├── database.ts
│   │   ├── logger.ts
│   │   └── swagger.ts
│   ├── models/             # 数据模型
│   ├── services/           # 业务逻辑
│   ├── types/              # TypeScript 类型
│   ├── utils/              # 工具函数
│   ├── app.ts              # Express 应用
│   └── server.ts           # 服务器入口
├── tests/
│   ├── unit/
│   ├── integration/
│   └── fixtures/
├── openapi/
│   └── openapi.yaml
├── scripts/
│   ├── generate-types.ts
│   └── seed-db.ts
├── package.json
├── tsconfig.json
├── .eslintrc.js
├── .prettierrc
├── jest.config.js
├── Dockerfile
├── docker-compose.yml
└── README.md

核心代码示例

typescript
// src/app.ts
import express, { Application } from 'express';
import cors from 'cors';
import helmet from 'helmet';
import compression from 'compression';
import morgan from 'morgan';
import swaggerUi from 'swagger-ui-express';
import { OpenAPIV3 } from 'openapi-types';

import { errorHandler } from './api/middlewares/errorHandler';
import { notFoundHandler } from './api/middlewares/notFoundHandler';
import { apiLimiter } from './api/middlewares/rateLimiter';
import routes from './api/routes';
import { logger } from './config/logger';
import { loadOpenAPISpec } from './config/swagger';

export const createApp = async (): Promise<Application> => {
  const app = express();

  // 基础中间件
  app.use(helmet());
  app.use(cors());
  app.use(compression());
  app.use(express.json());
  app.use(express.urlencoded({ extended: true }));
  
  // 日志
  app.use(morgan('combined', { stream: { write: (message) => logger.info(message.trim()) } }));
  
  // API 限流
  app.use('/api/', apiLimiter);
  
  // API 文档
  const openApiSpec = await loadOpenAPISpec();
  app.use('/docs', swaggerUi.serve, swaggerUi.setup(openApiSpec as OpenAPIV3.Document));
  
  // 健康检查
  app.get('/health', (req, res) => {
    res.json({ status: 'healthy', timestamp: new Date().toISOString() });
  });
  
  // API 路由
  app.use('/api/v1', routes);
  
  // 错误处理
  app.use(notFoundHandler);
  app.use(errorHandler);
  
  return app;
};

// src/server.ts
import { createApp } from './app';
import { connectDatabase } from './config/database';
import { logger } from './config/logger';
import { config } from './config/environment';

const startServer = async () => {
  try {
    // 连接数据库
    await connectDatabase();
    
    // 创建应用
    const app = await createApp();
    
    // 启动服务器
    const server = app.listen(config.port, () => {
      logger.info(`🚀 Server is running on port ${config.port}`);
      logger.info(`📚 API Documentation available at http://localhost:${config.port}/docs`);
    });
    
    // 优雅关闭
    const gracefulShutdown = async (signal: string) => {
      logger.info(`${signal} received. Starting graceful shutdown...`);
      
      server.close(() => {
        logger.info('HTTP server closed');
      });
      
      // 关闭数据库连接等资源
      // await closeDatabase();
      
      process.exit(0);
    };
    
    process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
    process.on('SIGINT', () => gracefulShutdown('SIGINT'));
    
  } catch (error) {
    logger.error('Failed to start server:', error);
    process.exit(1);
  }
};

startServer();

// src/api/middlewares/auth.ts
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { config } from '../../config/environment';

export interface AuthRequest extends Request {
  user?: {
    id: string;
    email: string;
    roles: string[];
  };
}

export const authenticate = async (
  req: AuthRequest,
  res: Response,
  next: NextFunction
): Promise<void> => {
  try {
    const token = req.headers.authorization?.replace('Bearer ', '');
    
    if (!token) {
      res.status(401).json({ error: 'Authentication required' });
      return;
    }
    
    const decoded = jwt.verify(token, config.jwtSecret) as any;
    req.user = {
      id: decoded.sub,
      email: decoded.email,
      roles: decoded.roles || []
    };
    
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
};

export const authorize = (...roles: string[]) => {
  return (req: AuthRequest, res: Response, next: NextFunction): void => {
    if (!req.user) {
      res.status(401).json({ error: 'Authentication required' });
      return;
    }
    
    const hasRole = roles.some(role => req.user!.roles.includes(role));
    
    if (!hasRole) {
      res.status(403).json({ error: 'Insufficient permissions' });
      return;
    }
    
    next();
  };
};

// src/api/controllers/base.controller.ts
import { Request, Response } from 'express';
import { validationResult } from 'express-validator';

export abstract class BaseController {
  protected validateRequest(req: Request, res: Response): boolean {
    const errors = validationResult(req);
    
    if (!errors.isEmpty()) {
      res.status(400).json({
        error: 'Validation failed',
        details: errors.array()
      });
      return false;
    }
    
    return true;
  }
  
  protected success(res: Response, data: any, statusCode: number = 200): void {
    res.status(statusCode).json({
      success: true,
      data
    });
  }
  
  protected error(res: Response, message: string, statusCode: number = 500, details?: any): void {
    res.status(statusCode).json({
      success: false,
      error: message,
      ...(details && { details })
    });
  }
}

快速启动脚本

一键启动脚本

bash
#!/bin/bash
# quick-start.sh

echo "🚀 API-First 项目快速启动"
echo "========================"

# 选择模板
PS3="请选择项目模板: "
options=("Java Spring Boot" "Python FastAPI" "Node.js Express" "退出")
select opt in "${options[@]}"
do
    case $opt in
        "Java Spring Boot")
            TEMPLATE="spring-boot"
            break
            ;;
        "Python FastAPI")
            TEMPLATE="fastapi"
            break
            ;;
        "Node.js Express")
            TEMPLATE="express"
            break
            ;;
        "退出")
            exit 0
            ;;
        *) echo "无效选项 $REPLY";;
    esac
done

# 输入项目名称
read -p "请输入项目名称: " PROJECT_NAME

# 克隆模板
echo "📦 下载项目模板..."
git clone https://github.com/api-first/templates.git temp_template
cp -r temp_template/$TEMPLATE $PROJECT_NAME
rm -rf temp_template

cd $PROJECT_NAME

# 初始化项目
echo "🔧 初始化项目..."
case $TEMPLATE in
    "spring-boot")
        ./mvnw clean install
        ;;
    "fastapi")
        python -m venv venv
        source venv/bin/activate
        pip install -r requirements.txt
        ;;
    "express")
        npm install
        ;;
esac

# 初始化 Git
echo "📝 初始化 Git 仓库..."
git init
git add .
git commit -m "Initial commit from API-First template"

# 启动说明
echo ""
echo "✅ 项目创建成功!"
echo ""
echo "开始开发:"
echo "  cd $PROJECT_NAME"
case $TEMPLATE in
    "spring-boot")
        echo "  ./mvnw spring-boot:run"
        ;;
    "fastapi")
        echo "  source venv/bin/activate"
        echo "  uvicorn app.main:app --reload"
        ;;
    "express")
        echo "  npm run dev"
        ;;
esac
echo ""
echo "查看 API 文档:"
echo "  http://localhost:8080/docs"

项目配置生成器

交互式配置文件生成

typescript
// scripts/generate-config.ts
import inquirer from 'inquirer';
import fs from 'fs/promises';
import yaml from 'js-yaml';

interface ProjectConfig {
  projectName: string;
  projectType: string;
  database: string;
  features: string[];
  authentication: string;
  deployment: string;
}

async function generateConfig() {
  const answers = await inquirer.prompt<ProjectConfig>([
    {
      type: 'input',
      name: 'projectName',
      message: '项目名称:',
      default: 'my-api'
    },
    {
      type: 'list',
      name: 'projectType',
      message: '项目类型:',
      choices: ['REST API', 'GraphQL API', 'WebSocket API', '混合']
    },
    {
      type: 'list',
      name: 'database',
      message: '数据库:',
      choices: ['PostgreSQL', 'MySQL', 'MongoDB', 'SQLite', 'None']
    },
    {
      type: 'checkbox',
      name: 'features',
      message: '选择功能:',
      choices: [
        { name: '用户认证', value: 'auth' },
        { name: '文件上传', value: 'upload' },
        { name: '邮件发送', value: 'email' },
        { name: '消息队列', value: 'queue' },
        { name: '缓存', value: 'cache' },
        { name: 'WebSocket', value: 'websocket' },
        { name: '定时任务', value: 'cron' },
        { name: '国际化', value: 'i18n' }
      ]
    },
    {
      type: 'list',
      name: 'authentication',
      message: '认证方式:',
      choices: ['JWT', 'OAuth2', 'API Key', 'Basic Auth', 'None'],
      when: (answers) => answers.features.includes('auth')
    },
    {
      type: 'list',
      name: 'deployment',
      message: '部署目标:',
      choices: ['Docker', 'Kubernetes', 'Serverless', 'Traditional']
    }
  ]);

  // 生成配置文件
  const config = generateProjectConfig(answers);
  
  // 保存配置
  await fs.writeFile('project-config.yaml', yaml.dump(config));
  await fs.writeFile('project-config.json', JSON.stringify(config, null, 2));
  
  console.log('✅ 配置文件已生成!');
  console.log('\n下一步:');
  console.log('  npx @api-first/generator init --config project-config.yaml');
}

function generateProjectConfig(answers: ProjectConfig) {
  const config: any = {
    name: answers.projectName,
    type: answers.projectType,
    version: '1.0.0',
    
    features: answers.features,
    
    database: answers.database !== 'None' ? {
      type: answers.database,
      host: 'localhost',
      port: getDatabasePort(answers.database),
      name: `${answers.projectName}_db`
    } : null,
    
    server: {
      port: 8080,
      cors: {
        enabled: true,
        origins: ['http://localhost:3000']
      }
    },
    
    openapi: {
      version: '3.0.3',
      info: {
        title: `${answers.projectName} API`,
        version: '1.0.0'
      }
    }
  };
  
  if (answers.features.includes('auth')) {
    config.authentication = {
      type: answers.authentication,
      secret: generateSecret(),
      expiresIn: '24h'
    };
  }
  
  if (answers.features.includes('cache')) {
    config.cache = {
      type: 'redis',
      host: 'localhost',
      port: 6379
    };
  }
  
  if (answers.deployment === 'Docker') {
    config.docker = {
      registry: 'docker.io',
      image: `${answers.projectName}:latest`
    };
  }
  
  return config;
}

function getDatabasePort(database: string): number {
  const ports: Record<string, number> = {
    PostgreSQL: 5432,
    MySQL: 3306,
    MongoDB: 27017,
    SQLite: 0
  };
  return ports[database] || 5432;
}

function generateSecret(): string {
  return require('crypto').randomBytes(32).toString('hex');
}

// 运行生成器
generateConfig().catch(console.error);

最佳实践

1. 项目初始化检查清单

  • [ ] OpenAPI 规范文件已创建
  • [ ] 数据库架构已设计
  • [ ] 环境变量已配置
  • [ ] Git 仓库已初始化
  • [ ] CI/CD 配置已添加
  • [ ] README 文档已更新
  • [ ] 测试框架已配置
  • [ ] 代码质量工具已设置
  • [ ] Docker 配置已完成
  • [ ] 日志系统已配置

2. 目录结构规范

project-root/
├── api/              # OpenAPI 规范
├── src/              # 源代码
├── tests/            # 测试代码
├── docs/             # 文档
├── scripts/          # 脚本
├── config/           # 配置文件
├── docker/           # Docker 相关
└── .github/          # GitHub 配置

3. 命名约定

  • 文件名: kebab-case (user-service.ts)
  • 类名: PascalCase (UserService)
  • 函数名: camelCase (getUserById)
  • 常量: UPPER_SNAKE_CASE (MAX_RETRY_COUNT)
  • 接口: IPascalCase (IUserService)

总结

使用项目模板的优势:

  • 🚀 快速启动: 几分钟内开始开发
  • 📐 最佳实践: 内置行业最佳实践
  • 🔧 预配置: 所有工具已配置好
  • 📚 文档齐全: 包含详细使用说明
  • 🎯 专注业务: 专注于业务逻辑开发

选择合适的模板,快速开始你的 API-First 项目!

SOLO Development Guide