简介
本文是《全栈开发 GraphQL + Flutter 最佳实践,文章系列》中的一员。主要介绍如何使用 TypeORM 接入数据库:
- 安装 TypeORM
- 创建连接
- 创建实体类
- 新增实体
- 修改实体
- 删除实体
什么是 TypeORM ?
TypeORM 是一个对象关系映射,可以在 NodeJS,浏览器,Cordova,PhoneGap,Ionic,React Native,NativeScript,Expo 和 Electron 平台上运行,并且可以与 TypeScript 和 JavaScript(ES5,ES6,ES7,ES8)一起使用。 其目标是始终支持最新的 JavaScript 功能,并提供其他功能,来帮助您开发需要使用数据库的应用程序。可以小到带有少量表的小型应用程序,也可以大到具有多个数据库的大型企业应用程序。
现在我们用 TypeORM 接入 MySQL 数据库。
项目依赖
-
初始项目
- 如果你已经完成《10 分钟搭建一个 GraphQL 后端项目》,可以继续使用项目代码
- 如果没有完成,可以点击这里下载初始项目
-
一个可连接的数据库
- 如果你已经完成《Docker 启动 MySQL 容器》,需确保 Docker 正常运行即可
- 如果你有其他可用数据库,下文需要自行替换连接参数
安装 TypeORM
前往项目根目录,执行以下命令:
npm install --save mysql@2.18.1 \
typeorm@0.2.25
创建连接
接着在应用内接入数据库,打开文件src/index.ts
作如下修改:
import "reflect-metadata"
import { buildSchema, ObjectType, Field, ID, Resolver, Query } from "type-graphql";
import { ApolloServer } from "apollo-server";
+import { createConnection } from 'typeorm';
...
找到 main
函数,将其改为:
async function main() {
try {
// const schema = await buildSchema({
// resolvers: [PostResolver],
// dateScalarMode: 'timestamp'
// });
// const server = new ApolloServer({
// schema,
// playground: true
// });
// const { url } = await server.listen(4444);
// console.log(`GraphQL Playground available at ${url}`);
const connection = await createConnection({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'tinylearn',
password: 'my_password',
database: 'tinylearn',
synchronize: true,
});
console.log('Create connection success!');
} catch (error) {
console.error(error);
}
}
这里我们用 createConnection
方法创建连接,然后填入连接参数:
- type - 数据库类型
- host - 主机
- port - 端口号
- username - 用户名
- password - 密码
- database- 数据库
- synchronize - 是否将本地 schema 同步到数据库上去
如需其他配置项可以参考 TypeORM Connection Options。
启动服务:
$ npm start
如果运行后显示以下问题:
这是因为不支持授权插件方法,我们可以打开数据库修改授权方法。
目前,我们配置的 MySQL
数据库只有 root
账号有权限做此修改。因此,我们需要在 MySQL Workbench 中,新建一个 root
账号的连接。
可以参考《如何使用 MySQL Workbench 图形化工具来操作数据库?》。
账号填 root
,密码填写启动 MySQL 时的环境变量 MYSQL_ROOT_PASSWORD
:
然后执行以下 SQL 语句,修改授权方法:
ALTER USER 'tinylearn'@'%' IDENTIFIED WITH mysql_native_password BY 'my_password';
再次启动服务:
$ npm start
连接成功!
创建实体类
我们现在创建一个博文的实体类 PostEntity
,然后将它同步到数据库里面去。
src/index.ts:
import "reflect-metadata"
import { buildSchema, ObjectType, Field, ID, Resolver, Query } from "type-graphql";
import { ApolloServer } from "apollo-server";
import { CreateDateColumn, Entity, PrimaryGeneratedColumn, Column, createConnection } from 'typeorm';
@Entity()
class PostEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@CreateDateColumn()
created: Date;
@Column('text')
content: string;
}
@ObjectType()
class Post {
@Field(type => ID)
id: string;
@Field()
created: Date;
@Field()
content: string;
}
...
与数据库同步
src/index.ts:
const connection = await createConnection({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'tinylearn',
password: 'my_password',
database: 'tinylearn',
synchronize: true,
+ entities: [PostEntity],
});
console.log('Create connection success!');
激活终端,按下快捷键 ctrl+C
停止当前运行状态,然后使用 npm start
重新启动:
使用 MySQL Workbench 打开数据库,验证表格是否成功创建:
结果显示表格已经成功创建。
新增实体
我们尝试添加一条博文:
try {
...
console.log('Create connection success!');
const postRepository = connection.getRepository(PostEntity);
const result = await postRepository.insert({ content: '提供基于GraphQL API的数据查询及访问,「Hasura」获990万美元A轮...' });
console.log(`insert post success: ${JSON.stringify(result, null, 2)}`);
} catch (error) {
console.error(error);
}
重启服务 ctrl+C
, npm start
:
验证结果:
新增的博文已在最下方显示,我们先记下它的 id
,后面修改博文时会用到,这个 id
是自动生成的。
修改实体
现在来编辑一下博文内容:
try {
...
console.log('Create connection success!');
const postRepository = connection.getRepository(PostEntity);
const result = await postRepository
.update('fefc7d7c-4d33-45e2-b437-88db8e920f5d', { content: 'Hello GraphQL' });
console.log(`update post success: ${JSON.stringify(result, null, 2)}`);
} catch (error) {
console.error(error);
}
这里通过博文 id
来修改它的内容。
重启服务 ctrl+C
, npm start
:
验证结果:
内容已经修改成功!
删除实体
最后,我们尝试删除这篇博文:
try {
...
console.log('Create connection success!');
const postRepository = connection.getRepository(PostEntity);
const result = await postRepository
.delete('fefc7d7c-4d33-45e2-b437-88db8e920f5d');
console.log(`delete post success: ${JSON.stringify(result, null, 2)}`);
} catch (error) {
console.error(error);
}
重启服务 ctrl+C
, npm start
:
验证结果:
博文已经被删除了。
下一步
想了解更多 TypeORM 的使用方法请访问: typeorm.io/#/ 。
下一篇 《GraphQL + TypeORM 黄金搭档》,会把当前模拟数据源替换成 MySQL 数据源。这样我们就可以通过“接口”实现数据库的增删改查了。
源码
src/index.ts
import "reflect-metadata"
import { buildSchema, ObjectType, Field, ID, Resolver, Query } from "type-graphql";
import { ApolloServer } from "apollo-server";
import { CreateDateColumn, Entity, PrimaryGeneratedColumn, Column, createConnection } from 'typeorm';
@Entity()
class PostEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@CreateDateColumn()
created: Date;
@Column('text')
content: string;
}
@ObjectType()
class Post {
@Field(type => ID)
id: string;
@Field()
created: Date;
@Field()
content: string;
}
@Resolver(Post)
class PostResolver {
@Query(returns => [Post])
async posts(): Promise<Post[]> {
return [
{
id: "0",
created: new Date(),
content: '提供基于GraphQL API的数据查询及访问,「Hasura」获990万美元A轮...'
},
{
id: "1",
created: new Date(),
content: '为什么GraphQL是API的未来'
},
{
id: "2",
created: new Date(),
content: 'Netflix:我们为什么要将 GraphQL 引入前端架构?'
},
]
}
}
async function main() {
try {
// const schema = await buildSchema({
// resolvers: [PostResolver],
// dateScalarMode: 'timestamp'
// });
// const server = new ApolloServer({
// schema,
// playground: true
// });
// const { url } = await server.listen(4444);
// console.log(`GraphQL Playground available at ${url}`);
const connection = await createConnection({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'tinylearn',
password: 'my_password',
database: 'tinylearn',
synchronize: true,
entities: [PostEntity],
});
console.log('Create connection success!');
const postRepository = connection.getRepository(PostEntity);
// 新增
// const result = await postRepository.insert({ content: '提供基于GraphQL API的数据查询及访问,「Hasura」获990万美元A轮...' });
// console.log(`insert post success: ${JSON.stringify(result, null, 2)}`);
// 修改
// const result = await postRepository
// .update('fefc7d7c-4d33-45e2-b437-88db8e920f5d', { content: 'Hello GraphQL' });
// console.log(`update post success: ${JSON.stringify(result, null, 2)}`);
// 删除
const result = await postRepository
.delete('fefc7d7c-4d33-45e2-b437-88db8e920f5d');
console.log(`delete post success: ${JSON.stringify(result, null, 2)}`);
} catch (error) {
console.error(error);
}
}
main();