从零开始搭建一个简易的本地Node服务端-ts基础版

前言

本次的Node环境搭建主要是搭建一个Node+Koa+ts+mysql的服务端,node版本为@15.14.0

初始化

新建一个文件夹npm进行初始化

使用npm进行初始化命令(npm初始化比较方便,也可以用yarn初始化,但需要手动添加一些配置)

npm init -y

初始化成功之后得到一个package.json文件

使用yarn安装第三方库

安装koa和koa-router

yarn add koa koa-router

注意:使用ts的情况下,安装第三方库之后需要安装ts对应的类型检测提示

安装kao和koa-router的类型检测提示

yarn add --save-dev @types/koa @types/koa-router

安装完kao和kao-router之后,会得到一个node_modules文件夹和yarn.lock文件

安装ts热更新编译

yarn add typescript ts-node nodemon -D

安装完成初始化tsconfig.json文件

tsc --init

配置tsconfig文件,指定需要编译的目录范围

json 复制代码
{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig to read more about this file */

    /* Language and Environment */
    "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */

    /* Modules */
    "module": "commonjs",                                /* Specify what module code is generated. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */

    /* Type Checking */
    "strict": true,                                      /* Enable all strict type-checking options. */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  },
  // 指定需要编译的目录范围
  "include": [
    "src/**/*",
  ],
  // 忽略类型检查的目录
  "exclude": ["node_modules"]
}

配置package.json文件

json 复制代码
  "scripts": {
    "build": "tsc",
    "start": "tsc dist/index.ts",
    "dev": "nodemon --watch src -e ts,tsx --exec ts-node src/index.ts"
  },

安装koa-body

yarn add koa-body

安装mysql

yarn add mysql
yarn add @types/mysql

服务器启动

在根目录下新建一个src文件夹,src下新建一个index.ts,这个index.ts文件就是我们的入口文件

在index文件里我们主要导入koa,设置请求配置,挂载路由以及监听端口

ts 复制代码
/*
 * @Author: chenyt
 * @Date: 2023-11-04 10:05:00
 * @Description: 
 */
import koa from 'koa';
import koaBody from 'koa-body';


const app = new koa();

// 使用中间件处理 post 传参 和上传图片
app.use(koaBody({
    multipart: true,
    formidable: {
        //   maxFileSize: config.uploadImgSize
    }
}));

// 先统一设置请求配置 => 跨域,请求头信息...
app.use(async (ctx, next) => {
    /** 请求路径 */
    // const path = ctx.request.path;

    console.log("--------------------------");
    console.count("request count");

    const { origin, referer } = ctx.headers;

    // const domain = utils.getDomain(referer || "");
    // console.log("referer domain >>", domain);
    // 如果是 允许访问的域名源 ,则给它设置跨域访问和正常的请求头配置
    // if (domain && config.origins.includes(domain)) {
        ctx.set({
            // "Access-Control-Allow-Origin": domain,
            "Access-Control-Allow-Origin": "*", // 开启跨域,一般用于调试环境,正式环境设置指定 ip 或者指定域名
            // "Content-Type": "application/json",
            // "Access-Control-Allow-Credentials": "true",
            // "Access-Control-Allow-Methods": "OPTIONS, GET, PUT, POST, DELETE",
            "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept, Authorization",
            // "X-Powered-By": "3.2.1",
            // "Content-Security-Policy": `script-src "self"` // 只允许页面`script`引入自身域名的地址
        });
    // }

    // 如果前端设置了 XHR.setRequestHeader("Content-Type", "application/json")
    // ctx.set 就必须携带 "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept, Authorization" 
    // 如果前端设置了 XHR.setRequestHeader("Authorization", "xxxx") 那对应的字段就是 Authorization
    // 并且这里要转换一下状态码
    // console.log(ctx.request.method);
    if (ctx.request.method === "OPTIONS") {
        ctx.response.status = 200;
    }

    // const hasPath = router.stack.some(item => item.path == path);
    // // 判断是否 404
    // if (path != "/" && !hasPath) {
    //     return ctx.body = "<h1 style="text-align: center; line-height: 40px; font-size: 24px; color: tomato">404:访问的页面(路径)不存在</h1>";
    // }

    try {
        console.log("成功");
        
        await next();
    } catch (err: any) {
        console.log("Error: " + err);
        ctx.response.status = err.statusCode || err.status || 500;
        ctx.response.body = {
            message: err.message
        }
    }
});
app.on("error", (err, ctx) => {
    console.log(`\x1B[91m server error !!!!!!!!!!!!! \x1B[0m`, err, ctx);
})

