If your project is of any size, you must have suffered. A long-ago API returns a huge amount of data that is fully serviced today. But obviously unnecessary data overload can cause back-end performance problems. It’s taking up bandwidth on the front end. Late maintenance can be tricky for both the front and back ends. The reason why FB proposed the standard of GraphQL is that FB itself supports too many products that meet such problems.

What’s GraphQL

Before we get started, a little bit about GraphQL. It’s a standard set by Facebook. Standards define only the end result and leave it up to the individual to do so. To get out of the mess of various apis mentioned above:

  • GraphQL consolidates all request endpoints into one. This way you can add, delete, change and check without having to write another API. One endpoint.
  • There is also a set of type descriptions for all data. This description starts with **query (must) ** or mutation.

GraphQL still returns data via Http GET and POST, but the length limit of the GET causes problems with possible queries. So you can generally use POST to retrieve and modify data. This means that GraphQL can request the API in exactly the same way as it normally does in a client App. In terms of basic usage, it doesn’t really matter whether there’s a third-party graphQL Client library or not. Such as:

input MessageInput {
  content: String
  author: String
}

type Message {
  id: ID!
  content: String
  author: String} type Query { getMessage(id: ID!) : Message } type Mutation { createMessage(input: MessageInput): Message updateMessage(id: ID! , input: MessageInput): Message }Copy the code

If the client side says it has a requirement and needs to get the necessary data for that requirement, then it basically needs to develop a new API. GraphQL is a huge API that returns exactly what you want. You can query the specified data, can also modify the backend data. A Query is a Query, and a modification is called Mutation.

Query:

query {
    todos {
      id
      title
    }
  }
Copy the code

This is a query. You want to query toDOS (you can temporarily think of it as a table), and you want to query the ID and title fields. This query returns only the data corresponding to the ID and title fields.

It can also be a conditional query:

query ($from: Int! , $limit: Int!) { todos (from: $from, limit: $limit) {
      id
      title
    }
  }
Copy the code

Add and modify

mutation CreateReviewForEpisode($ep: Episode! , $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}
Copy the code

return

{
  "data": {
    "createReview": {
      "stars": 5."commentary": "This is a great movie!"}}}Copy the code

So much for the basic introduction. Please refer to the official documentation for details.

Why not Relay

The official library is responsible for persuading people to leave. GraphQL is a standard from Facebook. Note that this is a standard, not an implementation. The server side of the situation is not familiar enough to say, but in the client side FB or Meta now, given an implementation and has been developed for many years. This library is called Relay.

It quickly took off with powerful features and Meta (then FB) endorsements. But the tool is clearly running out of steam. With the development of TypeScript projects today, there is a clear lack of support for TypeScript. One of its companion Babel plug-ins, for example, does not have TypeScript support. Of course, it is also a small problem, just add an index.d.ts file in your own project and add the type.

And then there’s the pattern. You can’t do development until you follow the official documentation Step by Step. Because when you add another file and query, you will find that the required query is not automatically generated. Either it’s quietly not reporting an error or generating a corresponding file, or it’s reporting something that doesn’t make any sense. Because you need to name the query (or any operation) according to your file name. That is, its pattern can be considered intrusive, although it will write less fixed code than the other way around, although it may not be. The writer’s level is limited, had to abandon first.

URQL?

First, URQL has a 6.5K star on Github. And the design is active enough. It was finally backed by another company. I can’t say it’s not a KPI project, but KPI projects have the advantage that at least there are people maintaining the code for KPIs.

In addition, the library is developed in TypeScript. That said, it’s definitely typescript-friendly, and your projects that use TypeScript don’t have to worry about outdated, incomplete types, etc.

It’s not that other libraries aren’t suitable, there are more libraries to choose from listed on the GraphQL website.

Use the off-the-shelf GraphQL service: Github

Github has provided GraphQL APIS for a long time. We’ll call github’s GraphQL API in our APP. To query a public codebase under an owner, such as Facebook.

To use Github’s GraphQL service requires tokens, so you need an interface for entering tokens. The token can be persisted after the user enters it. There also needs to be an interface to remove the token and give the user the opportunity to enter a new token. Especially after the user changes the permission, there must be a place to update the token.

navigation

After the user enters the APP, after clicking the Repo option, If no such Token exists, the Token page is displayed. After the user enters the token, the following functions can be continued.

The user enters the Token on the Token page and goes to the list page. On the Settings page, you can delete the Token and automatically jump to the Token page.

After the user successfully enters the token, the REPO list page is displayed. Now only the public repO below Facebook is shown. You can control which REPOS to search for by adding the search bar and typing owner.

Basic URQL configuration

The URQL configuration consists of two parts. The first is the configuration of the provider. Using providers makes it easy for any place that calls the GraphQL API to get the request endpoint and the required Auth token. Of course, instead of reading the plaintext, you can call the query directly.

