后端开挂!一个接口实现CRUD操作,这款工具绝了!

2023-05-27 0 891

做为前端合作开发,总之时常做的事是:调USB、画网页、调USB、画网页…

初始化的USB大机率是 restful 的,也是类似于此种:

/students 查阅大部份小学生重要信息

/student/1 查阅 id 为 1 的小学生重要信息

下面说的是 get 允诺。

假如对 /student/1 推送 POST、PUT、DELETE 允诺,就依次代表者了追加、修正、删掉。

这是 restful 艺术风格的 web USB。

此种USB回到甚么重要信息是服务器端这边下定决心的,应用程序而已传呵呵模块。

而相同情景下须要的统计数据相同,这时可能将就得新合作开发两个USB。的的版更捷伊这时候,USB会略有发生变动。

这种就很难引致一堆类似于的USB。

facebook 彼时也碰到了那个难题,只好她们缔造了一类捷伊USB同时实现计划:GraphQL。

用了 GraphQL 后,回到甚么统计数据无须是服务器端做主,而要应用程序她们下定决心。

服务器端只须要提供两个USB,应用程序通过那个接口就可以取任意格式的统计数据,同时实现 CRUD。

比如想查阅大部份的小学生,就可以这种:

后端开挂!一个接口实现CRUD操作,这款工具绝了!

想再查阅她们的年龄,就可以这种:

后端开挂!一个接口实现CRUD操作,这款工具绝了!

想查阅老师的名字和他教的小学生,就可以这种:

后端开挂!一个接口实现CRUD操作,这款工具绝了!

而这些都是在两个 http USB里完成的!

感受了 GraphQL 的好处了没?

两个 http USB就能同时实现大部份的 CRUD!

那这么强大的 GraphQL 是怎么同时实现的呢?

我们先写个 demo 快速入门呵呵:

facebook 提供了 graphql 的 npm 包,但那个封装的不够好,一般我们会用基于 graphql 包的 @apollo/server 和 @apollo/client 的包来同时实现 graphql。

首先引入那个包:

import { ApolloServer } from @apollo/server;

然后写一段这种的代码:

import { ApolloServer } from @apollo/server; const typeDefs = ` type Student { id: String, name: String, sex: Boolean age: Int } type Teacher { id: String, name: String, age: Int, subject: [String], students: [Student] } type Query { students: [Student], teachers: [Teacher], } schema { query: Query } `;

比较难看懂,定义了两个 Student 的对象类型,有 id、name、sex、age 这几个字段。

又定义了两个 Teacher 的对象类型,有 id、name、age、subject、students 这几个字段。students 字段是他教的小学生的重要信息。

然后定义了查阅的入口,可以查 students 和 teachers 的重要信息。

这种是两个 schema。

对象类型和对象类型之间有关联关系,老师关联了小学生、小学生也可以关联老师,关联来关联去这不是两个图么,也是 graph。

GraphQL 全称是 graph query language,是从那个对象的 graph 中查阅统计数据的。

现在我们声明的而已对象类型的关系,还要知道这些类型的具体统计数据,取统计数据的这部分叫做 resolver。

const students = [ { id: 1, name: async () => { await 取统计数据; return 光光 }, sex: true, age: 12 }, { id: 2, name:东东, sex: true, age: 13 }, { id: 3, name:小红, sex: false, age: 11 }, ]; const teachers = [ { id: 1, name: 神光, sex: true, subject: [体育, 数学], age: 28, students: students } ] const resolvers = { Query: { students: () => students, teachers: () => teachers } };

resolver 是取对象类型对应的统计数据的,每个字段都可以写两个 async 函数,里面执行 sql、访问USB等都可以,最终回到取到的统计数据。

当然,直接写具体的统计数据也是可以的。

这里我就 student 里那个 name 用 async 函数的方式写了呵呵。

这种有了 schema 类型定义,有了取统计数据的 resovler,就可以跑起 graphql 服务了。

也是这种:

import { startStandaloneServer } from @apollo/server/standalone const server = new ApolloServer({ typeDefs, resolvers, }); const { url } = awaitstartStandaloneServer(server, {listen: { port: 4000 }, }); console.log(` Server ready at: ${url}`);

传入 schema 类型定义和取统计数据的 resolver,就可以用 node 把服务跑起来。