app.listen(3000, () => {
    // for (let i = 0; i < 100; i++) {
    //     console.log(`\x1B[${i}m 颜色 \x1B[0m`, i);
    // }
    console.log("服务器启动完成:");
})

运行yarn dev可以看到控制台提示服务器启动完成

连接msyql

首先在src文件夹下新建几个文件夹,具体目录结构如下图:

接下来我们会在utils中新建一个mysql.ts用来连接mysql

但是在正式连接之前,我们需要先设置一些mysql的配置,在modules文件夹下新建一个Config.ts文件,然后将mysql需要用到的一些配置放到该文件中

ts 复制代码
// readonly 只读
class ModuleConfig {
    constructor() {

    }

    /** 数据库配置 */
    readonly db = {
        host: 'localhost',
        user: 'root',
        password: 'root',
        database: 'node-ts',
        port: 3306,
        maxLimit: 10,
    }

    /** 接口 */
    readonly apiPrefix = ''
}

/** 项目配置 */
const config = new ModuleConfig();
export default config;

在utils中新建mysql.ts文件,mysql文件用来封装一些mysql的操作,以及对mysql的返回结果进行一些处理

ts 复制代码
import * as mysql from 'mysql';
import config from '../modules/Config';

// mysql查询结果
interface MsqlResult {
    state: number;
    results: any;
    msg: string;
    error: mysql.MysqlError | null;
    fields: Array<mysql.FieldInfo> | null;
}

// 数据库连接池
const pool = mysql.createPool({
    host: config.db.host,
    user: config.db.user,
    password: config.db.password,
    database: config.db.database,
    port: config.db.port
})

/**
 * 数据库操作
 */

export default function query(command: string, value?: Array<any>) {
    const result: MsqlResult = {
        state: 0,
        results: null,
        msg: "",
        error: null,
        fields: null
    }
    return new Promise<MsqlResult>((resolve, reject) => {
        pool.getConnection((error: any, connection) => {
            if(error) {
                result.error = error;
                result.msg = "数据库连接出错";
                resolve(result);
            } else {
                const callback: mysql.queryCallback = (error: any, result, fields) => {
                    connection.release();
                    if(error) {
                        result.error = error;
                        result.msg = "数据库操作出错";
                        resolve(result);
                    } else {
                        result.state = 1;
                        result.msg = "数据库操作成功"
                        result.results = result;
                        result.fields = fields;
                        resolve(result);
                    }
                }
                if(value) {
                    pool.query(command, value, callback);
                } else {
                    pool.query(command, callback);
                }
            }
        })
    })
}

接口开发

node的接口开发流程主要是,model文件夹建立数据模型,controllers文件夹进行业务处理,routes文件夹进行路由处理,最终在index文件中进行挂载。

下面我们用获取tags列表来进行举例: model文件夹的Tags.ts文件

ts 复制代码
/*
 * @Author: chenyt
 * @Date: 2023-11-04 11:47:08
 * @Description: 
 */
import query from '../utils/msyql';

async function all() {
    const res = await query(`SELECT * FROM tags`)
    return res.results;
}
module.exports = { all }

contorllers的tagsController.ts文件

ts 复制代码
const tag = require('../models/Tags.ts');

async function getTagsList(ctx: { body: { code: number; msg: string; data: any; }; }) {
    const tags = await tag.all();
    ctx.body = {
        code: 200,
        msg: '获取成功',
        data: tags
    }
}
module.exports = { getTagsList }

routes文件夹的tags.ts文件

ts 复制代码
import router from "./mian";
const tagController = require('../controllers/tagsController');

router.get('/tags', tagController.getTagsList);

在routes文件夹下建立一个main.ts文件,用于路由的处理

ts 复制代码
import Router = require("koa-router");
import config from "../modules/Config";

const router = new Router({
    prefix: config.apiPrefix
});

export default router;

在index文件下进行路由挂载

