express+ts从零搭建node应用

661 阅读2分钟

为什么选择 express

刚学习node,express比较轻量,几乎没有内置任何的额外功能,灵活度较高

为什么选择 TS

主要是参数约束接口,规定调用方法的传参类型,避免出现数据类型不一致导致JS直接卡死在浏览器的语法错误

配置TS

npm install typescript -g

配置 tsconfig.config

    {
    "compilerOptions": {
        "module": "commonjs",
        "noImplicitAny": true,
        "declaration": true,
        "removeComments": true,
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "allowSyntheticDefaultImports": true,
        "esModuleInterop": true,
        "target": "es2017",
        "preserveConstEnums": true,
        "sourceMap": true,
        "outDir": "./dist",
        "baseUrl": "./",
        "incremental": true
    }, 
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "node_modules",
        "**/*.spec.ts",
        "dist"
    ]
}

安装 tsc-watch 监听ts文件,自动编译

  npm install tsc-watch -D
  

package.json文件

{
  "name": "user-link-tracking",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "tsc-watch --onSuccess \"node dist/main.js\"",
    "format": "prettier --write \"src/**/*.ts\""
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.24.0",
    "express": "^4.17.1",
    "mysql2": "^2.3.3"
  },
  "devDependencies": {
    "@types/express": "^4.17.13",
    "@types/mysql2": "github:types/mysql2",
    "@types/node": "^16.11.7",
    "dotenv": "^10.0.0",
    "nodemon": "^2.0.15",
    "prettier": "^2.4.1",
    "tsc-watch": "^4.5.0",
    "typescript": "^4.4.4"
  }
}

搭建项目并启动服务

image.png

const express = require("express")
const app = express()
const port = 3000
app.listen(3000, () => {
    console.log('服务启动')
})

数据库连接

mysql.ts

import mysql from 'mysql2'
import {
    MYSQL_HOST,
    MYSQL_PORT,
    MYSQL_USER,
    MYSQL_PASSWORD,
    MYSQL_DATABASE
} from '../../app/app.config'

export const connection = mysql.createConnection({
    host: MYSQL_HOST,
    port: parseInt(MYSQL_PORT),
    user: MYSQL_USER,
    password: MYSQL_PASSWORD,
    database: MYSQL_DATABASE
})

业务模型分层

model层:定义数据格式

export class PostModel{
    id?:number;
    title?:string;
    content?:string;
}

service层:处理数据逻辑

import { connection } from '../app/database/mysql'
import { PostModel } from './post.model'

export const getPosts = async () => {
    const statement = `SELECT post.id,post.title,post.content, JSON_OBJECT(
                                'id',user.id,
                                'name',user.name
                            ) as woker
                        FROM post
                        LEFT JOIN user ON post.userId=user.id`
    const [data] = await connection.promise().query(statement)
    console.log(data)
    return data
}

export const createPost = async (post: PostModel) => {
    const statement = `
        INSERT INTO post 
        set ?
    `
    const [data] = await connection.promise().query(statement,post)
    return data
}

 

controller层:处理逻辑,加工数据

import { Request, Response, NextFunction, request } from "express";
import { getPosts, createPost } from './post.service'


export const index = async (request: Request, response: Response, next: NextFunction) => {
    try {
        const posts = await getPosts()
        response.send(posts)
    } catch (error) {
        next(error)
    }
}


export const store = async (request: Request, response: Response, next: NextFunction) => {
    const { content, title } = request.body
    try {
        const data = await createPost({ content, title })
        response.status(200).send(data)
    } catch (error) {
        next(error)
    }
}

router层: 接收请求路由

import express from 'express';
import * as postController from './post.controller'
import { requestUrl } from '../app/app.middleware'
const router = express.Router()

/**
 * 内容接口
 */
router.get('/posts', requestUrl, postController.index)

router.post('/posts', postController.store)

export default router

中间件

参数解析中间件

app/index.ts

import express from 'express';
 /**
 * 处理json
 */
app.use(express.json());

请求异常处理中间件

app.middleware.ts

 import { Request, Response, NextFunction, request } from 'express'
     /**
     * 异常处理器
     */
    export const defaultErrorHandle = (error: any, request: Request, response: Response, next: NextFunction) => {
        if (error) {
            console.log(error.message)
        }
        let statusCode: number, message: string;
        switch (error.message) {
            default:
                statusCode = 500;
                message = '出错了';
                break;
        }
        response.status(statusCode).send({ message })

    }

app/index.ts

  import { defaultErrorHandle } from './app.middleware'
  app.use(defaultErrorHandle)
  

路由处理

app/index.ts

import postRouter from '../post/post.router';
import skyRouter from '../skyLog/log.router';
app.use(postRouter)
app.use(skyRouter)

总结

其他更多的公共逻辑都可以放在中间件层面去做,例如登录校验、权限校验等。到目前为止上面细节上还有很多问题没来得及做处理,比如接口转发,文件处理,后期还会对很多细节进行优化。