Configure the Provider

You can see it in app.tsx:

    <Provider value={client}>
      <NavigationContainer>
        <Stack.Navigator initialRouteName="Token">
          <Stack.Screen
            name="Tabs"
            component={Tabs}
            options={{ headerShown: false}} / >
        </Stack.Navigator>
      </NavigationContainer>
    </Provider>
Copy the code

This Provider works the same as the React-Redux Provider. The URQL provider provides a client.

Exchange

Exchange is a middleware mechanism for URQL. This mechanism is also similar to Redux’s middleware mechanism.

Here we need to fill in the blanks of authExchange provided by the official website and add the logic of obtaining and using tokens.

First you need to install authExchange:

yarn add @urql/exchange-auth
Copy the code

Then the path: SRC/data/graphqlClient ts can see to fill in the blanks authExchange code. What needs to be added here is the error handling in addition to obtaining the token and using the token. The error handling part is ignored for simplicity. Students who need to study the official website example.

The method of obtaining the token is getAuth. Our tokens are generated in Github configuration, and then added by users and stored in the APP. So no additional API calls are required to get the token.

  getAuth: async ({ authState }): Promise<AuthState | null> = > {try {
      if(! authState) {const token = await AsyncStorage.getItem(GITHUB_TOKEN_KEY);
        return { token } as AuthState; / / *
      }
      return null;
    } catch (e) {
      return null; }},Copy the code

As you can see in the star step, the token is returned as an attribute of the authState object.

The use of tokens is achieved through the method addAuthToOperation. At the end, a new operation is returned. It contains the tokens obtained from getAuth:

  addAuthToOperation: ({ authState, operation }) = > {
	/ /... slightly
    returnmakeOperation(operation.kind, operation, { ... operation.context,fetchOptions: {
        ...fetchOptions,
        headers: {
          ...fetchOptions.headers,
          Authorization: `Bearer ${authState.token}`./ / *,}}}); },Copy the code

The token is used in the add * step, and the token is read from the authState into the header authentication and sent to the back end with the API request.

Fill the URQL client

Once the token has been configured through Exchange, the key step is complete. The next step is to add the exchange configuration number and graphQL endpoint to the client for graphQL queries.

const getGraphqlClient = () = > {
  const client = createClient({
    url: 'https://api.github.com/graphql'./ / 1
    exchanges: [
      dedupExchange,
      cacheExchange,
      authExchange({	/ / 2. authConfig, }), fetchExchange, ], });return client;
};

export { getGraphqlClient }; / / 3
Copy the code
  1. Add github’s GraphQL endpoint to the URL property:https://api.github.com/graphql.
  2. Add auth Exchange to the exchange array. Note that the exchange for synchronous operations should be placed before the Exchange for asynchronous operations. So,authExchangePut it in third place.

Implementing a query

With the above configuration in place, we are ready to implement a simple query.

In the SRC/githubscreen.tsx file you can see the details of the query and the results of the execution.

Let’s start by preparing our query.

import { gql, useQuery } from 'urql'; / / 1

const REPO_QUERY = gql` // 2 query searchRepos($query: String!) { search(query: $query, type: REPOSITORY, first: 50) { repositoryCount pageInfo { endCursor startCursor } edges { node { ... on Repository { name } } } } } `;

Copy the code
  1. Introduce URQL into the utility methods GQL and useQuery. UseQuery will be used later
  2. Write a query statement.

This query statement can seem overwhelming to beginners. In the examples above we only mentioned query, metation and a few other keywords. So how can such a long (and not long) query be written? Github specifically provides a GraphQL API explorer. Click here to go to Explorer. In fact, there are many languages that implement GraphQL with such an Explorer, at least in the development phase.

  • Start by logging into your Github account on this page.
  • You can test a variety of query statements in GraphiQL.
  • If you have a schema that is unclear look at the Doc document on the far right
  • The Query Variable in the lower left corner can be used to enter the variables of the Query. Here is query, which corresponds to the owner of the repo and the license type of the repO.
  • The middle column is the result of the query.

After you see the results in the middle, you can determine whether your query is appropriate. In this case, that’s the query we need, copied directly into our code.

Or, if you’re familiar with the definition of schema, for example, we’re looking for repository. You can also use the smart hints in the query editor. Overall, it is very convenient to write queries and modify statements.

A simple query

Let’s use this query statement to query the REPO list.

Urql also provides such a hook for React.

import { gql, useQuery } from 'urql';  / / 1

const REPO_QUERY = gql `query searchRepos(...) { ... }`;

const GithubScreen = () = > {
  const [result] = useQuery({  / / 2
    query: REPO_QUERY,
    variables: { query: 'user:facebook'}});const { data, fetching, error } = result; / / 3

  / /... A little...

}
Copy the code

