行业资讯
📅 2026/7/10 22:42:20
Express + TypeScript + ESM 后端服务搭建教程
Express TypeScript ESM 后端服务搭建教程基于 tdt-tile-server 项目实战, 从零搭建企业级 Express 后端服务。目录一、项目初始化二、目录架构与分层设计三、核心配置详解四、开发工作流五、进阶主题一、项目初始化1.1 环境要求Node.js 18.x (推荐 20.x LTS)npm 9.x1.2 创建项目mkdirtdt-tile-servercdtdt-tile-servernpminit-y1.3 package.json 配置关键配置type: module启用 ES Module 支持{name:tdt-tile-server,version:1.0.1,type:module,scripts:{build:tsc,start:node dist/server.js,dev:cross-env NODE_ENVdevelopment nodemon -q -L -c nodemon.json}}type: module使 Node.js 将.js文件解析为 ES Module支持import/export语法。1.4 安装核心依赖# 生产依赖npminstallexpress axios cors dotenv helmet morgan winston zod# 开发依赖npminstall-Dtypescript tsx nodemon cross-envnpminstall-Dtypes/express types/node types/cors types/morgan依赖说明包名用途expressWeb 框架 (v5)typescript类型系统 编译器tsx直接执行 .ts 文件 (开发用)nodemon文件变更自动重启cross-env跨平台环境变量设置winston结构化日志helmet安全 HTTP 头cors跨域资源共享dotenv环境变量加载zod运行时数据校验1.5 TypeScript 配置tsconfig.json{compilerOptions:{target:ES2022,module:ESNext,moduleResolution:bundler,allowSyntheticDefaultImports:true,esModuleInterop:true,forceConsistentCasingInFileNames:true,strict:false,// 如果改为true,语法检查比较严格noUnusedLocals:true,noUnusedParameters:true,skipLibCheck:true,isolatedModules:true,outDir:./dist,rootDir:./src,sourceMap:true,declaration:true,resolveJsonModule:true},include:[src/**/*],exclude:[node_modules,dist]}关键配置解读选项说明module: ESNext输出 ES Module 格式, 与type: module配套moduleResolution: bundler解析策略: 允许省略.js后缀, 兼容 package.json exportsisolatedModules: true每个文件独立编译, 强制import type语法outDir: ./dist编译输出目录rootDir: ./src源码根目录resolveJsonModule: true允许importJSON 文件注意: ESM 模式下, 导入本地模块必须写.js后缀:import logger from ./utils/logger.js。1.6 多环境变量策略项目使用三层环境变量文件, 后加载的覆盖前加载的.env# 基础默认值.env.dev# 开发环境 (NODE_ENVdevelopment).env.prod# 生产环境 (NODE_ENVproduction).env(默认值):NODE_ENVdevelopment PORT8080 PROJECT_NAMEtdt-tile-server.env.dev(开发覆盖):NODE_ENVdevelopment PORT8080 ALLOWED_ORIGINShttp://localhost:8080,http://localhost:5500.env.prod(生产覆盖):NODE_ENVproduction PORT18000在app.ts中加载importdotenvfromdotenv;importpathfrompath;dotenv.config({path:path.resolve(process.cwd(),.env)});constnodeEnvprocess.env.NODE_ENV||development;constenvFilenodeEnvproduction?.env.prod:.env.dev;dotenv.config({path:path.resolve(process.cwd(),envFile),override:true});二、目录架构与分层设计src/ ├── server.ts # 入口: 端口监听 优雅关闭 ├── app.ts # Express 实例: 中间件 路由挂载 ├── config/ │ ├── env.ts # 环境变量加载逻辑 │ └── tileConfig.ts # 业务配置 (瓦片服务 URL、token、缓存) ├── controller/ │ └── tdtController.ts # 请求/响应处理, 参数校验 ├── service/ │ └── tdtService.ts # 业务逻辑: 瓦片下载/缓存/转换 ├── routes/ │ ├── index.ts # 路由聚合 (path - router 映射) │ └── tdt.ts # /tdtProxy 路由定义 ├── middleware/ │ ├── cors.ts # CORS 多环境策略 │ └── errorHandler.ts # 全局错误处理 ├── utils/ │ ├── logger.ts # Winston 日志 (控制台 文件分级) │ ├── response.ts # 统一 API 响应格式 │ ├── envParser.ts # 环境变量解析工具 │ └── unit.ts # 通用工具函数 └── lib/ └── vector_tile_tdt.ts # TDT 瓦片下载库分层原则层职责禁止routes定义 URL 路径、HTTP 方法不含业务逻辑controller参数提取、调用 service、格式化响应不含业务算法service业务逻辑编排、数据获取不操作 req/resmiddleware请求预处理/拦截不含业务逻辑lib纯算法/数据转换不依赖 Expressutils横切工具 (日志、响应格式)不含业务2.1 入口文件: server.tsimportappfrom./app.js;importloggerfrom./utils/logger.js;constPORTprocess.env.PORT||8080;asyncfunctionstartServe(){constserverapp.listen(PORT,(){console.log(Server running at http://localhost:${PORT});});// 优雅关闭constshutdownasync(signal:string){logger.info(收到${signal}信号, 开始关闭...);server.close(()logger.info(HTTP 服务器已关闭));};process.on(SIGTERM,()shutdown(SIGTERM));process.on(SIGINT,()shutdown(SIGINT));}startServe();2.2 Express 实例: app.tsconstapp:Applicationexpress();// 中间件注册顺序很重要app.use(helmet({/* CSP 配置 */}),);app.use(corsMiddleware);// CORSapp.use(express.json({limit:10mb}));app.use(morganMiddleware);// HTTP 请求日志// 路由挂载routes.map((route){app.use(/apiroute.path,route.router);});// 404 处理 (所有路由之后)app.use((req,res)ResponseUtil.notFound(res));// 500 处理 (最后)app.use(errorHandler);中间件顺序规则: 安全 → CORS → 解析 → 日志 → 路由 → 404 → 500。2.3 路由聚合: routes/index.tsimporttdtRoutesfrom./tdt.js;constroutes[{path:/tdtProxy,router:tdtRoutes}];exportdefaultroutes;2.4 具体路由: routes/tdt.tsconstrouterRouter();router.get(/getTile/:z/:x/:y,authenticate,TDTController.getTile);exportdefaultrouter;最终生成路径:/api/tdtProxy/getTile/:z/:x/:y。2.5 Controller 层exportclassTDTController{asyncgetTile(req:Request,res:Response){const{x,y,z}req.params;consttypereq.query.type||png;constresultawaittdtService.getTile(Number(x),Number(y),Number(z),typeasstring,);res.set(Content-Type,image/png);res.send(result);}}2.6 Service 层classTDTService{asyncgetTile(x:number,y:number,z:number,type:string){// 1. 检查缓存constcachePathgetCachePath(x,y,z);// 2. 缓存未命中 → 下载consttdtBufferawaitdownloadTile(x,y,z);// 3. 写入缓存writeFileSync(cachePath,tdtBuffer);// 4. 返回瓦片returnthis.parseTile(tdtBuffer);}}三、核心配置详解3.1 nodemon.json — 开发热重载{watch:[src],ext:ts,exec:tsx src/server.ts}字段说明watch监控src/目录变更ext只监控.ts文件exec用tsx直接执行 TypeScript, 无需预编译联合npm run dev使用cross-envNODE_ENVdevelopment nodemon-q-L-cnodemon.json为什么用 tsx 而不是 ts-node?tsx 基于 esbuild, 启动速度极快, 对 ESM 支持更好。3.2 tsconfig.json — ESM 关键配置ESM 的核心约束: 导入本地模块必须显式写.js后缀。// ✅ 正确: 带 .js 后缀importloggerfrom./utils/logger.js;importTDTControllerfrom../controller/tdtController.js;// ❌ 错误: 省略后缀 (bundler 模式下可省略, 但 tsc 编译期会报错)importloggerfrom./utils/logger;moduleResolution: bundler: 比node16更宽松, 允许省略.js后缀的导入 (配合allowImportingTsExtensions)package.json的exports字段解析不要求package.json有type: module就能解析 ESM3.3 环境变量加载src/config/env.tsimportdotenvfromdotenv;dotenv.config();配合app.ts中的多层加载dotenv.config({path:path.resolve(process.cwd(),.env)});constenvFileprocess.env.NODE_ENVproduction?.env.prod:.env.dev;dotenv.config({path:path.resolve(process.cwd(),envFile),override:true});override: true确保环境专属配置覆盖基础默认值。3.4 日志系统src/utils/logger.ts使用 Winston 实现多 Transportconstloggerwinston.createLogger({level:process.env.NODE_ENVdevelopment?debug:info,transports:[newwinston.transports.File({filename:logs/error.log,level:error}),newwinston.transports.File({filename:logs/all.log}),],});// 开发环境额外输出到控制台if(process.env.NODE_ENVdevelopment){logger.add(newwinston.transports.Console({format:colorize()}));}日志级别 (数字越小优先级越高):级别用途error (0)异常、失败warn (1)警告info (2)一般信息http (3)HTTP 请求debug (4)调试详情Morgan 集成到 Winston:importmorganfrommorgan;constmorganMiddlewaremorgan(:method :url :status - :response-time ms,{stream:{write:(message:string)logger.http(message.trim())},});app.use(morganMiddleware);四、开发工作流4.1 开发模式npmrun dev流程:cross-env设置环境变量 →nodemon监控src/→tsx直接执行src/server.ts。修改任何.ts文件后自动重启, 无需手动编译。4.2 生产构建npmrun buildtsc将src/编译到dist/, 输出标准 JavaScript source maps 类型声明。4.3 生产启动npmstart# 等价于: node dist/server.js4.4 静态资源托管const__filenamefileURLToPath(import.meta.url);const__dirnamepath.dirname(__filename);// 托管 public 目录app.use(/public,express.static(path.join(__dirname,../public)));// 托管 test 目录 (仅开发)app.use(/test,express.static(path.join(__dirname,../test)));访问http://localhost:8080/test/map.html即可查看测试页面。ESM 替代 __dirname: ESM 模块没有__dirname, 需通过fileURLToPath(import.meta.url)获取当前文件路径, 再用path.dirname()取目录。4.5 npm scripts 全览命令用途npm run dev开发 (热重载)npm run build编译 TS → JSnpm start生产启动五、进阶主题5.1 统一响应格式exportclassResponseUtil{staticsuccessT(res:Response,data?:T,message操作成功,code200){returnres.status(200).json({code,message,data,timestamp:Date.now(),success:true,});}staticerror(res:Response,message操作失败,code500,httpStatus400,){returnres.status(httpStatus).json({code,message,timestamp:Date.now(),success:false,});}staticnotFound(res:Response,message资源不存在){returnthis.error(res,message,404,null,404);}staticsuccessWithPaginationT(res,list,total,page,pageSize){returnthis.success(res,{list,total,page,pageSize});}}5.2 安全中间件 (Helmet)app.use(helmet({contentSecurityPolicy:{directives:{defaultSrc:[self],styleSrc:[self,unsafe-inline],scriptSrc:[self,unsafe-inline,unsafe-eval],imgSrc:[self,data:,https:],},},}),);5.3 CORS 多环境策略if(process.env.NODE_ENVdevelopment){// 开发: 宽松模式, 允许所有来源 (方便调试)app.use(devCorsMiddleware);}else{// 生产: 严格模式, 仅允许白名单来源app.use(corsMiddleware);}5.4 优雅关闭constshutdownasync(signal:string){server.close(()logger.info(HTTP 已关闭));// 30 秒超时强制退出setTimeout((){logger.error(超时强制退出);process.exit(1);},30000);};process.on(SIGTERM,()shutdown(SIGTERM));process.on(SIGINT,()shutdown(SIGINT));附录: 项目依赖一览生产依赖包版本用途express^5.0.0HTTP 框架axios^1.10.0HTTP 客户端cors^2.8.5CORS 中间件dotenv^16.5.0环境变量helmet^7.1.0安全头morgan^1.10.0HTTP 日志winston^3.17.0日志框架zod^3.25.67数据校验开发依赖包版本用途typescript^5.3.0类型系统tsx^4.22.2TS 运行时nodemon^3.0.2热重载cross-env^7.0.3跨平台 envtypes/express^4.17.17Express 类型types/node^20.0.0Node 类型