Type-GraphQL结合装饰器写法的node框架的学习笔记

Type-GraphQL结合装饰器写法的node框架

Type-GraphQL用装饰器的写法和一些改进的做法,让graphql的写法更加的清晰的在TypeScript中实现

Type-GraphQL官网地址(点击跳转)

还不怎么知道什么是graphql的可以先查看这个博客了解一下GraphQL的一些curd操作在express和koa的使用笔记(点击跳转)
1.graphQL依赖安装
yarn add type-graphql graphql @types/graphql reflect-metadata --save
2.Type-GraphQL的简单使用
2.1 定义pojo类 User.ts

Type-GraphQL结合装饰器写法的node框架的学习笔记_第1张图片

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的使用(点击跳转)

userType.ts
export default interface userType {
    _id?: string;
    name: string;
    email: string;
    phone: string;
    password: string;
    header?: string;
    friends?: [{
        friendId: string,
        status: boolean
    }];
    times: number;
}
2.2 定义typeObject.ts在types文件夹下(当有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;
}
2.3 定义一个resolver.ts

Type-GraphQL结合装饰器写法的node框架的学习笔记_第2张图片

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;
	}
}
2.4使用定义的UserResolver解析程序,注册成一个schema
//定义一个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程序注册
2.5 在express或者koa中注册Type-GraphQL的schema(mongodb数据库连接和依赖就不出代码了)
2.5.1 在express中使用,安装如下依赖
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}端口`);
});
2.5.2 在koa中使用,安装如下依赖
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}端口`);
});
2.5.3 定义一个index.js 用来使用ts-node 免编译ts代码
require('ts-node/register');
require('./server');
这样一个查询的type-GraphQL的小实例就完成了
接下来就是完善功能和增加type-graphql的功能
3.Mutation 增删改操作
3.1定义一个inputType装饰器装饰的类,表示是输入的,用于Mutation操作的
创建一个inputType.ts文件,实现type-graphql的Partial泛型接口
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;
}
目录结构如下:

Type-GraphQL结合装饰器写法的node框架的学习笔记_第3张图片

3.2在resolver.ts中使用,rerolver.ts文件如下
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对象的数据
3.3 再增加一个修改的mutation,在inputType.ts中加入以下代码
@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中除了删除的案例都做好了
4.接下来就是Type-GraphQL的安全认证authChecker
定义一个authorized文件夹,下面创建一个auth.ts文件
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;
    }
  };
修改schema.ts文件
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"]
    });
}
使用安全认证(注意:局部安全只能在类的方法/ObjectType注解后的类的字段中使用)
对之前的getUser类的方法使用安全认证,
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;
    }
//...

这样就注册成功了安全认证

5.Type-GraphQL中间件的使用,可以在使用graphql的同时注册一些中间件
5.1中间件定义(方法中间件) 我们还可以将中间件附加到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();
  };
  
5.2全局注册中间件
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]//注册全局中间件
5.3局部调用中间件(类的方法中使用)(我们还可以将中间件附加到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;
    }
//...
具体更详细的玩法,请看官方API

https://typegraphql.ml/docs/installation.html

6 fetch请求使用Type-GraphQL书写的API
查询操作
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) })
增加操作,修改data成以下这样即可,返回_id,name,password字段,按需获取返回字段,需要email则加上email
 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]'
		}
	}
};
AddUserInput类型是后台自己定义的类型
这样简单的一个Type-GraphQL结合node的express,koa的实例就完成了
在react vue中使用graphql接口/type-graphql接口
进阶type-GraphQL,用装饰器写法书写GraphQL,在typescript使用装饰器

觉得不错的可以点个赞,收个藏,关个注

还有其他的ts结合框架的实例,安装等,请点击头像观看本人的其它博客?

你可能感兴趣的:(TypeScript,Express,Nodejs,GraphQL,Type-GraphQL)