nestjs实战 - buildadmin重构后端(初始化mock接口)

前言

实战项目比写demo更有意义、更有价值,更能考虑实际情况的应用,所以选用了 buildadmin 这个后台管理系统

采用 buildadmin/web 前端项目不改,直接把 php 后端重构成 nestjs 后端

初始化 nestjs

创建服务

bash 复制代码
nest new basys

接口数据格式

在开发 api 之前,我们需要确定它的 api 接口数据格式

json 复制代码
{
    "code": 1,
    "msg": "",
    "time": 1753953842,
    "data": {
        "captcha": true
    }
}
创建 nestjs 拦截器(src/core/interceptors/response.interceptor.ts)

创建 nestjs 拦截器,格式化接口输出格式

ts 复制代码
// src/core/interceptors/response.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class ResponseInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const response = context.switchToHttp().getResponse();
    // buildadmin headers
    response.setHeader('access-control-allow-headers', 'think-lang, server, ba_user_token, ba-user-token, ba_token, ba-token, batoken, Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With');
    response.setHeader('access-control-allow-methods', 'GET, POST, PUT, DELETE, OPTIONS');
    return next.handle().pipe(
      map(data => {
        return {
          code: data?.code ?? 1,
          msg: data?.msg ?? '',
          time: Math.floor(Date.now() / 1000),
          data: data?.data ?? data ?? null
        };
      })
    );
  }
}
加载拦截器(src/main.ts)

加载拦截器,顺便设置跨域问题,本地开发会出现端口跨域问题

typescript 复制代码
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ResponseInterceptor } from './core/interceptors/response.interceptor';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 跨域
  app.enableCors({
    origin: 'http://localhost:1818',
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
    credentials: true,
  });
  // 拦截器
  app.useGlobalInterceptors(new ResponseInterceptor());
  await app.listen(process.env.PORT ?? 8000);
}
bootstrap();

创建 restful api

为了保持 buildadmin 接口格式不变,分析出从登录到进入首页所需接口:

  • [GET] /admin/index/login 登录页信息
  • [GET] /api/common/clickCaptcha 点击验证码
  • [POST] /api/common/checkClickCaptcha 校验验证码
  • [POST] /admin/index/login 提交登录信息(账号密码)
  • [GET] /admin/index/index 首页信息
  • [GET] /admin/dashboard/index 控制台信息

我们先实现这 6 个接口的 mock 数据,基于 MVCNestjs 设计理念,可以这样设计目录结构: /[module]/[submodule]/[controller].[action]

md 复制代码
- src
    - admin
        - admin.module.ts
        - index
            - index.module.ts
            - index.controller.ts # /admin/index/index
            - login.controller.ts # /admin/index/login
        - dashboard
            - dashboard.module.ts
            - index.controller.ts # /admin/dashboard/login
    - api
        - api.module.ts
        - common
            - common.module.ts
            - click-captcha.controller.ts # /api/common/clickCaptcha|checkClickCaptcha

以上先忽略了 xxx.service.ts 后面打通数据库在添加

完善 module

两个主模块导入各自的子模块

src/admin/admin.module.ts
typescript 复制代码
// src/admin/admin.module.ts
import { Module } from '@nestjs/common';
import { IndexModule } from './index/index.module';
import { DashboardModule } from './dashboard/dashboard.module';

@Module({
    imports: [IndexModule, DashboardModule],
})
export class AdminModule {}
src/api/api.module.ts
typescript 复制代码
// src/api/api.module.ts
import { Module } from '@nestjs/common';
import { CommonModule } from './common/common.module';

@Module({
    imports: [CommonModule],
})
export class ApiModule {}
  • 各个子模块
src/admin/index/index.module.ts
typescript 复制代码
// src/admin/index/index.module.ts
import { Module } from '@nestjs/common';
import { IndexController } from './index.controller';
import { LoginController } from './login.controller';

@Module({
  controllers: [IndexController, LoginController]
})
export class IndexModule {}
src/admin/dashboard/dashboard.module.ts
typescript 复制代码
// src/admin/dashboard/dashboard.module.ts
import { Module } from '@nestjs/common';
import { IndexController } from './index.controller';

@Module({
  controllers: [IndexController]
})
export class DashboardModule {}
src/api/common/common.module.ts
typescript 复制代码
// src/api/common/common.module.ts
import { Module } from '@nestjs/common';
import { ClickCaptchaController } from './click-captcha.controller';

@Module({
    controllers: [ClickCaptchaController],
})
export class CommonModule {}

完善 controller

src/admin/index/login.controller.ts
typescript 复制代码
import { Controller, Get, Post } from '@nestjs/common';

@Controller('admin/index')
export class LoginController {
    /**
     * [GET] /admin/index/login
     */
    @Get('login')
    getLogin() {
        return {
            captcha: true
        }
    }

