Type-GraphQL官网地址(点击跳转)
yarn add type-graphql graphql @types/graphql reflect-metadata --save
import { Length, IsEmail, IsMobilePhone } from 'class-validator';
import { ObjectType, Field, ID } from 'type-graphql';
import userType from "../../types/userType";
@ObjectType()//注册成一个graphql类 相当于graphql的type
export default class User implements userType {
@Field(type => ID, { nullable: true,description:'这是主键字段,数据库中存储的_id' })//可以为空,这是主键 使用函数语法,我们解决了循环依赖的问题
public _id?: string;
@Length(2, 16, {
message: 'name要在长度在2-16之间'
})
@Field()
public name!: string;
@IsEmail({}, {
message: '邮箱不正确'
})
@Field()
public email!: string;
@IsMobilePhone('zh-CN', {
message: '手机号不正确'
})
@Field()
public phone!: string;
@Length(4, 16, {
message: 'password要在长度在4-16之间'
})
@Field()
public password!: string;
@Field({ nullable: true })
public header?: string;
@Field(type => [String], { nullable: true })
public friends?: [{ friendId: string; status: boolean; }];
@Field()
public times!: number;
}
class Friend {
public friendId!: string;
public status!: boolean;
}
@ObjectType() 注册成一个graphql类 相当于graphql的type
@Field() 相当于把typescript的类型定义转换成graphql的类型定义
nullable: true 表示可以为空
type => [String] 告诉graphql这是一个返回String数组
上面其它标签使用了 class-validator 类验证器,需要了解的可以观看这个博客,不想的话忽略掉 class-validator导出的
routing-controllers、class-validator、typedi的使用(点击跳转)
export default interface userType {
_id?: string;
name: string;
email: string;
phone: string;
password: string;
header?: string;
friends?: [{
friendId: string,
status: boolean
}];
times: number;
}
当有2-3个args时,这很有效。但是当你有更多时,解析器的方法定义会变得臃肿。在这种情况下,我们可以使用类定义来描述参数。它看起来像对象类型类,但它@ArgsType()顶部有装饰器。
)import { ArgsType, Field, ID } from "type-graphql";
//
// 对象类型类 graphql
@ArgsType()//参数集的类型
export class getUserArgs {
//ID表示是一个数据库的唯一标识,也是graphql定义的主键表示
@Field(type=>ID,{ nullable: true})
public _id!: string;
@Field({ nullable: true, defaultValue: 'mack' })
//defaultValue选项或使用属性初始化程序为装饰器中的可选字段定义默认值
public seachName!:string;
}
import { Resolver, Query, Arg,Args } from 'type-graphql';
import { Users } from '../../db/coll-connect';//连接数据库
import User from "../pojo/User";//引入pojo类
import { getUserArgs } from '../types/typeObject';
//如果不需要这个类注册,isAbstract: => 是否是抽象的 => @Resolver({ isAbstract: true }),并且表示这个不是用来执行注册的的
@Resolver()//注册成一个graphql 解析程序
export class UserResolver {
//@Query装饰器,类方法标记为GraphQL查询
@Query(returns => User)//返回一个User类型的对象,也可以是一个Promise
public async getUser(@Arg('_id', { nullable: true }) _id: string): Promise<User> {//@Arg('_id',{ nullable: true }) 接收graphql的参数_id,可以为空
return await Users.findById(_id) as any;
}
@Query(returns => [User])//返回一个对象数组
public async seachUsers(@Args() { _id, seachName }: getUserArgs): Promise<[User]> {
const regex = new RegExp(seachName, 'i'); //不区分大小写, => $options: '$i'
return await Users.find({ name: { '$regex': regex } }) as any;
}
}
//定义一个schema.ts文件
import { buildSchema } from 'type-graphql';
import { UserResolver } from './resolver/resolver';//注册一个查询修改的graphql schema
export const schema = async () => {
return await buildSchema({
resolvers: [UserResolver],
//或者使用
//resolvers: [__dirname + "/api/graphql/**/*.ts"]
//当有多个解析程序类时,手动导入可能很麻烦。所以我们也可以提供一个解析器模块文件的路径数组,其中包括globs:
});
}
buildSchema把解析程序等注册成一个真正的schema,提供给graphql程序注册
(mongodb数据库连接和依赖就不出代码了)
yarn add express-graphql express @types/express ts-node --save
server.ts
import "reflect-metadata";
import express, { Express } from 'express';
import expressGraphql from 'express-graphql';
import { schema } from './graphql/schema';
const app: Express = express();
//使用graphql
(async ()=>{
app.use('/graphql',expressGraphql({
schema: await schema(),
graphiql: true
}))
})();
const port: number = (process as any).env.PORT | 5000;
app.listen(port, () => {
console.log(`服务器运行在${port}端口`);
});
yarn add koa-graphql koa-mount koa ts-node --save
server.ts
import "reflect-metadata";
import Koa from 'koa';
import mount from 'koa-mount';
import expressGraphql from 'express-graphql';
import { schema } from './graphql/schema';
const app = new Koa();
//使用graphql
(async ()=>{
app.use(mount('/graphql',expressGraphql({
schema: await schema(),
graphiql: true
})))
})();
const port: number = (process as any).env.PORT | 5000;
app.listen(port, () => {
console.log(`服务器运行在${port}端口`);
});
require('ts-node/register');
require('./server');
这样一个查询的type-GraphQL的小实例就完成了
import { InputType, Field, ID } from "type-graphql";
import User from "../pojo/User";
@InputType({ description: "New user data" })
export class AddUserInput implements Partial<User> {
@Field()
public name!: string;
@Field()
public password!: string;
@Field()
public phone!: string;
@Field()
public email!: string;
@Field()
public times!: number;
}
import { Resolver, Query, Arg,Args,Mutation } from 'type-graphql';
import { Users } from '../../db/coll-connect';//连接数据库
import User from "../pojo/User";//引入pojo类
import { getUserArgs } from '../types/typeObject';
import { AddUserInput } from '../types/inputType';
//如果不需要这个类注册,isAbstract: => 是否是抽象的 => @Resolver({ isAbstract: true }),并且表示这个不是用来执行注册的的
@Resolver()//注册成一个graphql 解析程序
export class UserResolver {
//@Query装饰器,类方法标记为GraphQL查询
@Query(returns => User)//返回一个User类型的对象,也可以是一个Promise
public async getUser(@Arg('_id', { nullable: true }) _id: string): Promise<User> {//@Arg('_id',{ nullable: true }) 接收graphql的参数_id,可以为空
return await Users.findById(_id) as any;
}
@Query(returns => [User])//返回一个对象数组
public async seachUsers(@Args() { _id, seachName }: getUserArgs): Promise<[User]> {
const regex = new RegExp(seachName, 'i'); //不区分大小写, => $options: '$i'
return await Users.find({ name: { '$regex': regex } }) as any;
}
@Mutation(returns => User)//异变、添加修改删除操作
//user参数,一个对象类型的参数,类型是 AddUserInput,返回一个promise对象
public async addUser(@Arg("user") newUser: AddUserInput): Promise<User> {
return await Users.create(newUser) as any;
}
}
@Arg("user") newUser: AddUserInput,key为user,value为一个对象类型的参数,类型是 AddUserInput,返回一个promise对象,同时返回一个User对象的数据
@InputType({ description: 'update user date' })
export class UpdateUserInput extends AddUserInput{
@Field(type => ID)
public _id!: string;
}
// - import { AddUserInput } from '../types/inputType';
import { AddUserInput, UpdateUserInput } from '../types/inputType';
@Mutation(returns => User)//修改异变操作
public async updateUser(@Arg("user") updateUser: UpdateUserInput): Promise<User> {
const result:any = await Users.updateOne({ _id: updateUser._id }, updateUser) as any;
if(result.n === 1){
return await Users.findById(updateUser._id) as any;
}
return 404 as any;//不返回也可以,当请求出错,graphql自动返回错误
}
这样,CRUD中除了删除的案例都做好了
import { AuthChecker } from "type-graphql";
/**
* 安全认证,配合@Authorized()注解到对象里面的方法来使用,但是也要在schema.ts里面注册
* @param param0 里面包含context上下文,args参数数组,root等属性
* @param roles //数据库授权的参数
*/
export const customAuthChecker: AuthChecker<any> = async (
{ root, args, context, info },
roles,
) => {
if(context.headers['authorization']){//判断有没有传入token
return true;
}else{
return false;
}
};
import { buildSchema } from 'type-graphql';
import { customAuthChecker } from './authorized/auth';
import { UserResolver } from './resolver/resolver';//注册一个查询修改的graphql
export const schema = async () => {
return await buildSchema({
resolvers: [UserResolver],
authChecker: customAuthChecker,//安全认证注册
validate: false,//禁用默认验证
// resolvers: [__dirname + "/api/graphql/**/*.ts"]
});
}
import { Authorized } from 'type-graphql';
//...
@Authorized()
//@Query装饰器,类方法标记为GraphQL查询
@Query(returns => User)//返回一个查询为User的类型,反射元数据系统将返回类型显示为a Promise,因此我们必须添加装饰器的参数,returns => User以声明它解析为User对象类型 //参数 可以为空? 默认值
public async getUser(@Arg('_id', { nullable: true }) _id: string): Promise<User> {
return await Users.findById(_id) as any;
}
//...
这样就注册成功了安全认证
我们还可以将中间件附加到ObjectType的字段中
终止执行并且错误返回给用户,例如当解析器参数不正确时,我们也可以在中间件中抛出错误。这样我们就可以创建一个阻止访问解析器的防护,并防止执行或任何数据返回。
import { MiddlewareFn } from "type-graphql";
//中间件可以通过不调用next函数来破坏中间件堆栈。这样,将使用从中间件返回的结果,而不是调用解析器并返回它的结果。
export const CompetitorDetector: MiddlewareFn = async ({ args }, next) => {
if (args.frameworkName === "type-graphql") {//当args中包含这个字符串的时候,返回下面字符串
return "TypeGraphQL";
}
if (args.frameworkName === "typegql") {//返回一个错误
throw new Error("Competitive framework detected!");
}
return next();
};
import { buildSchema } from 'type-graphql';
import { customAuthChecker } from './authorized/auth';
import { UserResolver } from './resolver/resolver';//注册一个查询修改的graphql
import { CompetitorDetector } from './middleware/errormiddle';//调用错误中间件
export const schema = async () => {
return await buildSchema({
resolvers: [UserResolver],
authChecker: customAuthChecker,//安全认证
validate: false,//禁用默认验证
globalMiddlewares:[CompetitorDetector]//注册全局中间件
// resolvers: [__dirname + "/api/graphql/**/*.ts"]
});
}
//但是,当有多个解析程序类时,手动导入可能很麻烦。所以我们也可以提供一个解析器模块文件的路径数组,其中包括globs:
globalMiddlewares:[CompetitorDetector]//注册全局中间件
(我们还可以将中间件附加到ObjectType字段中,与@Authorized()装饰器相同。)
import { CompetitorDetector } from '../middleware/competitorMiddle';
//...
@UseMiddleware(CompetitorDetector)
@Authorized()
//@Query装饰器,类方法标记为GraphQL查询
@Query(returns => User)//返回一个查询为User的类型,反射元数据系统将返回类型显示为a Promise,因此我们必须添加装饰器的参数,returns => User以声明它解析为User对象类型 //参数 可以为空? 默认值
public async getUser(@Arg('_id', { nullable: true }) _id: string): Promise<User> {
return await Users.findById(_id) as any;
}
//...
https://typegraphql.ml/docs/installation.html
const url = 'http://localhost:5000/graphql';//请求url
const Authorization = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkNWNmOTkyNDE1MmJiZTIzN2E2ZWVlMiIsImVtYWlsIjoiMjI3ODc2NTEwNUBxcS5jb20jY0Njg1MTMsImV4cCI6MTU2NjQ3NTcxM30.kfjReuewYWHwKrK3wJtzvBrA7IOAl96e8Z6vY8UKe_c";//token
const data = {//请求数据
query: `query($seachName:String){
seachUsers(_id:"5d5e31500ef1f6f6f3f0f0d0",seachName:$seachName){
_id,
name,
times,
phone,
password,
email
}
getUser(_id:"5d5e31500ef1f6f6f3f0f0d0"){
_id,
name,
times,
phone,
email
}
}`,
variables: {//对变量进行赋值$seachName:String
seachName: 'w'
}
}
// _id, name,times,phone,email...成功请求后需要接收的字段
fetch(url, {
method: "POST",
headers: {
Authorization,//设置请求头发送请求
'Content-Type': "application/json",
'Accept': "application/json"
},
body: JSON.stringify(data)
})
.then(res => res.json())
.then(data => { console.log(data) })
const data = {
query:`mutation($user:AddUserInput!){
addUser(
user:$user
){
_id,name,password
}
}`,
variables:{
user:{
times:Date.now(),
name:'xxx',
password:"123456",
phone:'13444422554',
email:'[email protected]'
}
}
};
这样简单的一个Type-GraphQL结合node的express,koa的实例就完成了
觉得不错的可以点个赞,收个藏,关个注