解决方案:[译]如何基于已有的 REST API 实现 GraphQL API

优采云 发布时间: 2022-11-27 04:20

  解决方案:[译]如何基于已有的 REST API 实现 GraphQL API

  你把你爸爸的笑话放在哪里?当然是在数据库中。假设您是世界上最受欢迎的爸爸笑话数据库的管理员。项目的技术大纲是:使用一个REST API与数据库进行通信,这个REST API具有搜索和评分笑话的功能;该网站的访问者可以通过一个简单的用户界面对每个笑话进行评分。

  最近你了解到一种新技术GraphQL,它具有一定的灵活性,可以准确获取你需要的数据,并且使用单一的API节点。这听起来不错,因此您将在您的应用程序中使用该技术。但是,您不想对原创

REST API 进行太多更改。您的项目可以同时支持 REST API 和 GraphQL API 吗?

  在本文中,我们将讨论如何基于现有的 REST API 实现 GraphQL API。使用这种方法,您可以在项目未完成的模块中使用 GraphQL,而无需对原创

基于 REST API 的框架进行调整。

  如果您想查看最终结果,可以访问 REST API 代码以及前端和 GraphQL API 代码。也记得浏览网页,笑料很值得一看。

  初始架构

  该项目的后端最初是使用 Node 和 JSON Server 开发的。JSON Server 利用 Express 为从简单 JSON 文件生成的模拟数据库提供完整的 REST API。前端使用Vanilla JS实现,使用浏览器内置的Fetch API进行API请求。该应用程序托管在 Heroku 上,便于部署和监控。

  我们使用的 JSON 文件收录

一些笑话和评级信息。下面,我们完整地重现它:

  {

"jokes": [

{

"id": 1,

"content": "I don't often tell dad jokes, but when I do, sometimes he laughs."

},

{

"id": 2,

"content": "Why was the scarecrow promoted? For being outstanding in his field."

},

{

"id": 3,

"content": "What did the grape do when someone stepped on him? He let out a little whine."

},

{

"id": 4,

"content": "Einstein, Pascal, and Newton are playing hide and seek. Einstein covers his eyes and begins counting. While Pascal runs off and hides, Newton takes out some chalk and marks a square on the ground with side lengths of exactly 1 meter, then sits down inside the square. When Einstein is finished counting and sees Newton sitting on the ground, he yells, \"Ha, I've found you, Newton!\". Newton replies, \"No you haven't! You've found one Newton over a square meter. You've found Pascal!"

}

],

"ratings": [

{ "id": 1, "jokeId": 1, "score": 8 },

{ "id": 2, "jokeId": 2, "score": 3 },

{ "id": 3, "jokeId": 3, "score": 6 },

{ "id": 4, "jokeId": 1, "score": 7 },

{ "id": 5, "jokeId": 2, "score": 6 },

{ "id": 6, "jokeId": 3, "score": 4 },

{ "id": 7, "jokeId": 1, "score": 9 },

{ "id": 8, "jokeId": 2, "score": 10 },

{ "id": 9, "jokeId": 3, "score": 2 },

{ "id": 10, "jokeId": 4, "score": 10 },

{ "id": 11, "jokeId": 4, "score": 10 },

{ "id": 12, "jokeId": 4, "score": 10 },

{ "id": 13, "jokeId": 4, "score": 10 },

{ "id": 14, "jokeId": 4, "score": 10 },

{ "id": 15, "jokeId": 4, "score": 10 }

]

}

  JSON Server系统使用这个文件中的数据作为数据库的初始数据,然后实现了一套REST API,包括对GET、POST、PUT、PATCH和DELETE请求的支持。JSON Server的神奇之处在于使用这个API就可以实现对JSON文件的修改,因此数据库是完全交互的。JSON Server可以直接通过npm脚本启动,不需要安装,但是为了配置它和设置端口,我们可以写几行代码运行它,代码如下:

  const jsonServer = require('json-server')

const server = jsonServer.create()

const router = jsonServer.router('db.json')

const middlewares = jsonServer.defaults()

server.use(middlewares)

server.use(router)

server.listen(process.env.PORT || 3000, () => {

console.log(` JSON Server is running on port ${process.env.PORT || 3000}`)

})

  要测试此模拟数据库,您可以在本地克隆 API 相关存储库并运行 `npm install` 和 `npm start`。访问:3000/jokes 在浏览器中,页面会显示所有的笑话。Access: 3000/ratings ,页面会显示所有的评分信息。

  惊人的。我们可以在浏览器上运行应用程序的后台。现在我们在 Heroku 中托管 API。首先,您需要安装 Heroku 命令行工具。然后,我们就可以进行这些操作:登录,创建项目,推送到Heroku服务器,在浏览器中打开项目的操作界面。

  # 登录你的 Heroku 账户