    /**
     * [POST] /admin/index/login
     */
    @Post('login')
    postLogin() {
        return {
            "userInfo": {
                "id": 1,
                "username": "admin",
                "nickname": "Admin",
                "avatar": "\/static\/images\/avatar.png",
                "last_login_time": "2025-07-31 16:50:23",
                "token": "128adae3-665e-4424-94d8-124089ddd460",
                "refresh_token": ""
            }
        };
    }
}
src/api/common/click-captcha.controller.ts
typescript 复制代码
import { Controller, Get, Post, Query } from '@nestjs/common';

@Controller('/api/common')
export class ClickCaptchaController {
    /**
     * [GET] /api/common/clickCaptcha
     */
    @Get('clickCaptcha')
    getCaptcha(@Query('id') id: string) {
        return {
            id,
            "text": [
                "检",
                "步"
            ],
            "base64": "",
            "width": 350,
            "height": 200
        };
    }

    /**
     * [POST] /api/common/checkClickCaptcha
     */
    @Post('checkClickCaptcha')
    postCheckCaptcha() {
        // 假装校验通过
        return null;
    }
}
src/admin/dashboard/index.controller.ts
typescript 复制代码
import { Controller, Get } from '@nestjs/common';

@Controller('admin/dashboard')
export class IndexController {
    /**
     * [GET] /admin/dashboard/index
     */
    @Get('index')
    index() {
        return {
            "remark": "开源等于互助;开源需要大家一起来支持,支持的方式有很多种,比如使用、推荐、写教程、保护生态、贡献代码、回答问题、分享经验、打赏赞助等;欢迎您加入我们!"
        };
    }
}
src/admin/index/index.controller.ts
typescript 复制代码
import { Controller, Get } from '@nestjs/common';

@Controller('admin/index')
export class IndexController {
    /**
     * [GET] /admin/index/index
     */
    @Get('index')
    index() {
        return {
            "adminInfo": {
                "id": 1,
                "username": "admin",
                "nickname": "Admin",
                "avatar": "\/static\/images\/avatar.png",
                "last_login_time": "2025-07-31 16:50:24",
                "super": true
            },
            "menus": [
                {
                    "id": 1,
                    "pid": 0,
                    "type": "menu",
                    "title": "控制台",
                    "name": "dashboard",
                    "path": "dashboard",
                    "icon": "fa fa-dashboard",
                    "menu_type": "tab",
                    "url": "",
                    "component": "\/src\/views\/backend\/dashboard.vue",
                    "keepalive": "dashboard",
                    "extend": "none",
                    "children": [
                        {
                            "id": 94,
                            "pid": 1,
                            "type": "button",
                            "title": "查看",
                            "name": "dashboard\/index",
                            "path": "",
                            "icon": "",
                            "menu_type": null,
                            "url": "",
                            "component": "",
                            "keepalive": 0,
                            "extend": "none"
                        }
                    ]
                },
            ],
            "siteConfig": {
                "siteName": "BuildAdmin",
                "version": "v1.0.0",
                "cdnUrl": "\/\/demo.buildadmin.com",
                "apiUrl": "https:\/\/buildadmin.com",
                "upload": {
                    "maxSize": 10485760,
                    "saveName": "\/storage\/{topic}\/{year}{mon}{day}\/{fileName}{fileSha1}{.suffix}",
                    "allowedSuffixes": "jpg,png,bmp,jpeg,gif,webp,zip,rar,wav,mp4,mp3",
                    "allowedMimeTypes": [],
                    "mode": "local"
                }
            },
            "terminal": {
                "phpDevelopmentServer": false,
                "npmPackageManager": "pnpm"
            }
        };
    }
}

到这里基本完成 nestjs 的接口设计,直接 npm run start 就能跑起来服务

之后直接运行前端项目,就能看到界面了

代码放这里了 buildadmin-nestjs

相关推荐
天天摸鱼的java工程师27 分钟前
QPS 10 万,任务接口耗时 100ms,线程池如何优化?
java·后端·面试
双向3327 分钟前
从O(n²)到O(n log n):深度剖析快速排序的内存优化与cache-friendly实现
后端
回家路上绕了弯30 分钟前
深度解析:频繁 Full GC 的诊断与根治方案
jvm·后端
武子康31 分钟前
大数据-57 Kafka 高级特性 Producer 消息发送流程与核心配置详解
大数据·后端·kafka
知其然亦知其所以然33 分钟前
MySQL社招面试题:索引有哪几种类型?我讲给你听的不只是答案!
后端·mysql·面试
天天摸鱼的java工程师36 分钟前
掘金图片上传被拒:一次由CheckAuthenticationError引发的密钥‘失踪’迷案
java·后端
福大大架构师每日一题37 分钟前
2025-08-01:粉刷房子Ⅳ。用go语言,给定一个偶数个房屋排列在一条直线上,和一个大小为 n x 3 的二维数组 cost,其中 cost[i][j] 表
后端
error_cn37 分钟前
网络i_o对cpu负载分析
后端
bug菌38 分钟前
学生信息管理系统,真的是码农的必修课吗?
java·后端·java ee
就是帅我不改40 分钟前
深入实战建造者模式:在订单系统中的应用
后端·架构