It’s very simple a simple query will do.

  1. Only need to pleaseuseQueryAppearance.
  2. Use useQuery. The hook also returns a method to re-execute the query, mainly for use when refreshing.
  3. There are three states of network request where data means data and error means an error occurred on a query.

The following code can do different things depending on what happens.

Finally, the FlatList shows that user is all of Facebook’s public repo.

A query with parameters

If we give the user a place to type user, what do we need to do to query all public repOs for a particular user? As shown in figure:

If the user types in Github, we search for all public repOs on Github, and search for the list of specified and user repOs. The query action is triggered by clicking the query button. The user clicks the query button, then executes a query.

UserQuery is no longer available at this point. It returns the other of the two elements of the tuple that can be refreshed but cannot modify the query statement.

const[result, reexecuteQuery] = useQuery({... });Copy the code

The reexecuteQuery returned by useQuery can only execute the given query again. You can modify some cache policies and things like that, but not the query statements. So, we have to find another solution.

That is the client we used earlier when configuring URQL. It is also stored in the Provider context. So you can get it through useContext hook. The urQL official also provides us with a convenient access to client objects: useClient. Once you get the client from useClient, you can call its Query method to execute the query. That’s a lot more flexible.

We can start the query after the user enters the user string and clicks the query button. Then display the results in a list.

const [searchText, setSearchText] = useState(' ');  / / 1
  const client = useClient();  / / 2
  const [result, setResult] = useState({ / / 3
    fetching: false.data: null.error: null});const { data, fetching, error } = result;

  const handleSearchChange = (text: string) = > setSearchText(text); / / 4
  const handleIconPress = async() = > {/ / 5
    setResult({ fetching: true.data: null.error: null });
    try {
      const res = await client
        .query(REPO_QUERY, { query: `user:${searchText}` }) / / 6
        .toPromise();
      setResult({
        fetching: false.data: res.data? .search? .edges ?? [].error: res.error,
      });
    } catch(e) { setResult({ ... result,fetching: false.data: null.error: e }); }};Copy the code
  1. Records the user character string entered by the user in the query box
  2. throughuseClientHook Obtains the client object
  3. Process the results in the query. Fetching, data and error and aboveuseQueryThe results are basically the same.
  4. Handler for search box text input
  5. Search box, search button click handler
  6. useclientObject to execute a query statement.

A query with parameters is complete. The calls to the modified and newly created GraphQL API are pretty much the same.

A little reflection (all nonsense, can be ignored)

So far, all we do with URQL is basically query and slightly more complicated Auth operations. However, the author in the use of authExchange when actually encountered a little pit. You wouldn’t be able to run this app without copying all of the Exchanges in the example.

There is also a problem with the initial configuration of the client. The URQL Provider is closest to the root component, which is
. Of course, these have not been finalized, but the app will run when the above conditions are met, which is enough to illustrate the problem.

GraphQL can be implemented directly with fetch. It would be faster if you used fetch and redux or, to be fair, directly used the hooks above to handle existing functions. Learning cost, all code written in util method can also be used comfortably. Key, there is no learning cost, fetch or Axios libraries are used every day.

Fortunately, URQL also has a useClient to make things easier. Check the documentation, of course. However, the caching problem that needs to be addressed later is a little more complicated. We build our own wheels to implement a cache management tool? That’s a lot more complicated than scrolling through documents. Therefore, when we choose the library, we must take into account the learning cost, development and maintenance cost, community maturity, combined with the time pressure of going online, etc.

The last

A simple query is already done in this app. But there is clearly some work to be done. For example, loading and error handling are fairly crude. We mentioned the Redux-Toolkit in the previous series. Can there be a slice that allows these logical processing to be combined with Redux?

We have also added the React-native Paper component library to our dependencies. The library works both native and on the Web. I haven’t posted a screenshot of the web crash yet, mainly because I’m too sad to bet on it. The UI can also be embellished in the back.

Most of the work is on how to use GraphQL in actual development. It has the potential not just to look new, but to actually solve problems. One of the biggest obstacles facing the front end students is whether or not the back end students accept this not-too-new thing.

There’s a 30-minute video of graphQL on YouTube that you can watch here. The actual backend integration of GraphQL is certainly not as easy as it is in the video, and it itself only demonstrates query processing. But it’s not as hard as it looks. GraphQL is a standard that transforms data from an external query statement to an internal one, as defined by resolver and schema in the video. It still relies on the underlying “DAO” layer, or HTTP requests from rest apis. After GraphQL is implemented, the benefits are very obvious, and the need for back-end modifications on the data consumer side (various apps) will be greatly reduced. Why add apis to the back end when you can just fiddle with query statements?