1

If your project is a little scaled, then you must have experienced a kind of torture. A long time ago API returned a lot of necessary data, which can fully serve current needs. However, too much unnecessary data can cause performance problems on the backend. It takes up bandwidth at the front end. Later maintenance may cause thorny problems for the front and back ends. The reason why FB proposed the GraphQL standard is because too many products supported by FB itself have encountered such problems.

What is GraphQL

Before we officially start, let's introduce GraphQL a little bit. It is a standard set by Facebook. The standard is that only the final effect is defined. As for how to do it, everyone can play freely. In order to get rid of the chaos of various APIs mentioned above:

  • GraphQL unifies all request endpoints into one. In this way, you can add, delete, modify, and check without having to write another API. An endpoint is done.
  • There is also a set of descriptions of all types of data. This description starts with query (required) or mutation.

A simple example of an official website:

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
}

GraphQL still returns data through Http's GET and POST, but the length limitation of GET causes possible queries to go wrong. Therefore, POST can generally be used to obtain and modify data. This means that GraphQL can request the API in the same way as the client App. In terms of basic usage, it makes no difference whether there is the help of a third-party graphql client library.

With GraphQL, if the client says that there is any demand, it will get the necessary data for this demand, so there is no need to develop a new API.

Inquire:

query {
    todos {
      id
      title
    }
  }

This is a query. What you want to query is todos (it can be understood as a table for the time being), and what you want to query is the two fields id and title This query will only return data corresponding to the two fields id and title

It can also be a query with conditions:

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

GraphiQL

Add, modify

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

return

{
  "data": {
    "createReview": {
      "stars": 5,
      "commentary": "This is a great movie!"
    }
  }
}

That's it for the basic introduction. Reference may be official document .

支持的语言

Why not Relay

Have you ever experienced the official library full-time responsible for persuading you to leave? GraphQL is a standard proposed by Facebook. Note that this is a standard rather than an implementation. I don’t know much about the situation of the server, but it’s called Meta on the client FB or now. An implementation is given and it has been developed for many years. This library is called Relay .

With its powerful functions and Meta (FB) endorsement, it quickly developed. However, this tool obviously lacks stamina. Judging from the current development of the TypeScript project, it obviously lacks support for TypeScript. For example, one of its supporting babel plugins does not support TypeScript. Of course it is also a small problem. Just add a index.d.ts file and add the type to your project.

Then there is its model. After you follow the step by step of the official document, you still can't do development. Because when you add another file and query, you will find that the required query will not be automatically generated. Either silently, no errors were reported and no corresponding files were generated, or some inexplicable errors were reported. Because you need to name the query (or any operation) based on your file name. That is to say, its mode can be considered as intrusive, although it will write less fixed code than other methods, although it is not necessarily. The author's level is limited, so I had to abandon it first.

How about URQL

First of all, urql has a 6.5K star on github. And the design is active enough. Finally, it was backed by another company. It can not be said that it is not a KPI project, but KPI projects also have a benefit, at least for the KPI people who are maintaining the code.

In addition, this library was developed with TypeScript. In other words, it must be TypeScript friendly. If you use TypeScript in your project, you don't have to worry about obsolescence and incompleteness.

image.png

It's not that other libraries are inappropriate. More libraries to choose from are listed GraphQL official website

Use the existing GraphQL service: Github

Github provided the GraphQL API a long time ago. We call the GraphQL API of github in the APP. Used to query the public code base under a certain owner (such as facebook).

To use github's GraphQL service, token is needed, so an interface for entering token is needed. After the user enters the token, the token can be stored persistently. There needs to be an interface to delete this token, so that users have the opportunity to enter a new token. Especially after the user modifies the permissions, there must be a place to update the token.

navigation

simulator_screenshot_B30AF36D-0A2F-4257-AB60-2541F100FD62.png

simulator_screenshot_F318440D-1F2F-49BB-9DCC-51FF7FF2A215.png

After the user enters the APP, after clicking the Repo option, if such a token does not exist, it will enter the token page. The following functions can only be continued after the user enters the token.

