当前位置: 首页 > 后端技术 > Node.js

【开源】Koa+GraphQL实战(Typescript版)

时间:2023-04-03 20:34:51 Node.js

前几天看了一篇文章,GraphQLwithKoa最佳入门实践,里面很详细的描述了koa集成graphql的实战,不过是js版本。而且因为是两年前的文章,所以现在有些包更新了使用方法。于是一时兴起,在原作者版本的基础上更新了一个ts版本。代码在这里https://github.com/sunxiuguo/Koa-GraphQL-Template,喜欢的话请开始一波star吧?~主要变化是graphql->type-graphqlmongoose->typegoosegraphql-server-koa->apollo-server-koa增加nodemon监控代码变化,用typegoose替换mongoose进行热更新。考虑到后续要使用type-graphql,我们将实体定义单独取出来,新建一个entities文件夹。在typegoose中,字段被定义为一个classsrc/entities/info.tsexportclassInfo{//arraytype@arrayProp({items:String})publichobby!:string[];@prop()公高!:string;@prop()publicweight!:number;//其他文件定义的元类型@prop({_id:false})publicmeta!:Meta;//不生成_id}类定义好后,我们来生成modelexportconstInfoModal=getModelForClass(Info);//是不是很简单!mongohooks原来的mongoosehooks,比如schema.pre('save',function(){}),已经改为装饰器@pre('save',function(){//直接this.meta.xxx赋值不会生效this.meta=this.meta||{};if(this.isNew){this.meta.createdAt=this.meta.updatedAt=Date.now();}else{this.meta.updatedAt=Date.now();}})exportclassInfo{@arrayProp({items:String})publichobby!:string[];@prop()公高!:string;@prop()公开权重!:number;@prop({_id:false})publicmeta!:元数据;//不生成_id}exportconstInfoModal=getModelForClass(Info);同样,让我们??定义学生实体src/entities/student.tsimport{prop,getModelForClass,Ref,pre}from'@typegoose/typegoose';import{Info}from'./info';import{Meta}from'./meta';@pre('save',function(){//直接赋值this.meta.xxx不会生效this.meta=this.meta||{};if(this.isNew){this.meta.createdAt=this.meta.updatedAt=Date.now();}else{this.meta.updatedAt=Date.now();}})exportclassStudent{@prop()publicname!:string;@prop()公共性!:细绳;@prop()publicage!:number;//info字段与Info实体的id相关联。在typegoose中,我们使用Ref来定义@prop({ref:Info})publicinfo!:Ref;@prop({_id:false})publicmeta!:Meta;}exportconstStudentModel=getModelForClass(Student);usetype-graphqlinsteadofgraphql上面我们已经定义了student和info实体的类型,如果再定义type-graphql,那岂不是麻烦到爆了?!幸运的是,type-graphql还提供了一种定义类的方法。使用装饰器来定义各种类型。我们可以直接在student.ts和info.ts中添加几行type-graphql定义定义字段类型src/entities/student.tsimport{Field,ObjectType}from'type-graphql';@ObjectType()exportclassStudent{@Field({description:'id'})public_id?:string;@Field({description:'Name'})@prop()publicname!:string;@Field({description:'gender'})@prop()publicsex!:string;@Field({description:'age'})@prop()publicage!:number;@Field(()=>Info,{description:'infoid'})@prop({ref:Info})publicinfo!:Ref;@Field(()=>Meta,{description:'time'})@prop({_id:false})publicmeta!:Meta;}Definitionresolver这里定义了带参数的query、关联的query和三个新的方法.src/graphql/resolvers/student.tsimport{Resolver,Query,Mutation,Arg,InputType,Field,Args,ArgsType}from'type-graphql';import{StudentModel,Student}from'../../entities/student';@Resolver(Student)exportclassStudentResolver{@Query(()=>[Student],{nullable:true,description:'查询学生列表'})asyncstudents(@Args()args:StudentArgs){//TODOargs必须是必需的吗?返回等待StudentModel.find(args);}@Query(()=>[Student],{nullable:true,description:'Querystudentslist(withInfo)'})asyncstudentsWithInfo(){returnawaitStudentModel.find({}).populate('info','爱好身高体重').exec();}@Mutation(()=>Student)asyncsaveStudent(@Arg('data')newStudent:StudentInput){conststudent=newStudentModel(newStudent);constres=awaitstudent.save();返回资源;在type-graphql中,query和mutation的参数需要分别用ArgsType()和InputType()来定义。这里因为我的两个参数不一样,定义了两份。@InputType()classStudentInput{@Field({description:'Name',nullable:false})publicname!:string;@Field({description:'Gender',nullable:false})publicsex!:string;//考虑enum@Field({description:'age',nullable:false})publicage!:number;@Field({description:'infoid',nullable:false})publicinfo!:string;}@ArgsType()classStudentArgs{@Field({description:'name',nullable:true})publicname?:string;@Field({description:'gender',nullable:true})publicsex?:string;@Field({description:'age',nullable:true})publicage?:number;@Field({description:'infoid',nullable:true})publicinfo?:string;}Koa服务集成graphqlsrc/graphql/index.ts原文中的graphql-server-koa包已经更新为apollo-server-koa,本文使用的是v2版本。现在我们已经定义了架构,下一步是初始化apolloServer。因为初始化的时候需要传入schema参数,所以我们先获取schema。获取所有定义的模式import{buildSchema}from'type-graphql';importpathfrom'path';//获取匹配所有解析器的路径functiongetResolvers(){return[path.resolve(__dirname+'/resolvers/*.ts')];}//使用buildSchema方法生成graphqlschemaasyncfunctiongetSchema(){returnbuildSchema({resolvers:getResolvers()});}集成koa因为我们是为已有的koa服务集成graphql,所以我们单独写一个函数去做这个。导出异步函数integrateGraphql(app:Koa){constserver=newApolloServer({schema:awaitgetSchema(),playground:{settings:{'request.credentials':'include'}}和任何一样,内省:真,上下文:({ctx})=>ctx});server.applyMiddleware({app});returnserver;}在初始化Koa的时候,可以直接调用这个函数来支持graphqlconstapp=newKoa();integrateGraphql(app).then(server=>{//使用bodyParser和KoaStatic中间件app.use(bodyParser());app.use(KoaStatic(__dirname+'/public'));app.use(router.routes()).use(router.allowedMethods());app.listen(4000,()=>{console.log(`服务器在${server.graphqlPath}运行成功`);});});使用nodemon监视代码更改。热更新服务非常简单。我们安装好nodemon后,直接添加一条命令即可。nodemon--watchsrc-ets--exects-nodesrc/start.ts这个命令的意思是监听src文件下的所有ts文件,执行src/start.ts文件。大功告成,开始吧!yarnserve访问graphql查询页面现在我们可以访问graphql查询页面了,开始检查,修改吧!http://localhost:4000/graphql让我们先插入一个infomutation{saveInfo(data:{hobby:["sing","jump","rap","basketball"],height:"165",weight:100}){hobbyheightweight}}然后再来查询#在这里写你的query或mutationquery{#students(age:22){#sex#name#age#}#studentsWithInfo{#sex#name#age#}infos{_id身高体重爱好}}完成!!!Github地址码在这里https://github.com/sunxiuguo/Koa-GraphQL-Template,喜欢的话请用小手star?~转载请注明原文链接和作者。