后端开挂!一个接口实现CRUD操作,这款工具绝了!

有同学可能将问了,node 可以直接解析 esm 模块么?

可以的。只须要在 package.json 中声明 type 为 module:

后端开挂!一个接口实现CRUD操作,这款工具绝了!

那大部份的 .js 就都会做为 esm 模块解析:

后端开挂!一个接口实现CRUD操作,这款工具绝了!

跑起来后,浏览器访问呵呵:

就可以看到这种的 sandbox,这里可以执行 graphql 的查阅:

后端开挂!一个接口实现CRUD操作,这款工具绝了!

(graphql USB是监听 POST 允诺的,用 get 允诺那个 url 才会跑那个调试的辅助工具)

我查阅大部份小学生的 id、name、age 就可以这种:

后端开挂!一个接口实现CRUD操作,这款工具绝了!
后端开挂!一个接口实现CRUD操作,这款工具绝了!

这里 “光光” 那个小学生是异步取的统计数据,resolver 会执行对应的异步函数,拿到最终统计数据:

后端开挂!一个接口实现CRUD操作,这款工具绝了!

取老师的重要信息就可以这种:

后端开挂!一个接口实现CRUD操作,这款工具绝了!

这种我们就同时实现了两个 graphql USB!

感觉到甚么叫应用程序下定决心取甚么统计数据了么?

当然,我们这里是在 sandbox 里测的,用 @apollo/client 包也很简单。

比如 react 的 graphql 应用程序是这种的:

后端开挂!一个接口实现CRUD操作,这款工具绝了!

两个 gql 的 api 来写查阅语言,两个 useQuery 的 api 来执行查阅。

学起来很简单。

我们后还是直接在 sandbox 里测试。

有的同学可能将会说,假如我想查阅某个名字的老师的重要信息呢?

怎么传模块?

graphql 当然是支持的,这种写:

type Query { students: [Student], teachers: [Teacher], studentsbyTeacherName(name: String!): [Student] }

新加两个 query 入口,声明两个 name 的模块。(这里 String 后的 ! 代表者不能为空)

然后它对应的 resolver 是这种的:

const resolvers = { Query: { students: () => students, teachers: () =>teachers, studentsbyTeacherName: async (…args) => {console.log(args); await 执行了两个异步查阅 returnstudents } } };

studentsbyTeacherName 字段的 resolver 是两个异步函数,里面执行了查阅,然后回到了查到的小学生重要信息。

我们打印下模块看看传过来的是甚么。

有模块的查阅是这种的:

后端开挂!一个接口实现CRUD操作,这款工具绝了!

传入老师的 name 模块为 111,回到查到的小学生的 id、name 重要信息。

可以看到回到的是查阅到的结果。

而服务器端的 resolver 接收到的模块是这种的:

后端开挂!一个接口实现CRUD操作,这款工具绝了!

其余的几个模块不用管,只要知道第二个模块是应用程序传过来的查阅模块就好了。

这种我们就可以根据那个 name 模块同时实现异步的查阅,然后回到统计数据。

这就同时实现了有模块的查阅。

不是说 graphql 能取代 restful 做 CRUD 么?那增删改怎么做呢?

其实看到下面的有模块的查阅应该就能想到了,其实写起来差不多。

在 schema 里添加这种一段类型定义:

type Res { success: Boolean id: String } type Mutation { addStudent(name:String! age:Int! sex:Boolean!): Res updateStudent(id: String! name:String! age:Int! sex:Boolean!): Res deleteStudent(id: String!): Res } schema { mutation: Mutation query: Query }

和有模块的查阅差不多,只不过这部分增删改的类型要定义在 mutation 部分。

然后 resolver 也要有对应的同时实现:

async function addStudent (_, { name, age, sex }) { students.push({ id: 两个随机 id, name, age, sex }); return { success: true, id: xxx} }async function updateStudent (_, { id, name, age, sex }) { return { success: true, id: xxx } } async function deleteStudent (_, { id }) { return { success: true, id: xxx } } const resolvers = { Query: { students: ()=> students, teachers: () => teachers, studentsbyTeacherName: async (…args) => { console.log(args); await 执行了两个异步查阅 return students } }, Mutation: { addStudent: addStudent, updateStudent: updateStudent, deleteStudent: deleteStudent } };

