likes
comments
collection
share

Nest项目(三)-连接 MySQL 数据库并渲染页面

作者站长头像
站长
· 阅读数 69

前言

到目前位置服务已经满足了如下功能:

  • 启动一个服务,端口为 3000
  • 可以处理普通 get 请求,并返回 接口数据
  • 可以访问静态 js | css | html | img 资源
  • 可以通过接口访问经过 ejs 编译后的 html

在第一篇中已经说明,这个示例项目首先要做到服务端渲染一个网站的效果。而到目前为止已经实现页面的渲染,但是还是不够完美,有些少量的数据是可以放在 接口返回体里面的,但是更多的数据是存在其他地方的:json、excel、数据库。

既然看到了 json、excel 都是通过解析文件的方式实现的,就不进行着重记录了。

本篇记录一下如何连接 MySQL 数据库,并获取数据,然后给 ejs 进行渲染。

配置流程

一、安装依赖

$ pnpm install --save @nestjs/typeorm typeorm mysql2
  • mysql2: 用于node.js来操作mysql的库;
  • typeorm:一个orm库,可以理解为把对象和表做了一个映射关系,来方便操作;
  • @nestjs/typeorm:nestjs基于typeorm的封装。

通过上面的安装,我们就在nestjs集成了mysql库并使用typeorm来操作。

当然前提条件是有个数据库供我们访问,如果没有的话可以安装个 phpstudy,虽然是叫 phpstudy,但是我们这里只用到它的 MySQL 相关的功能,对于新手来说比较友好。如果有现成的远程仓库也可以连接远程仓库。

二、项目配置

1. 直接使用 mysql2 链接数据库

这个方案其实是因为我没找到如何在不声明 entity 的场景下去直接查询数据库,主要是用在比较简单且需要迅速实现一个简单的逻辑的场景下。

还有一个场景就是,在现有的数据库,不太方便去声明 entity 时候,要操作数据库。因为之前使用过 mysql2 就把这个方法放在了这里。建议还是使用官方推荐的方法。

具体实现方式:

因为 mysql2 默认的方式是回调方式调用,想在现在的接口中返回查询结果就要封装成一个 promise 函数,同时也能提高代码的可复用性。

然后在接口内部调用,为了演示方便我放在了一个文件里面,正常情况下,应该是单独提到个公共的地方。

首先要先增加一个接口 getUsers,这个在前面已经增加 ejs 的时候已经展示了。

这里就只放 具体 service 部分的逻辑。

// database.ts
export const CONF_MYAQL2: ConnectionOptions = {
  host: 'localhost',
  user: 'root',
  password: 'root',
  database: 'nest-test',
  timezone: 'Z',
  charset: 'utf8',
};

// app.service.ts
import { Injectable } from '@nestjs/common';
import { CONF_MYAQL2 } from './config/database'; // mysql2 连接信息
import mysql from 'mysql2';

// 封装 查询 Promise 方法
const queryPromise = (query) =>
  new Promise<any>((resolve, reject) => {
    const connection = mysql.createConnection(CONF_MYAQL2);
    connection.connect();
    connection.query(query, (err, results) => {
      if (err) {
        reject(err);
        throw err;
      }
      return resolve(results);
    });
    connection.end();
  });

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }

  // 新增 getIsers 方法,被 /users 调用
  async getUsers(): Promise<any> {
    // 执行 sql 语句
    const result = await queryPromise('select * from user1;');

    // 返回执行结果
    return result;
  }
}

数据如下: Nest项目(三)-连接 MySQL 数据库并渲染页面

然后浏览器访问 http://localhost:3000/users ,即可看到查询结果。

Nest项目(三)-连接 MySQL 数据库并渲染页面

2. 使用官方推荐的方式连接 MySQL 数据库

连接数据库很简单,做如下配置就好:

// app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    // 使用 typeorm 链接数据库
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      timezone: 'Z', // MySQL 服务器上配置的时区。这用于将服务器日期/时间值强制转换为 JavaScript Date 对象,反之亦然。该值可以是`local`,`Z`或`+HHMM`或`-HHMM`形式的偏移。 (默认:`local`)
      database: 'nest',
      entities: ['dist/modules/**/*.entity{.ts,.js}'],
      synchronize: true,
      dateStrings: true,
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

2.1 创建User模块相关文件

nest 内置了脚手架,可以通过nest相关命令创建一个新的模块。为了使项目更整洁,在 src 下创建 modules 用来存放所有的模块。

$ nest g mo modules/user && nest g co modules/user && nest g service modules/user

---
CREATE src/modules/user/user.module.ts (81 bytes)
UPDATE src/app.module.ts (499 bytes)
CREATE src/modules/user/user.controller.spec.ts (478 bytes)
CREATE src/modules/user/user.controller.ts (97 bytes)
UPDATE src/modules/user/user.module.ts (166 bytes)
CREATE src/modules/user/user.service.spec.ts (446 bytes)
CREATE src/modules/user/user.service.ts (88 bytes)
UPDATE src/modules/user/user.module.ts (240 bytes)