ts 复制代码
import koa from 'koa';
import koaBody from 'koa-body';
// import config from './modules/Config';
import router from './routes/mian';
// import tok
// import utils from './utils';
import "./routes/tags"


const app = new koa();

// 使用中间件处理 post 传参 和上传图片
app.use(koaBody({
    multipart: true,
    formidable: {
        //   maxFileSize: config.uploadImgSize
    }
}));

// 先统一设置请求配置 => 跨域,请求头信息...
app.use(async (ctx, next) => {
    /** 请求路径 */
    // const path = ctx.request.path;

    console.log("--------------------------");
    console.count("request count");

    const { origin, referer } = ctx.headers;

    // const domain = utils.getDomain(referer || "");
    // console.log("referer domain >>", domain);
    // 如果是 允许访问的域名源 ,则给它设置跨域访问和正常的请求头配置
    // if (domain && config.origins.includes(domain)) {
        ctx.set({
            // "Access-Control-Allow-Origin": domain,
            "Access-Control-Allow-Origin": "*", // 开启跨域,一般用于调试环境,正式环境设置指定 ip 或者指定域名
            // "Content-Type": "application/json",
            // "Access-Control-Allow-Credentials": "true",
            // "Access-Control-Allow-Methods": "OPTIONS, GET, PUT, POST, DELETE",
            "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept, Authorization",
            // "X-Powered-By": "3.2.1",
            // "Content-Security-Policy": `script-src "self"` // 只允许页面`script`引入自身域名的地址
        });
    // }

    // 如果前端设置了 XHR.setRequestHeader("Content-Type", "application/json")
    // ctx.set 就必须携带 "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept, Authorization" 
    // 如果前端设置了 XHR.setRequestHeader("Authorization", "xxxx") 那对应的字段就是 Authorization
    // 并且这里要转换一下状态码
    // console.log(ctx.request.method);
    if (ctx.request.method === "OPTIONS") {
        ctx.response.status = 200;
    }

    // const hasPath = router.stack.some(item => item.path == path);
    // // 判断是否 404
    // if (path != "/" && !hasPath) {
    //     return ctx.body = "<h1 style="text-align: center; line-height: 40px; font-size: 24px; color: tomato">404:访问的页面(路径)不存在</h1>";
    // }

    try {
        console.log("成功");
        
        await next();
    } catch (err: any) {
        console.log("Error: " + err);
        ctx.response.status = err.statusCode || err.status || 500;
        ctx.response.body = {
            message: err.message
        }
    }
});
// 路由挂载
app.use(router.routes())
app.on("error", (err, ctx) => {
    console.log(`\x1B[91m server error !!!!!!!!!!!!! \x1B[0m`, err, ctx);
})

app.listen(3000, () => {
    // for (let i = 0; i < 100; i++) {
    //     console.log(`\x1B[${i}m 颜色 \x1B[0m`, i);
    // }
    console.log("服务器启动完成:");
})

import "./routes/tags" 这个导入一定要有,需要在主页中导入对应的路由 访问:http://localhost:3000/tags 就可以直接访问到接口获取到对应数据。

参考

全栈之路:node+ts+koa 开发环境搭建 - 掘金 (juejin.cn)

node + koa + ts 构建服务端应用 - 掘金 (juejin.cn)

相关推荐
烛阴7 小时前
告别 any!用联合类型打造更灵活、更安全的 TS 代码
前端·typescript
Hello.Reader7 小时前
Elasticsearch Node.js 客户端连接指南(Connecting)
elasticsearch·node.js·jenkins
醉方休8 小时前
Node.js 精选:50 款文件处理与开发环境工具库
linux·运维·node.js
Hello.Reader12 小时前
Elasticsearch Node.js 客户端的安装
elasticsearch·node.js·vim
Juchecar13 小时前
跨端桌面框架 Tauri 架构原理 的通俗解读
javascript·node.js
FanetheDivine14 小时前
在ts中定义全局和模块的变量和类型
typescript
断竿散人14 小时前
Node 版本管理工具全指南
前端·node.js
哈撒Ki14 小时前
快速入门zod4
前端·node.js
tianchang15 小时前
打造你的本地AI助手:基于RAG+向量数据库的智能问答系统
人工智能·设计模式·node.js
月舞之剑16 小时前
linux离线安装nodejs
linux·node.js