和 query 部分差不多,只不过这里同时实现的是增删改。

我只对 addStudent 做了同时实现。

我们测试下:

执行 addStudent,添加两个小学生:

后端开挂!一个接口实现CRUD操作,这款工具绝了!

然后再次查阅大部份的小学生:

后端开挂!一个接口实现CRUD操作,这款工具绝了!

就可以查到刚来的小刚同学。

这种,我们就可以在两个 graphql 的 POST USB里完成大部份的 CRUD!

全部代码如下,感兴趣可以跑一跑(注意要在 package.json 里加个 type: “module”)

import { ApolloServer } from @apollo/server; import { startStandaloneServer } from @apollo/server/standalone const typeDefs = ` type Student { id: String, name: String, sex: Boolean age:Int } type Teacher { id: String, name: String, age: Int, subject: [String], students: [Student] } type Query { students: [Student], teachers: [Teacher], studentsbyTeacherName(name: String!): [Student] } type Res { success: Boolean id: String } type Mutation { addStudent(name:String! age:Int! sex:Boolean!): Res updateStudent(id: String! name:String! age:Int! sex:Boolean!): Res deleteStudent(id: String!): Res } schema { mutation: Mutation query: Query } `; const students = [ { id: 1, name: async () => { await 取统计数据; return 光光 }, sex: true, age: 12 }, { id: 2, name:东东, sex: true, age: 13 }, { id: 3, name:小红, sex: false, age: 11 }, ]; const teachers = [ { id: 1, name: 神光, sex: true, subject: [体育, 数学], age: 28, students: students } ] async function addStudent (_, { name, age, sex }) { students.push({ id: 两个随机 id, name, age, sex }); return { success: true, id: xxx } } async function updateStudent (_, { id, name, age, sex }) { return { success: true, id: xxx } } async function deleteStudent (_, { id }) { return { success: true, id: xxx } } const resolvers = { Query: { students: () => students, teachers: () => teachers, studentsbyTeacherName: async (…args) => { console.log(args); await 执行了两个异步查阅 return students } }, Mutation: { addStudent: addStudent, updateStudent: updateStudent, deleteStudent: deleteStudent } }; const server = new ApolloServer({ typeDefs, resolvers, }); const { url } = await startStandaloneServer(server, { listen: { port: 4000 }, }); console.log(` Server ready at: ${url}`);

完成了 graphql 的入门,我们再稍微思考下它的原理。graphql 是怎么同时实现的呢?

回顾整个流程,我们发现涉及到两种 DSL(领域特定语言),两个是 schema 定义的 DSL,两个是查阅的 DSL。

服务器端通过 schema 定义的 DSL 来声明 graph 图,通过 resolver 来接受模块,执行查阅和增删改。

客户端通过查阅的 DSL 来定义如何查阅和如何增删改,再发给服务器端来解析执行。

通过此种 DSL 同时实现了动态的查阅。

确实很方便很灵活,但也有缺点,是 parse DSL 为 AST 性能肯定是不如 restful 那种直接执行增删改查高的。

具体要不要用 graphql 还是要根据具体情景来做判断。

总结

restful USB是 url 代表者资源,GET、POST、PUT、DELETE 允诺代表者对资源的增删改查。

此种USB回到甚么统计数据完全由服务器端下定决心,每次USB发生变动可能将就得新加一类USB。

为了解决此种难题,facebook 缔造了 graphql,此种USB回到甚么统计数据完全由应用程序下定决心。增删改查通过这两个USB就可以搞定。

graphql 须要在服务器端定义 schema,也是定义对象类型和它的字段,对象类型和对象类型之间会有关联,也是两个 graph,查阅是从那个 graph 里查阅统计数据。

除了 schema 外,还须要有 resolver,它负责接受应用程序的模块,完成具体统计数据的增删改查。

graphql 会暴露两个 post USB,通过查阅语言的语法就可以从通过那个USB完成大部份增删改查。

本地测试的这时候,get 允诺会跑两个 sandbox,可以在这里测试USB。

整个流程涉及到两种新语言:schema 定义语言和 query 查阅语言。入门后向深入的话是要学下这两种 DSL 的更多语法。

感受到 graphql 的强大之处了么?两个USB就可以同时实现大部份的 CRUD!

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务