如何使用斜线graphql后端构建pok%C3%A9dex react应用

Frontend developers want interacting with the backend of their web application to be as painless as possible. Requesting data from the database or making updates to records stored in the database should be simple so that frontend developers can focus on what they do best: creating beautiful and intuitive user interfaces.

前端开发人员希望与他们的Web应用程序的后端交互尽可能轻松。 从数据库请求数据或对存储在数据库中的记录进行更新应该很简单,以便前端开发人员可以专注于自己最擅长的事情:创建美观而直观的用户界面。

GraphQL makes working with databases easy. Rather than relying on backend developers to create specific API endpoints that return pre-selected data fields when querying the database, frontend developers can make simple requests to the backend and retrieve the exact data that they need — no more, no less. This level of flexibility is one reason why GraphQL is so appealing.

GraphQL使使用数据库变得容易。 前端开发人员不必依赖后端开发人员来创建在查询数据库时返回预选数据字段的特定API端点,而是可以向后端进行简单的请求并检索所需的确切数据,仅此而已。 这种灵活性是GraphQL如此吸引人的原因之一。

Even better, you can use a hosted GraphQL backend — Slash GraphQL (by Dgraph). This service is brand new and was publicly released on September 10, 2020. With Slash GraphQL, I can create a new backend endpoint, specify the schema I want for my graph database, and — voila! — be up and running in just a few steps.

更好的是,您可以使用托管的GraphQL后端-Slash GraphQL (由Dgraph提供)。 该服务是全新的,并于2020年9月10日公开发布。 使用Slash GraphQL,我可以创建一个新的后端端点,为我的图形数据库指定所需的架构,然后-瞧! —只需几个步骤即可启动并运行。

The beauty of a hosted backend is that you don’t need to manage your own backend infrastructure, create and manage your own database, or create API endpoints. All of that is taken care of for you.

托管后端的优点在于,您无需管理自己的后端基础结构,创建和管理自己的数据库或创建API端点。 所有这些都为您服务。

In this article, we’re going to walk through some of the basic setup for Slash GraphQL and then take a look at how I built a Pokémon Pokédex app with React and Slash GraphQL in just a few hours!

在本文中,我们将介绍Slash GraphQL的一些基本设置,然后看一下如何在短短几个小时用React和Slash GraphQL构建PokémonPokédex应用程序

You can view all of the code here on GitHub.

您可以在GitHub上查看所有代码

演示应用程序概述 (Overview of the Demo App)

Pokémon Pokédex app
Pokémon Pokédex app
神奇宝贝Pokédex应用

What 90s child (or adult, for that matter) didn’t dream of catching all 150 original Pokémon? Our demo app will help us keep track of our progress in becoming Pokémon masters.

哪个90年代的孩子(或成年人)没有梦想捕获全部150个原始的神奇宝贝? 我们的演示应用程序将帮助我们跟踪成为神奇宝贝大师的过程。

As we build out our app, we’ll cover all the CRUD operations for working with an API: create, read, update, and delete.

在构建应用程序时,我们将介绍使用API​​的所有CRUD操作:创建,读取,更新和删除。

We’ll start by adding all our Pokémon to the database online in Slash GraphQL’s API Explorer. Then, in the Pokédex app UI, we’ll display all 151 Pokémon queried from the database. (Hey, I couldn’t leave out Mew, could I?) At the top of the screen, we’ll show two dropdown menus that will allow us to filter the shown results by Pokémon type and by whether or not the Pokémon has been captured. Each Pokémon will also have a toggle switch next to it that will allow us to mark the Pokémon as captured or not. We won’t be deleting any Pokémon from our database via the app’s UI, but I’ll walk you through how that could be done in the event that you need to clean up some data.

我们将从在Slash GraphQL的API资源管理器中在线将所有神奇宝贝添加到数据库开始。 然后,在Pokédex应用程序UI中,我们将显示从数据库查询的所有151个Pokémon。 (嘿,我不能遗忘Mew,对吗?)在屏幕顶部,我们将显示两个下拉菜单,这些菜单可让我们根据神奇宝贝的类型以及神奇宝贝是否被被抓每个神奇宝贝旁边都会有一个拨动开关,这将使我们能够将神奇宝贝标记为已捕获或未捕获。 我们不会通过应用程序的用户界面从数据库中删除任何神奇宝贝,但我将带您逐步了解如何在需要清理某些数据的情况下完成该操作。

Ready to begin our journey?

准备开始我们的旅程了吗?