如上所示,就在项目中创建了一个新的模块 user,并且和 app 一样,它也是拥有三个核心文件 module/controller/service,并且 nest 脚手架已经同步更新了 user.module.ts ,将 controller 和 service 加载了进去。

2.2 声明并使用 User Entity

在user模块中创建 user.entity.ts:

import {
  Entity,
  PrimaryGeneratedColumn,
  Column,
  VersionColumn,
  CreateDateColumn,
  UpdateDateColumn,
} from 'typeorm';

export enum Sex {
  '女' = 0,
  '男' = 1,
}

export abstract class Common {
  // 主键id
  @PrimaryGeneratedColumn()
  id: number;

  // 创建时间
  @CreateDateColumn({ type: 'timestamp' })
  createTime: Date;

  // 更新时间
  @UpdateDateColumn({ type: 'timestamp' })
  updateTime: Date;

  // 软删除
  @Column({
    default: false,
    select: false,
  })
  isDelete: boolean;

  // 更新次数
  @VersionColumn({
    select: false,
    default: 1,
  })
  version: number;
}

@Entity()
export class User extends Common {
  // 姓名
  @Column({ length: 16 })
  username: string;

  // 性别
  @Column()
  sex: Sex;
}


如上所示,为了便于日后复用,我特意将几个通用的字段放在了 Common 中,而 User 私有的字段则继承自 Common。

而添加这个 entity 之后,查看数据库则会发现,已经创建好了一张 名为 user 的表,且字段和声明的一致。

Nest项目(三)-连接 MySQL 数据库并渲染页面

注意,使用 typeorm 连接数据库根据 entity 重新初始化数据库,如果连接已经有数据的数据库,千万要先备份数据再操作。

2.3 操作数据库 CURD

操作数据库需要先在 module 中安装数据模型。修改如下:

// user.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { User } from './user.entity';

@Module({
  imports: [TypeOrmModule.forFeature([User])] /* 安装数据模型 */,
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

然后,在 user.controller.ts 中增加两个接口,一个 get 接口返回页面,另一个 post 接口用来新增 数据:

// user.controller.ts

import { Controller, Get, Post } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  constructor(private userService: UserService) {}

  @Get('listPage')
  getListPage() {
    return this.userService.getListPage();
  }

  @Post('create')
  create() {
    return this.userService.create();
  }
}

再在 user.service.ts 中添加操作数据库的逻辑:

// user.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
  ) {}

  async getListPage() {
    const userlist = this.userRepository.createQueryBuilder('user').getMany();
    const list = await userlist;
    return list;
  }

  async create() {
    const user = { username: '张三', sex: 1 };
    const result = await this.userRepository.save(user);
    return result;
  }
}

至此就可以访问这两个接口操作数据库了。现在的get接口只是查询数据并没有返回到页面上。

3. 渲染返回页面

然后我们渲染查询的结果放在page页面上。

创建 page.ejs 页面。

// page.ejs

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>用户列表</title>
    <style>
      table{
        border-collapse: collapse;
      }
      th,td{
        border: 1px solid #ccc;
        padding: 5px 15px;
      }
    </style>
  </head>
  <body>
    <h1>用户列表</h1>

    <table>
      <tr>
        <% Object.entries(list[0]).forEach(function(item){%>
        <th><%- item[0] %></th>
        <% }) %>
      </tr>

      <% list.forEach(function(item){%>
      <tr>
        <% Object.entries(item).forEach(function(b){%>
        <td><%- b[1] %></td>
        <% }) %>
      </tr>
      <% }) %>
    </table>
  </body>
</html>

然后通过 Render 方法返回渲染出来的页面:

// user.controller.ts

import { Controller, Get, Post, Render } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  constructor(private userService: UserService) {}

  @Get('listPage')
  @Render('page')  /* 渲染page页面 */
  getListPage() {
    return this.userService.getListPage();
  }

  @Post('create')
  create() {
    return this.userService.create();
  }
}

浏览器访问:http://localhost:3000/user/listPage 看到如下页面:

Nest项目(三)-连接 MySQL 数据库并渲染页面

rest 介绍

介绍一个vscode插件 REST,可以在编辑器中发起接口请求,用来测试 接口 很方便。

安装

Nest项目(三)-连接 MySQL 数据库并渲染页面

如图所示安装即可。

编写接口请求

创建 user.rest 文件并编写如下内容:

@host = http://localhost
@port = 3000
@contentType = application/json

###

# 获取列表
GET {{host}}:{{port}}/users HTTP/1.1
content-type: {{contentType}}

###

# 获取列表页面
GET {{host}}:{{port}}/user/listPage HTTP/1.1
content-type: {{contentType}}

###

# 添加一条数据
POST {{host}}:{{port}}/user/create HTTP/1.1
content-type: {{contentType}}

发起请求

发起请求的方法方法更容易了:

Nest项目(三)-连接 MySQL 数据库并渲染页面

点击 Send Request 即可,会在右面看到请求结果。

下一步计划

前后端分离,通过 ajax 的方式来请求数据,并进行页面渲染和交互,并统一接口返回体。

参考文档

转载自:https://juejin.cn/post/7171992098332213284
评论
请登录