heroku login

# 创建项目

heroku create dad-joke-dadabase-rest-api

# 将代码部署到 Heroku 服务端

git push heroku master

# 打开项目的后台页面

heroku open

  看,现在我们已经将 API 发布到公共网络上了!

  构建用户界面

  现在我们已经部署了一个可用的 REST API,我们可以制作前端页面并使用 API 在页面上呈现笑话数据并对笑话进行评分。以下 HTML 页面代码实现了一个容器,用于显示由 JavaScript 代码加载的笑话内容。

  

Dad Joke Dadabase

Dad Joke Dadabase

Rate this joke:

1

2

3

4

5

6

7

8

9

10

Average Rating: 7.8

See Next Joke

  JavaScript代码如下。与REST API交互的关键代码在于两次获取数据的请求。第一个请求通过访问 `/jokes?_embed=ratings` 获取数据库中的所有笑话,第二个请求是 POST 类型,它通过访问 `/ratings` 提交对笑话的评分。

  const jokeContent = document.querySelector('.jokeContent')

const jokeRatingValue = document.querySelector('.jokeRatingValue')

const nextJokeButton = document.querySelector('#nextJoke')

const jokes = []

let currentJokeIndex = -1

const displayNextJoke = () => {

currentJokeIndex++

if (currentJokeIndex >= jokes.length) {

currentJokeIndex = 0

}

const joke = jokes[currentJokeIndex]

jokeContent.textContent = joke.content

const totalScore = joke.ratings.reduce(

<p>

" />

(total, rating) => (total += rating.score),

)

const numberOfRatings = joke.ratings.length

const averageRating = totalScore / numberOfRatings

jokeRatingValue.textContent = averageRating.toFixed(1)

}

const submitJokeRating = () => {

const ratingInput = document.querySelector(&#39;input[name="yourRating"]:checked&#39;)

if (ratingInput && ratingInput.value) {

const score = Number(ratingInput.value)

const jokeId = jokes[currentJokeIndex].id

const postData = { jokeId, score }

fetch(&#39;/ratings&#39;, {

method: &#39;POST&#39;,

headers: {

&#39;Content-Type&#39;: &#39;application/json&#39;,

},

body: JSON.stringify(postData),

})

.then(response => response.json())

.then(responseData => {

const jokeToUpdate = jokes.find(joke => joke.id === responseData.jokeId)

jokeToUpdate && jokeToUpdate.ratings.push(responseData)

})

.finally(() => {

ratingInput.checked = false

displayNextJoke()

})

} else {

displayNextJoke()

}

}