Slash GraphQL入门 (Getting Started with Slash GraphQL)

创建一个新的后端(Creating a New Backend)

Once you’ve created your Slash GraphQL account, you can have your GraphQL backend up and running in just a few steps:

创建Slash GraphQL帐户后,只需几个步骤即可使GraphQL后端启动并运行:

  1. Click the “Create a Backend” button.

    点击“创建后端”按钮。
  2. Give it a name. (For example, I chose “pokedex”.)

    给它起个名字。 (例如,我选择了“ pokedex”。)
  3. Optionally, give the API endpoint URL a subdomain name. (Again, I chose “pokedex”.)

    (可选)为API端点URL指定一个子域名。 (同样,我选择了“ pokedex”。)
  4. Optionally, choose a provider and a zone. (This defaults to using AWS in the US West 2 region.)

    (可选)选择提供程序和区域。 (默认情况下,在美国西部2地区使用AWS。)
  5. Click the “Create New Backend” button to confirm your choices.

    点击“创建新后端”按钮以确认您的选择。
  6. Get your backend endpoint. (Mine looks like this: https://pokedex.us-west-2.aws.cloud.dgraph.io/graphql.)

    获取您的后端端点。 (我的看起来像这样: https : //pokedex.us-west-2.aws.cloud.dgraph.io/graphql 。)

  7. Click the “Create your Schema” button.

    单击“创建您的架构”按钮。

That’s it! After creating a new backend, you’ll have a live GraphQL database and API endpoint ready to go.

而已! 创建新的后端后,您将可以使用实时GraphQL数据库和API端点。

Creating a new backend
Creating a new backend
创建一个新的后端

创建模式(Creating a Schema)

Now that we have our backend up and running, we need to create the schema for the type of data we’ll have in our database. For the Pokédex app, we’ll have a Pokémon type and a PokémonType enum.

现在我们已经启动并运行了后端,我们需要为数据库中的数据类型创建模式。 对于Pokédex应用程序,我们将有一个Pokémon类型和一个PokémonType枚举。

enum PokemonType {
  Bug
  Dark
  Dragon
  Electric
  Fairy
  Fighting
  Fire
  Flying
  Ghost
  Grass
  Ground
  Ice
  Normal
  Poison
  Psychic
  Rock
  Steel
  Water
}


type Pokemon {
  id: Int! @search
  name: String! @search(by: [fulltext])
  captured: Boolean! @search
  imgUrl: String!
  pokemonTypes: [PokemonType!]! @search
}

There’s a lot to unpack in that small amount of code! The PokémonType enum is straightforward enough—it's a set of all the Pokémon types, including Fire, Water, Grass, and Electric. The Pokémon type describes the shape of our data that we'll have for each Pokémon. Each Pokémon will have an ID, a name, an image URL for displaying the Pokémon's picture, the types of Pokémon it is, and a status indicating whether or not the Pokémon is captured.

这么少的代码有很多要解压的东西! PokémonType枚举非常简单,它是所有Pokémon类型的集合,包括火,水,草和电。 Pokémon类型描述了每个神奇宝贝所拥有的数据形状。 每个神奇宝贝都会有一个ID,一个名称,一个用于显示神奇宝贝图片的图像URL,它的神奇宝贝类型以及一个状态,该状态指示是否捕获了神奇宝贝。

You can see that each field has a data type associated with it. For example, id is an Int (integer), name and imgUrl are String types, and captured is a Boolean. The presence of an exclamation point ! means the field is required. Finally, adding the @search keyword makes the field searchable in your queries and mutations.

您可以看到每个字段都有与之关联的数据类型。 例如, id是一个Int (整数), nameimgUrlString类型, capturedBoolean 。 感叹号的存在! 表示必填字段。 最后,添加@search关键字可使该字段在您的查询和突变中可搜索。

To test out working with our database and newly created schema, we can use the API Explorer, which is a neat feature that allows us to run queries and mutations against our database right from within the Slash GraphQL web console.

为了测试使用我们的数据库和新创建的架构,我们可以使用API​​ Explorer,这是一个简洁的功能,使我们可以从Slash GraphQL Web控制台中直接对数据库运行查询和变异。

填充我们的数据库 (Populating Our Database)

Let’s use the API Explorer to insert all of our Pokémon into the Pokédex database. We’ll use the following mutation:

让我们使用API​​ Explorer将所有神奇宝贝插入Pokédex数据库。 我们将使用以下突变:

mutation AddPokemon {
  addPokemon(input: [
    { id: 1, name: "Bulbasaur", captured: false, imgUrl: "http://img.pokemondb.net/artwork/bulbasaur.jpg", pokemonTypes: [Grass, Poison] },
    { id: 2, name: "Ivysaur", captured: false, imgUrl: "http://img.pokemondb.net/artwork/ivysaur.jpg", pokemonTypes: [Grass, Poison] },
    { id: 3, name: "Venusaur", captured: false, imgUrl: "http://img.pokemondb.net/artwork/venusaur.jpg", pokemonTypes: [Grass, Poison] },
    { id: 4, name: "Charmander", captured: false, imgUrl: "http://img.pokemondb.net/artwork/charmander.jpg", pokemonTypes: [Fire] },
    { id: 5, name: "Charmeleon", captured: false, imgUrl: "http://img.pokemondb.net/artwork/charmeleon.jpg", pokemonTypes: [Fire] },
    { id: 6, name: "Charizard", captured: false, imgUrl: "http://img.pokemondb.net/artwork/charizard.jpg", pokemonTypes: [Fire, Flying] },
    { id: 7, name: "Squirtle", captured: false, imgUrl: "http://img.pokemondb.net/artwork/squirtle.jpg", pokemonTypes: [Water] },
    { id: 8, name: "Wartortle", captured: false, imgUrl: "http://img.pokemondb.net/artwork/wartortle.jpg", pokemonTypes: [Water] },
    { id: 9, name: "Blastoise", captured: false, imgUrl: "http://img.pokemondb.net/artwork/blastoise.jpg", pokemonTypes: [Water] },
  ]) {
    pokemon {
      id
      name
      captured
      imgUrl
      pokemonTypes
    }
  }
}

For brevity I’ve only shown the first nine Pokémon in the snippet above. Feel free to check out the full code snippet for adding all the Pokémon.

为简洁起见,我仅在上面的片段中显示了前九个神奇宝贝。 随意查看完整的代码段,以添加所有Pokémon

Adding all the Pokémon via the API Explorer
Adding all the Pokémon via the API Explorer
通过API资源管理器添加所有神奇宝贝

Now, for a quick sanity check, we can query our database to make sure that all our Pokémon have been added correctly. We’ll request the data for all our Pokémon like so:

现在,为了进行快速状态检查,我们可以查询数据库,以确保正确添加了所有神奇宝贝。 我们将像这样请求所有神奇宝贝的数据:

query GetAllPokemon {
  queryPokemon {
    id
    name
    captured
    imgUrl
    pokemonTypes
  }
}

Here’s what it looks like in the API Explorer:

这是API资源管理器中的外观:

Querying for all Pokémon in the API Explorer
Querying for all Pokémon in the API Explorer
在API Explorer中查询所有神奇宝贝

We could also write a similar query that only returns the Pokémon names if that’s all the data we need. Behold, the beauty of GraphQL!

我们也可以编写一个类似的查询,仅在我们需要的所有数据时才返回神奇宝贝的名字。 看哪,GraphQL的美丽!

query GetAllPokemonNames {
  queryPokemon {
    name
  }
}
Querying for all Pokémon names in the API Explorer
Querying for all Pokémon names in the API Explorer
在API Explorer中查询所有神奇宝贝名称

在应用程序中获取数据(Fetching Data in the App)

Now that we’ve added our Pokémon to the Pokédex and verified the data is in fact there, let’s get it to show up in our app. Our app was built with React and Material UI for the frontend and was bootstrapped using create-react-app. We won’t be going through step-by-step how to build the app, but we’ll highlight some of the key parts. Again, the full code is available on GitHub if you’d like to clone the repo or just take a look.

现在我们已经将神奇宝贝添加到了神奇宝贝中,并验证了数据确实存在,让我们将其显示在我们的应用中。 我们的应用程序使用前端的ReactMaterial UI构建,并使用create-react-app引导。 我们将不逐步介绍如何构建应用程序,但我们将重点介绍一些关键部分。 同样,如果您想克隆存储库或只是看看,完整的代码可以在GitHub上找到

When using Slash GraphQL in our frontend code, we essentially just make a POST request to our single API endpoint that we were provided when creating the backend. In the body of the request, we provide our GraphQL code as the query, we write a descriptive name for the query or mutation as the operationName, and then we optionally provide an object of any variables we reference in our GraphQL code.

在我们的前端代码中使用Slash GraphQL时,我们实际上只是向创建后端时提供的单个API端点发出POST请求。 在请求的主体中,我们提供GraphQL代码作为query ,为查询或突变编写一个描述性名称作为operationName ,然后我们有选择地提供一个在GraphQL代码中引用的任何variables的对象。

Here’s a simplified version of how we follow this pattern to fetch our Pokémon in the app:

这是我们遵循此模式以在应用程序中获取神奇宝贝的简化版本:

// Main generic GraphQL request
async function fetchGraphQL(operationsDoc, operationName, variables) {
  const result = await fetch(
    'https://pokedex.us-west-2.aws.cloud.dgraph.io/graphql',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        query: operationsDoc,
        operationName,
        variables,
      }),
    }
  )


  return await result.json()
}


