资讯专栏INFORMATION COLUMN

从零编写一个Koa + graphQL的案例

wangjuntytl / 1682人阅读

摘要:在的文档中看到了有集成的指导,所以在本地尝试下先用写出一个,之后再去与集成起来。

在Nest.js的文档中看到了有集成GraphQL的指导,所以在本地尝试下先用Koa写出一个DEMO,之后再去与Nest.js集成起来。

先写出数据库模型(这个文件是之前就存在的,没有做更改,将文件名改成了models.ts):

/**
 * Created by w on 2018/4/13.
 */
const mongoose = require("mongoose");

mongoose.Promise = global.Promise;
mongoose.connect("mongodb://localhost/ticket", {
  server: {
    socketOptions: {
      keepAlive: 1
    }
  }
});

const models = {
  users: {
    username: {
      type: String,
      required: true
    },
    password: {
      type: String,
      required: true
    },
    description: {
      type: String
    },
    createTime: {
      type: Date,
      default: new Date()
    }
  },
  tickets: {
    name: {
      type: String,
      required: true
    },
    price: {
      type: Number,
      requred: true
    },
    holdTime: {   //举办时间
      type: Date,
      required: true
    },
    count: {    //剩余数量
      type: String,
      required: true
    },
    place:{
      type: String,
      required: true
    },
    img: {
      type: String
    },
    description: {
      type: String
    },
    publicTime: {
      type: Date,
      default: new Date()
    },
  }
};

for (let m in models) {
  mongoose.model(m, mongoose.Schema(models[m]));
}

module.exports = {
  getModules: (name) => {
    return mongoose.model(name);
  }
};

之后编写各自模型的GraphQL查询文件(user.ts)):

// @ts-ignore
const {
  //@ts-ignore
  graphql, GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLID, GraphQLList, GraphQLNonNull, GraphQLInt, isOutputType,
} = require("graphql");
// @ts-ignore
const User = require("../db/model").getModules("users");

const userType = new GraphQLObjectType({
  name: "User",
  fields: {
    username: {
      type: GraphQLString,
    },
    password: {
      type: GraphQLString,
    },
    description: {
      type: GraphQLString,
    },
    createTime: {
      type: GraphQLString,
    }
  }
});

module.exports = {
  query: {
    type: new GraphQLList(userType),
    args: {
      username: {
        username: "username",
        type: GraphQLNonNull(GraphQLString)
      }
    },
    resolve(root, params, options) {
      if (params.username === "$all") {
        return User.find().exec()
      }
      return User.find({ username: params.username }).exec();
    }
  },
  mutate: {
    type: new GraphQLList(userType),
    args: {
      operate: {
        name: "operate",
        type: GraphQLString
      },
      username: {
        name: "username",
        type: GraphQLNonNull(GraphQLString)
      },
      possword: {
        name: "price",
        type: GraphQLNonNull(GraphQLString)
      },
      createTime: {
        name: "createTime",
        type: GraphQLString,
      },
      description: {
        name: "description",
        type: GraphQLString
      }
    },
    resolve: async (root, params, options) => {
      try {
        if (params.operate === "$delete") {
          await User.delete({username: params.username});

          return User.find().exec()
        }
        if (params.operate === "$update") {
          await User.update({username: params.username});

          return User.find({ username: params.username }).exec()
        }
        let user = new User({
          username: params.username,
          password: params.password,
          description: params.description,
          createTime: params.createTime
        });
        await user.save();

        return user.find({ username: params.username }).exec()
      } catch (e) {
        console.error("Error while save data: ", e);
      }
    }
  }
}

ticket.ts:

// @ts-ignore
const {
  // @ts-ignore
  graphql, GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLID, GraphQLList, GraphQLNonNull, GraphQLInt, isOutputType,
} = require("graphql");
// @ts-ignore
const Ticket = require("../db/model").getModules("tickets");

const ticketType = new GraphQLObjectType({
  name: "Ticket",
  fields: {
    name: {
      type: GraphQLString
    },
    price: {
      type: GraphQLInt
    },
    holdTime: {
      type: GraphQLString
    },
    count: {
      type: GraphQLInt
    },
    place: {
      type: GraphQLString
    },
    img: {
      type: GraphQLString
    },
    description: {
      type: GraphQLString
    },
    publicTime: {
      type: GraphQLString
    }
  }
});

module.exports = {
  query: {
    type: new GraphQLList(ticketType),
    args: {
      name: {
        name: "name",
        type: GraphQLNonNull(GraphQLString)
      }
    },
    resolve(root, params, options) {
      if (params.name === "$all") {
        return Ticket.find().exec()
      }
      return Ticket.find({ name: params.name }).exec();
    }
  },
  mutate: {
    type: new GraphQLList(ticketType),
    args: {
      operate: {
        name: "operate",
        type: GraphQLString
      },
      name: {
        name: "name",
        type: GraphQLNonNull(GraphQLString)
      },
      price: {
        name: "price",
        type: GraphQLInt
      },
      holdTime: {
        name: "holdTime",
        type: GraphQLNonNull(GraphQLString)
      },
      count: {
        name: "count",
        type: GraphQLNonNull(GraphQLInt)
      },
      place: {
        name: "place",
        type: GraphQLNonNull(GraphQLString)
      },
      img: {
        name: "img",
        type: GraphQLString
      },
      description: {
        name: "description",
        type: GraphQLString
      },
      publicTime: {
        name: "publicTime",
        type: GraphQLString
      }
    },
    resolve: async (root, params, options) => {
      try {
        if (params.operate === "$delete") {
          await Ticket.findOneAndRemove({ name: params.name });

          return Ticket.find().exec();
        }
        if (params.operate === "$update") {
          await Ticket.findByIdAndUpdate({ name: params.name }, {
            name: params.name,
            price: params.price,
            holdTime: params.holdTime,
            count: params.count,
            place: params.place,
            img: params.img,
            description: params.description,
            publicTime: params.publicTime
          });

          return Ticket.find().exec();
        }
        let ticket = new Ticket({
          name: params.name,
          price: params.price,
          holdTime: params.holdTime,
          count: params.count,
          place: params.place,
          img: params.img,
          description: params.description,
          publicTime: params.publicTime
        });
        await ticket.save();

        return Ticket.find({ name: params.name }).exec()
      } catch (e) {
        console.error("Error while save data: ", e);
      }
    }
  }
}