After entering the token on the Token page, the user jumps to the list page. You can delete the Token on the Settings page, and then automatically jump to the Token page.

After the user successfully enters the token, enter the repo list page to see the repo list. Now only the public repo under facebook is displayed. After adding the search bar to enter owner, you can control which repo you want to search.

URQL basic configuration

The configuration of urql is divided into two parts. The first is the configuration of the provider. Using the provider allows all the places that call the graphql api to easily get the requested endpoint and the required auth token. Of course, it is not the reading of plaintext but the query can be called directly.

Configure Provider

You can see in App.tsx

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

The role of this provider is the same as the provider of react-redux. Here urql provider provides a client.

Exchange

Exchange is a middleware mechanism of urql. This mechanism is also similar to Redux's middleware mechanism.

authExchange provided by the official website, and add the logic of acquiring and using tokens.

First, you need to install authExchange :

yarn add @urql/exchange-auth

Then in the path: src/data/graphqlClient.ts you can see the code to fill in the blanks authExchange What needs to be added here is the content of error handling besides obtaining the token mentioned above and using the token. For the sake of simplicity, the error handling part is ignored. Students in need can study the official website examples.

The method to obtain token is getAuth . Our token is generated in the Github configuration, and then the user is completely added and stored in the APP. Therefore, no additional API calls are required to obtain tokens.

  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;
    }
  },

In the step of adding stars, you can see that the token is returned as an attribute of the authState

The use of tokens is achieved through the method addAuthToOperation . Here will finally return to a newly created operation. The token getAuth is stored inside:

  addAuthToOperation: ({ authState, operation }) => {
    // ...略
    return makeOperation(operation.kind, operation, {
      ...operation.context,
      fetchOptions: {
        ...fetchOptions,
        headers: {
          ...fetchOptions.headers,
          Authorization: `Bearer ${authState.token}`,  // *
        },
      },
    });
  },

In the step of adding *, the token is used, and the token authState and placed in the authentication of the header and sent to the backend along with the api request.

Fill the urql client

After the token is configured through Exchange, the critical step is completed. Next, you need to add the exchange of the configuration number and the endpoint of graphql to the client for graphql query.

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

  return client;
};

export { getGraphqlClient }; // 3
  1. Add the endpoint of github's graphql in the url attribute: https://api.github.com/graphql .
  2. Add the auth exchange to the exchange array. When configuring here, you need to pay attention to the synchronous operation of the exchange to be placed in front of the asynchronous operation of the exchange. Therefore, authExchange should be placed in third place.

Implement a query

After completing the above configuration, we can start to implement a simple query.

You can see the specific query and the effect after execution in the src/GithubScreen.tsx

First, let's prepare our query statement.

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
          }
        }
      }
    }
  }