// Fetch all Pokemon - GraphQL
const fetchAllPokemonOperationsDoc = `
  query fetchAllPokemon {
    queryPokemon {
      id
      name
      captured
      imgUrl
      pokemonTypes
    }
  }
`


// Fetch all Pokemon - Function
function fetchAllPokemon() {
  return fetchGraphQL(fetchAllPokemonOperationsDoc, 'fetchAllPokemon', {})
}


// The rest of the following code is called in the main App component:


const { errors, data } = await fetchAllPokemon()


if (errors) {
  console.error(errors)
}


const result = data.queryPokemon.sort(
  (pokemonA, pokemonB) => pokemonA.id - pokemonB.id
)


// setPokedexData is a setter method from using the `useState` React hook, not shown in this gist
setPokedexData(result)

We then take that data and loop over it using the Array map helper function to display each Pokémon in the UI.

然后,我们使用数组map帮助器函数获取数据并在其上循环以在UI中显示每个神奇宝贝。

The filters at the top of the page are hooked up to our API as well. When the filter values change, a new API request kicks off, but this time with a narrower set of search results. For example, here are all the Fire type Pokémon that we’ve captured:

页面顶部的过滤器也已连接到我们的API。 过滤器值更改时,将启动新的API请求,但这一次的搜索结果范围更窄。 例如,以下是我们捕获的所有Fire类型的神奇宝贝:

Captured Fire type Pokémon
Captured Fire type Pokémon
射击火系神奇宝贝

The JavaScript for making an API request for Pokémon filtered by type and captured status looks a little like this:

用于按类型和捕获状态过滤的神奇宝贝API请求JavaScript看起来像这样:

const fetchPokemonOfCertainTypeAndByCapturedStatusOperationsDoc = ({
  pokemonType,
  isCaptured,
}) => `
  query fetchPokemonOfCertainTypeAndByCapturedStatus {
    queryPokemon(filter: { captured: ${isCaptured}, pokemonTypes: { eq: [${pokemonType}] } }) {
      id
      name
      captured
      imgUrl
      pokemonTypes
    }
  }
`


function fetchPokemonOfCertainTypeAndByCapturedStatus({
  pokemonType,
  isCaptured,
}) {
  return fetchGraphQL(
    fetchPokemonOfCertainTypeAndByCapturedStatusOperationsDoc({
      pokemonType,
      isCaptured,
    }),
    'fetchPokemonOfCertainTypeAndByCapturedStatus',
    {}
  )
}

在应用程序中更新数据(Updating Data in the App)

At this point we’ve sufficiently covered creating Pokémon from the API Explorer and fetching Pokémon within our Pokédex app via JavaScript. But what about updating Pokémon? Each Pokémon has a toggle switch that controls the Pokémon’s captured status. Clicking on the toggle updates the Pokémon’s captured status in the database and then updates the UI accordingly.

至此,我们已经充分介绍了如何通过API Explorer创建Pokémon,以及如何通过JavaScript在我们的Pokédex应用程序中获取Pokémon。 但是更新神奇宝贝呢? 每个神奇宝贝都有一个拨动开关,用于控制神奇宝贝的捕获状态。 单击切换按钮将更新数据库中神奇宝贝的捕获状态,然后相应地更新UI。

Here is our JavaScript to update a Pokémon:

这是我们JavaScript,用于更新神奇宝贝:

// Update the Pokemon Captured Status - GraphQL
const updatePokemonCapturedStatusOperationsDoc = (
  pokemonId,
  newIsCapturedValue
) => `
  mutation updatePokemonCapturedStatus {
    updatePokemon(input: {filter: {id: {eq: ${pokemonId}}}, set: {captured: ${newIsCapturedValue}}}) {
      pokemon {
        id
        name
        captured
        imgUrl
        pokemonTypes
      }
    }
  }
`