nextJokeButton.addEventListener(&#39;click&#39;, submitJokeRating)

fetch(&#39;/jokes?_embed=ratings&#39;)

.then(response => response.json())

.then(data => {

jokes.push(...data)

displayNextJoke()

})

</p>

  安装和使用 Apollo 服务器

  这样,我们就完成了项目的架构:它有一个简单的页面,通过 REST API 与数据库通信。那么,我们如何使用 GraphQL?使用 GraphQL 之前需要做哪些准备?第一步,我们安装apollo-server-express,这是一个整合Apollo Server和Express的包。还需要安装 apollo-datasource-rest 包来集成 REST API 和 Apollo Server。然后,我们来配置服务器,我们需要编写如下代码:

  const express = require(&#39;express&#39;)

const path = require(&#39;path&#39;)

const { ApolloServer } = require(&#39;apollo-server-express&#39;)

const JokesAPI = require(&#39;./jokesAPI&#39;)

const RatingsAPI = require(&#39;./ratingsAPI&#39;)

const typeDefs = require(&#39;./typeDefs&#39;)

const resolvers = require(&#39;./resolvers&#39;)

const app = express()

const server = new ApolloServer({

typeDefs,

resolvers,

dataSources: () => ({

jokesAPI: new JokesAPI(),

ratingsAPI: new RatingsAPI(),

}),

})

server.applyMiddleware({ app })

app

.use(express.static(path.join(__dirname, &#39;public&#39;)))

.get(&#39;/&#39;, (req, res) => {

res.sendFile(&#39;index.html&#39;, { root: &#39;public&#39; })

})

.get(&#39;/script.js&#39;, (req, res) => {

res.sendFile(&#39;script.js&#39;, { root: &#39;public&#39; })

})

.get(&#39;/style.css&#39;, (req, res) => {

res.sendFile(&#39;style.css&#39;, { root: &#39;public&#39; })

})

app.listen({ port: process.env.PORT || 4000 }, () => {

console.log(` Server ready at port ${process.env.PORT || 4000}`)

})

  如您所见,我们配置了 Apollo Server 的三个属性:`typeDefs`、`resolvers` 和 `dataSources`。其中,`typeDefs` 属性收录

与我们的 GraphQL API 相关的模式。我们在相应的包中定义了笑话和评分的数据类型,以及如何查询和更新数据;`resolvers` 告诉服务器如何处理各种查询和更新需求,以及如何连接到数据源;最后,`dataSources` 大致描述了 GraphQL API 和 REST API 之间的关系。

  下面的代码定义了 `Joke` 和 `Rating` 数据类型,以及如何查询和更新数据。

  const { gql } = require(&#39;apollo-server-express&#39;)

const typeDefs = gql`

type Joke {

id: Int!

content: String!

ratings: [Rating]

}

type Rating {

id: Int!

jokeId: Int!

score: Int!

}

type Query {

joke(id: Int!): Joke

jokes: [Joke]

rating(id: Int!): Rating

ratings: [Rating]

}

type Mutation {

rating(jokeId: Int!, score: Int!): Rating

}

`

module.exports = typeDefs

  以下是JokesAPI类的代码,主要定义了笑话数据的创建、查询、更新、删除等方法,这些方法调用相应的REST API实现相关的数据操作。

  const { RESTDataSource } = require(&#39;apollo-datasource-rest&#39;)

class JokesAPI extends RESTDataSource {

constructor() {

super()

this.baseURL = &#39;https://dad-joke-dadabase-rest-api.herokuapp.com/&#39;

}

async getJoke(id) {

return this.get(`jokes/${id}?_embed=ratings`)

}

async getJokes() {

return this.get(&#39;jokes?_embed=ratings&#39;)

}

async postJoke(jokeContent) {

return this.post(&#39;jokes&#39;, jokeContent)

<p>

" />

}

async replaceJoke(joke) {

return this.put(&#39;jokes&#39;, joke)

}

async updateJoke(joke) {

return this.patch(&#39;jokes&#39;, { id: joke.id, joke })

}

async deleteJoke(id) {

return this.delete(`jokes/${id}`)

}

}

module.exports = JokesAPI</p>

  评分数据类似于笑话,不同之处在于“笑话”在每个实例中都更改为“评分”。获取这部分代码可以参考GitHub上的代码仓库。

  最后,我们设置解析器,我们在其中定义如何使用数据源。

  const resolvers = {

Query: {

joke: async (_source, { id }, { dataSources }) =>

dataSources.jokesAPI.getJoke(id),

jokes: async (_source, _args, { dataSources }) =>

dataSources.jokesAPI.getJokes(),

rating: async (_source, { id }, { dataSources }) =>

dataSources.ratingsAPI.getRating(id),

ratings: async (_source, _args, { dataSources }) =>

dataSources.ratingsAPI.getRatings(),

},

Mutation: {

rating: async (_source, { jokeId, score }, { dataSources }) => {

const rating = await dataSources.ratingsAPI.postRating({ jokeId, score })

return rating

},

},

}

module.exports = resolvers

  完成这些步骤后,我们就可以通过 Apollo Server 调用 GraphQL API 了。要在 Heroku 上托管新的前端和 GraphQL API,我们需要创建和部署第二个应用程序:

  # 创建 Heroku 应用程序

heroku create dad-joke-dadabase

# 把代码部署在 Heroku 上

git push heroku master

# 在本地打开 Heroku 应用程序

heroku open

  更改 API 端点函数以获取笑话的代码

  您会记得,我们有两个前端页面调用的 API 端点:它们的功能是获取笑话和提交评级。现在我们将REST API中获取笑话的代码改成GraphQL API形式:

  fetch(&#39;/jokes?_embed=ratings&#39;)

.then(response => response.json())

.then(data => {

jokes.push(...data)

displayNextJoke()

})

  我们将上面的代码改成:

  fetch(&#39;/graphql&#39;, {

method: &#39;POST&#39;,

headers: { &#39;Content-Type&#39;: &#39;application/json&#39; },

body: JSON.stringify({

query: `

query GetAllJokesWithRatings {

jokes {

id

content

ratings {

score

id

jokeId

}

}

}

`,

}),

})

.then(res => res.json())

.then(res => {

jokes.push(...res.data.jokes)

displayNextJoke()

})

  现在,我们可以在本地运行应用程序。事实上,从用户的角度来看,什么都没有改变。但是如果你在浏览器的开发者工具中查看网络请求,你会发现现在获取笑话是通过访问 `/graphql` 端点来实现的。惊人的!

  修改API端点函数提交评分代码

  一个 API 请求已完成,还有一个!我们现在修改评分函数的代码。提交评分的代码类似于:

  fetch(&#39;/ratings&#39;, {

method: &#39;POST&#39;,

headers: {

&#39;Content-Type&#39;: &#39;application/json&#39;,

},

body: JSON.stringify(postData),

})

.then(response => response.json())

.then(responseData => {

const jokeToUpdate = jokes.find(joke => joke.id === responseData.jokeId)

jokeToUpdate && jokeToUpdate.ratings.push(responseData)

})

.finally(() => {

ratingInput.checked = false

displayNextJoke()

})

  现在我们进行以下更改以使其使用我们的 GraphQL API:

  fetch(&#39;/graphql&#39;, {

method: &#39;POST&#39;,

headers: { &#39;Content-Type&#39;: &#39;application/json&#39; },

body: JSON.stringify({

query: `

mutation CreateRating {

rating(jokeId: ${jokeId}, score: ${score}) {

id

score

jokeId

}

}

`,

}),

})

.then(res => res.json())

.then(res => {

const rating = res.data.rating

const jokeToUpdate = jokes.find(joke => joke.id === rating.jokeId)

jokeToUpdate && jokeToUpdate.ratings.push(rating)

})

.finally(() => {

ratingInput.checked = false

displayNextJoke()

})

  经过快速测试,这段代码符合要求。同样,用户体验没有改变,但现在我们正在使用 `/graphql` 端点请求数据。

  综上所述

  我们做到了。我们基于现有的 REST API 成功地实现了一个 GraphQL API 端点。因此,我们也可以使用GraphQL来实现一些核心功能,现有的功能和原有的REST API都不需要修改。我们今天可以弃用 REST API,将来它可能会过时。

  虽然爸爸笑话数据库是一个完全虚拟的项目,但几乎所有在2015年GraphQL大会之前成立的科技公司都发现,如果自己改变技术路线,使用GraphQL,自身的情况就和爸爸笑话一样,也是可行的. 此外,还有一个好消息,Apollo Server 是一个更灵活的产品,它还可以从包括 REST API 端点在内的各种数据源获取数据。

  解决方案:信息流优化比较好的工具有哪些? -从创意思考,素材制作,落地页制作,数据监测

  关于素材图片的内容水平,强烈推荐这些

  素材图片检索:微信文章检索_今日头条文章内容检索_知乎问答内容检索-优采云

采集

大数据

  它可以帮助人们快速找到内容图片。当你需要写内容,需要写灵感或者想了解相关新闻时,可以使用该功能同时从今日头条、知乎、微信三个搜索引擎采集相关话题,获取最新消息。通过素材图片搜索找到一篇文章作为原创原型,从基础文字中扩展文章内容。

  内容统筹:需求树_需求分析报告_层级统筹专用工具-有财云采集

大数据

  

" />

  利用内容策划工具了解用户需求,需求就是总流量,找到文章主要话题的相关需求

  智能原创:伪原创_在线伪原创转换成_SEO文章内容原创文章-有财云采集

大数据

  使用智能原创功能增加原创性,绕过所有原创性检测和优化算法

  数据监控、关键词挖掘、数据统计分析

  这几类需求,有财云采集

官网大部分都可以处理百度站长工具,综合seo搜索,长尾词发现-有财云采集

大数据

  

" />

  可以使用优财云采集

-长尾关键词挖掘专用工具_关键词分析专用工具_挖掘工具来查询该域名的所有排名情况,具体可以查询域名下前100个关键词排名词和出价状态等。

  点击添加监控SEO综合实效监控-优财云采集

大数据,让您海量监控上千个您关注的域名整体状态

  点击seo分析SEO搜索-优财云采集

大数据,陪你看很多关于这个域名seo的详细资料

  在所有还能搜索到的域名下,排名多少的子域名,同行业竞争对手的网站,找到了多少个竞价词

  并匹配一些长尾关键词关键词、相关词、流量词、相关素材图片等,让你对用户需求有准确的把握。

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线