接下来编写用于查询或修改数据的Schema.ts:

const {
  //@ts-ignore
  GraphQLSchema, GraphQLObjectType,
} = require("graphql");
//@ts-ignore
const Ticket = require("./ticket");
//@ts-ignore
const User = require("./user");

module.exports = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: "Queries",
    fields: {
      Ticket: Ticket.query,
      User: User.query,
    }
  }),
  mutation: new GraphQLObjectType({
    name: "Mutations",
    fields: {
      Ticket: Ticket.mutate,
      User: User.mutate,
    }
  })
})

编写服务端(server.ts):

const Koa = require("koa");
const app = new Koa();
const koaBody = require("koa-body");
const CombileRouter = require("./combineRouter");

app
  .use(koaBody({
    multipart: true,
    formidable: {
      keepExtensions: true,
    },
  }))
  .use(CombileRouter.routes())
  .use(CombileRouter.routes());

app.listen(5000, () => {
  console.log("App running~");
})

定义ticket路由的ticket.router.ts:

const KoaRouter = require("koa-router");
const ticketRouter = new KoaRouter();
// @ts-ignore
const { graphqlKoa, graphiqlKoa } = require("graphql-server-koa");
// @ts-ignore
const Ticket = require("../db/model").getModules("tickets");
const ticketSchema = require("../graphql/schema");

ticketRouter.get("/ticket/all", async (ctx) => {
  try {
    let result = await Ticket.find({});

    ctx.body = { data: Object.assign({}, result), code: "0" };
  } catch (e) {
    console.error("Getting all ticket error: ", e);
  }
});

ticketRouter.post("/graphql", async (ctx, next) => {
  console.log("graphql", ctx.request.body);
  await graphqlKoa({schema: ticketSchema})(ctx, next)
})
.get("/graphql", async (ctx, next) => {
  await graphqlKoa({schema: ticketSchema})(ctx, next)
})
.get("/graphiql", async (ctx, next) => {
  await graphiqlKoa({endpointURL: "/graphql"})(ctx, next)
})


module.exports = ticketRouter;

还有之前定义的user.router.ts(测试这个user路由是否是reachable的):

const Router = require("koa-router");
const userRouter = new Router();
// @ts-ignore
const { graphiqlKoa } = require("graphql-server-koa");

userRouter.get("/user/all", async (ctx) => {
  ctx.body = {
    code: "0",
    msg: "OK",
    info: {
      data: [{
        username: "a",
        id: 0,
        desc: "ok",
      }, {
        username: "d",
        id: 1,
        desc: "ok",
      }, {
        username: "c",
        id: 2,
        desc: "ok",
      }, {
        username: "b",
        id: 3,
        desc: "ok",
      }]
    }
  }
});

module.exports = userRouter;

将两个路由文件融合起来(combineRouter.ts):

const UserRouter:object = require("./koa-router/user.router");
const TicketRouter:object = require("./koa-router/ticket.router");

const combiler = (routers: object[]): object => {
  let arr = [];
  
  routers.forEach(v => {
    //@ts-ignore
    arr.push(...v.stack)
  });
  return Object.assign(routers[0], { stack:arr });
}

const res = combiler([UserRouter, TicketRouter]);

module.exports = res;

OK了,这部分就写好了基础的query和mutation功能,还有删除的功能之后再加上。
附上tsconfig.json文件:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "sourceMap": true,
    "experimentalDecorators": true,
  },
  "exclude": [
    "node_modules"
  ]
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104265.html

相关文章

  • GraphQL 搭配 Koa 最佳入门实践

    摘要:如下图嗯,如图都已经查询到我们保存的全部数据,并且全部返回前端了。如图没错,什么都没有就是查询服务的界面。写好了之后我们在配置一下路由,进入里面,加入下面几行代码。 GraphQL一种用为你 API 而生的查询语言,2018已经到来,PWA还没有大量投入生产应用之中就已经火起来了,GraphQL的应用或许也不会太远了。前端的发展的最大一个特点就是变化快,有时候应对各种需求场景的变化,不...

    MoAir 评论0 收藏0
  • 编写 Node.js Rest API 10 个最佳实践

    摘要:要对进行黑盒测试测试的最好办法是对他们进行黑盒测试,黑盒测试是一种不关心应用内部结构和工作原理的测试方法,测试时系统任何部分都不应该被。此外,有了黑盒测试并不意味着不需要单元测试,针对的单元测试还是需要编写的。 本文首发于之乎专栏前端周刊,全文共 6953 字,读完需 8 分钟,速度需 2 分钟。翻译自:RingStack 的文章 https://blog.risingstack.co...

    ermaoL 评论0 收藏0
  • koa2 + graphql + typescript + jwt + typeormnodejs

    最近写了一个node项目,主要使用到的技术有: koa2 // nodejs 框架 koa-router // koa路由 graphql // 查询api typescript // 强类型语言 jwt // 授权 typeorm // typescript的一个orm mysql2 // 内容数据库 mongodb // 日志存储数据库 redis // 服务器缓存 项目结构:sh...

    Dogee 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<