// Update the Pokemon Captured Status - Function
export function updatePokemonCapturedStatus(pokemonId, newIsCapturedValue) {
  return fetchGraphQL(
    updatePokemonCapturedStatusOperationsDoc(pokemonId, newIsCapturedValue),
    'updatePokemonCapturedStatus',
    {}
  )
}

We then call the updatePokemonCapturedStatus function when the toggle value changes. This kicks off the API request to update the value in the database. Then, we can either optimistically update the UI without waiting for a response from the backend, or we can wait for a response and merge the result for the single Pokémon into our frontend's larger dataset of all Pokémon. We could also simply request all the Pokémon again and replace our frontend's stored Pokémon info with the new result, which is what I chose to do.

然后,当切换值更改时,我们将调用updatePokemonCapturedStatus函数。 这将启动API请求以更新数据库中的值。 然后,我们可以乐观地更新UI而不用等待后端的响应,或者我们可以等待响应并将单个神奇宝贝的结果合并到我们前端的所有神奇宝贝的更大数据集中。 我们也可以简单地再次请求所有神奇宝贝,并用新结果替换前端存储的神奇宝贝信息,这是我选择要做的。

从数据库中删除数据 (Deleting Data from the Database)

The last of the CRUD operations is “delete”. We won’t allow users to delete Pokémon from within the app’s UI; however, as the app admin, we may need to delete any mistakes or unwanted data from our database. To do so, we can use the API Explorer again.

CRUD操作的最后一个是“删除”。 我们不允许用户从应用程序的用户界面内删除神奇宝贝; 但是,作为应用程序管理员,我们可能需要从数据库中删除任何错误或不需要的数据。 为此,我们可以再次使用API​​ Explorer。

For example, if we found that we have an extra Bulbasaur in our Pokédex, we could delete all the Bulbasaurs:

例如,如果我们发现Pokédex中有一个额外的Bulbasaurs,则可以删除所有Bulbasaurs:

mutation DeletePokemon {
  deletePokemon(filter: { name: { alloftext: "Bulbasaur" } }) {
    pokemon {
      name
    }
  }
}
Deleting all Bulbasaur Pokémon via the API Explorer
Deleting all Bulbasaur Pokémon via the API Explorer
通过API资源管理器删除所有BulbasaurPokémon

Then, we could add one Bulbasaur back:

然后,我们可以添加一个Bulbasaur:

mutation AddPokemon {
  addPokemon(input: [
    { id: 1, name: "Bulbasaur", captured: false, imgUrl: "http://img.pokemondb.net/artwork/bulbasaur.jpg", pokemonTypes: [Grass, Poison] }
  ]) {
    pokemon {
      id
      name
      captured
      imgUrl
      pokemonTypes
    }
  }
}

结语(Wrapping Up)

So, what did we learn? By now we should understand how to work with Slash GraphQL in the context of a React app. We’ve covered all the CRUD operations to make a pretty sweet Pokédex app. We may have even caught a few Pokémon along the way.

那么,我们学到了什么? 到目前为止,我们应该了解如何在React应用程序的上下文中使用Slash GraphQL。 我们已经介绍了所有CRUD操作,以制作出非常漂亮的Pokédex应用程序。 我们甚至可能在此过程中抓到了一些神奇宝贝。

Hopefully we didn’t… hurt ourselves in confusion… [cue audible groans from the readers].

希望我们不会……在混乱中伤害自己……[提示读者可听到吟声]。

We haven’t yet covered how to add authentication to secure our app or how to use the Apollo client when making our GraphQL requests, but those are important topics for another article!

我们还没有介绍如何在添加GraphQL请求时添加身份验证来保护我们的应用程序或如何使用Apollo客户端,但是这些是另一篇文章的重要主题!

As an experienced frontend developer but without much experience using GraphQL, working with Slash GraphQL was refreshingly easy. Getting set up was a breeze, and the API Explorer along with the documentation played a crucial role in helping me explore the various queries and mutations I could make with my data.

作为经验丰富的前端开发人员,但没有使用GraphQL的丰富经验,使用Slash GraphQL十分轻松。 设置非常容易,API Explorer和文档在帮助我探索可以对数据进行的各种查询和变异中起着至关重要的作用。

Slash GraphQL, I choose you! [more audible groans from the readers]

Slash GraphQL,我选择了你! [来自读者的更多吟声]

翻译自: https://levelup.gitconnected.com/how-to-build-a-pok%C3%A9dex-react-app-with-a-slash-graphql-backend-ccc8c650d29b

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值