`;
  1. Introduce urql to the tool methods gql and useQuery. useQuery will be used later
  2. Write the query statement.

This query seems to be overwhelming for beginners. In the above example, we only mentioned a few keywords such as query and meta. So how can such a long (not long) query statement be written? Github specifically provides an explorer for graphql api. Click here to explorer. In fact, there is such an explorer in the implementation of GraphQL in many languages, at least in the development stage, you can enjoy this service.

image.png

  • First log in to your github account on this page.
  • You can test a variety of query statements in GraphiQL.
  • If you have an unclear schema, you can see the Doc document on the far right
  • The Query Variable in the lower left corner can enter the variable to be queried. Here is the 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 seeing the query results in the middle, you can judge whether your query statement is appropriate. In this case, it is the query statement we need, which is directly copied to our code for use.

Or, if you have a little understanding of the schema definition, for example, what we want to query this time is the repository. You can also use the smart prompt in the query statement editor. Overall, it is very convenient to write queries and modify statements.

A simple query

The above mentioned how to write a query statement, now let's use this statement to query the repo list.

Urql also provides such a hook for React to use.

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

  // ...略...

}

Very simple A simple query can be done.

  1. Just ask useQuery play.
  2. Use useQuery. This hook will also return a method to re-execute the query, which is mainly used when refreshing.
  3. The network request has three states, data is data, fetching means that the request is in progress, and error means that there is an error in the query.

The following code can be processed differently according to what appears.

Finally, it is shown in FlatList that user is all public repo of facebook.

image.png

A query with parameters

What if I give the user a place to input user and query all public repo of a specific user? As shown in the figure:

image.png

If the user enters github, then we search all public repo on github, and search the specified and user's repo list for what is entered. The query operation is triggered by clicking the query button. The user clicks the query button, and then executes a query.

At this time, userQuery cannot be used. It returns the other of the two elements of the tuple that can perform refresh operations, but cannot modify the query statement.

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

useQuery returned by reexecuteQuery can only be used to execute the given query again. What can be modified is some caching strategies and the like, but not including query statements. Therefore, we can only find another solution.

That is the client we used when configuring urql. It is also stored in the context of the provider. So you can get it through the hook useContext The urql official also provides us with a convenient tool for obtaining client objects: useClient . useClient getting the client from 0618dfd917de76, you can call its query method to execute the query. This is much more flexible.

We can start the query after the user enters the user string and clicks the query button. Then display the results in the 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 });
    }
  };
  1. Record the user string entered by the user in the query box
  2. Obtain the client object through useClient
  3. Process the results in the query. Fetching, data and error are basically the same as the results obtained by useQuery
  4. Handler for text input in the search box
  5. Search box, the handler for the click of the search button
  6. Use the client object to execute the query statement.

A query with parameters is complete. The calls to the modified and newly created GraphQL APIs are basically the same.

A little reflection (all nonsense, can be ignored)

At present, what we are doing with urql is basically just query and, which is a little more complicated auth operation. However, the author actually encountered a little pitfall when using authExchange. If you don't copy all the exchanges in the example directly, you really won't be able to get this app up and running.

There is also a problem when configuring the client at the beginning. The Provider of urql should be closest to the root component, which is <App /> . Of course, these are not finalized, but the app will run when the above conditions are met, which is enough to explain the problem.

GraphQL can be implemented fetch If you use fetch and redux or just use the above hooks directly to deal with the existing function speed reading will be faster. The learning cost is local, and all the code can be used comfortably when written as a util method. The key is that there is no learning cost, libraries such as fetch or Axios are used every day.

Fortunately, urql also has a useClient make the problem easier. Of course, check the documentation. However, the caching problem that needs to be dealt with later is more complicated. Do we make our own wheels to implement a cache management tool? The complexity is much more complicated than scanning the document. Therefore, when choosing a library, we must still consider learning costs, development and maintenance costs, community maturity, combined with the time pressure of going online.

finally

A simple query has been completed in this app. But obviously there is still some work to be done. For example, loading and error handling are relatively simple. We mentioned redux-toolkit in the previous series. Is it possible to have a slice to combine the processing of these logics with redux.

We have also added the react-native-paper component library in the dependencies. This library can be used on both native and web. I haven't uploaded the screenshot of the web break, mainly because it's a bit miserable and can't bear to bet. The UI can also be slightly beautified at the back.

The main job is how to use graphql in actual development. Its potential is not just that it looks novel and simple, but it can actually solve problems. One of the biggest resistance that front-end students will encounter is whether or not the back-end students accept this not-so-new thing.

There is a 30-minute graphql video on youtube, click here to see The actual back-end integration of graphql is certainly not as easy as it is in the video screen, and it only demonstrates the processing of query operations. But it will not be as difficult as imagined. GraphQL is a standard, and its implementation is also the conversion from external query statements to internal data acquisition, that is, the resolver in the video, and the schema definition. It still relies on the underlying "DAO" layer, or the http request of the rest api. After the implementation of GraphQL, the benefits are very obvious, and the demand for back-end modifications on the data consumer side (various apps) will be greatly reduced. Fiddle with the query statement, why bother adding an API to the backend?


小红星闪啊闪
914 声望1.9k 粉丝

时不我待