It has been nearly a year since Facebook released a new version of Relay (Relay Modern), but there are still not many Relevant Chinese materials and practical cases. The reason may have something to do with the lack of detail in official documents. By analyzing GraphQL and Relay, this paper hopes to reduce the difficulty of getting started and make it easier to judge whether the Relay framework is suitable for our own business. What? Go straight to the code? Monkey ~ can clone Relay application template on Github, which integrates front and back end and routing, basically meeting the needs of common apps.
GraphQL
GraphQL is an independent data query system. Its introduction and use have been introduced in detail on the official website, and now there is a Chinese version for reference. For basic concepts, you are advised to read the official website.
Design idea
Graph is included in the name because GraphQL uses a graph-structured query. Take an example: We want to design a company’s internal people management system. Assume a simple scenario that contains at least two pieces of information: department and employee. A graphical representation of their relationship might look like this:
Figure database
The introduction of secondary
{employee (ID:"022") {name position Department {name} Colleague {ID Name position}}}Copy the code
Normally, the server returns the following result:
{Employee {Name:"L a"The position:"Employees", Department: {Name:"Front end"} colleague: [{ID:"022"The name:"A" SThe position:"Manager"
},
{
ID: "033"The name:"A Y"The position:"Director"}}}]Copy the code
As you can see, the employee information is all returned in Json format according to the query request. Corresponding to the figure, the blue part of the information is actually extracted:
The data of the graph structure is extracted into a tree structure
What if I want to query information about two nodes at the same time, but there are no edges between them?
{employee (ID:"022"{name, position... } Department (name:"Design Department") {name}}Copy the code
Return result:
{Employee {Name:"L a"The position:"Employees". } Department {name:"Design Department"}}Copy the code
In the actual application development, the above design structure is usually adopted. At this point, let’s summarize. GraphQL extracts graph structure data into tree structure through query statement and returns the result. The graph-structured data here corresponds to GraphQL’s type system. How to relate the nodes in the graph, namely the types defined, to each other, requires concrete logical code to achieve. Official and community GraphQL libraries are also available in various languages. So the question is, what if I want to change the data? To solve this problem, GraphQL introduced the concept of Mutation. Mutation can be thought of as a special query for which you need to define a name, parameters, return data, and perform its specific data operations in specific code logic. Please refer to the official documentation for details.
advantage
The official website gives a brief overview of GraphQL’s advantages, but you may be wondering what GraphQL has over other API design patterns. According to my understanding from practice, GraphQL mainly solves some contradictions frequently encountered in front-end API development and later maintenance. Let’s take a closer look at RESTful apis.
flexibility
Looking at the example from the previous section, scenarios such as obtaining all information about an employee are fairly common in application development. What about a RESTful API? I think there are two ways to do this:
- 1. Separate API to pull all information;
- 2. Use “colleague” and “department” information as independent apis and combine them through multiple apis in the front end.
So let’s do the first one. This approach has several obvious drawbacks: the first is redundancy of information. If I only need to display basic employee information elsewhere, without specific department or colleague information, there is data redundancy. Second, from a back-end perspective, there are maintenance costs. Because the presentation requirements of the front end are relatively variable, there are likely to be many apis that are no longer used and that are not easily removed. Or, in the case of new requirements, apis that duplicate existing apis are likely to be added, resulting in redundancy of back-end business code. Furthermore, even if the option of making the return value optional is considered as an argument, it does increase some flexibility, but it inevitably increases the complexity of the argument. Let’s do the second one. This fine-grained module-partitioning approach reduces the maintenance cost of back-end code compared to the first approach, but is extremely front-end unfriendly. For example, the back end only provides an independent query method for a certain field in a list. When the list data volume is very large, the number of requests increases greatly, directly affecting front-end performance and user experience. In addition, the large number of asynchronous requests undoubtedly increases the front-end code complexity and thus front-end maintenance costs. GraphQL only needs to define the type and the corresponding data processing mode, and expose it to the query root node. The front end can request at will, which solves the above contradiction well.
Front-end friendliness
The use of GraphQL as BFF (Backend For Frontend) has its inherent advantages, the most important of which is its front-end friendly design. Mutation also reduced the number of requests, in addition to improving the front-end experience by reducing the number of query requests mentioned above. In Restful apis, the front-end usually needs to issue another GET request to pull the changed data after all other requests are completed. If a portion of data is added to the result for the front-end display interface, the reusability of the back-end code is broken. In the GraphQL Mutation, the returned data could be determined according to the data requirements of the front-end interface, and only needed one request. Combined with Relay framework, ideal updates can be defined to reduce the frequency of Loading interface and further improve user experience. (More on this in the next section)
Reduce communication costs
In the current popular development mode of front and back end separation, the communication cost of front and back end developers is also a major factor affecting the project schedule. Often, to reduce communication costs, back-end developers need to define API documentation in advance from which the front end mocks data to develop the front end interface. Backend developers also need to test with various tools, such as PostMan. After the completion of the development of the front and back end, we also need to do joint adjustment and docking. For GraphQL, the Schema itself is a good document. GraphiQL, a postman-like tool, is also available for debugging.
other
In combination with the Relay framework, GraphQL provides additional benefits, such as consistent type validation and front-end caching, as described in the next section.
Relay
Relay is a framework based on GraphQL and React that combines the two components to further encapsulate requests into components on top of the original React component. The official provides a TodoMVC demo for reference, which basically covers CRUD operations.
QueryRenderer
The Relay framework provides a high-level component (HOC) called the QueryRenderer to encapsulate the React component and the GraphQL request. This component accepts four Props: Environment, Query, Variables, and Render. Environment needs to be configured with network request and Store. Query accepts the GraphQL request; The variables accept the variables required in the GraphQL request, and the final render is used to define the rendering of the component. Suppose we want to develop a Relay component that displays basic employee information. It might look something like this:
<QueryRenderer
environment={environment}
query={graphql`
query StaffQuery($id: ID!) {employee (ID:$id) {ID name position}} '} variables={{ID:'011' }}
render={({error, props}) => {
if (error) {
return <div>{error.message}</div>;
} else if (props) {
return<div> workid: {props["Employees"] ["ID"]}; Name: {props ["Employees"] ["Name"]}; Position: {props ["Employees"] ["Position"]}; </div>; }return<div>Loading... </div>; }} / >Copy the code
Fragment
Now that we have a component that shows basic employee information, what if we wanted to further encapsulate a component that lists employees? In the same way as the React component, you can create a new component that receives props containing an array of employee ids, and inside this new component, a Relay component with multiple employee information based on the ID array Map. This seems to work, but the problem is that if you have 10 ids, then such a component would also make 10 GraphQL requests, which obviously goes against the design philosophy of GraphQL. You can also create a new Relay component: query directly requests a set of employee data and renders the list. But the reuse of the component is lost, because it is clear that the logic and style of displaying each piece of employee information in the new component is the same as that of the individual employee information component. Here, Relay provides a HOC component of a Fragment that accepts two Props: Component and fragmentSpec. Component accepts the React component, which handles specific component views and logic. FragmentSpec accepts a GraphQL Fragment. The Fragment, which corresponds to the figure in the previous section, is a part of a node. Such as:
Fragment Employee information on employee {ID Name position}Copy the code
In a request, you can introduce a Fragment like this:
{employee (ID:"022") {... Employee information}}Copy the code
Back in Relay, you can create a Fragment component for employee information like this:
CreateFragmentContainer (class employee extends React.Component {render() {
return<div> workid: {this.props. Data ["Employees"] ["ID"]}; Name: {this. Props. Data ["Employees"] ["Name"]}; Position: {this. Props. Data ["Employees"] ["Position"]}; </div>; }}, graphql 'fragment on {ID}',)Copy the code
With such an employee information Fragment component, we can create an employee information list component:
<QueryRenderer
environment={environment}
query={graphql`
query StaffListQuery($ids: [ID]!) {employee (IDs:$ids) {... Employee information}} '} variables={{id: ['011'.'022'.'033'] }}
render={({error, props}) => {
if (error) {
return <div>{error.message}</div>;
} else if (props) {
return< employee information data={this.props. Data} />; }return<div>Loading... </div>; }} / >Copy the code
In this way, the actual request of the component is still only one, but the component for employee information is successfully reused. If you need to display employee information in other components, you just need to introduce the Fragment component as well. In addition to the basic Fragment Container, Relay also provides Refetch Container and Pagination Container components. The former uses the Refetch method on top of the original Fragment component. In order to meet the scenario where the component needs to update data (for example, the user actively clicks the refresh button of the data list); The latter, however, adds a number of paging operations, which I won’t elaborate on here.
Relay Store
The environment configured in QueryRenderer mainly contains network requests and stores. The Store here is not quite the same as Redux’s Store. Whereas Redux is used to centrally manage component states, the Relay Store records records. The Record here is essentially each Type of GraphQL, or each node corresponding to the graph in the previous section. When the Relay framework receives the data returned by GraphQL, it will Record an ID for each node data and save it as a Record in the Relay Store. Relay also provides CRUD methods for these records. Please refer to the official documentation for details.
Mutations
Relay provides the commitMutation method to facilitate the launching of GraphQL Mutation operations in the component. In addition to Mutation initiation, Relay Store can be used to locate and update page data conveniently, realize ideal update, and further improve user experience.
advantage
At this point, you have basically covered all the functions of Relay. As can be seen from the above, on the basis of GraphQL’s original advantages, Relay also brings the following two advantages:
- 1. Realize the combination of data query and components, further improve the degree of front-end modularization, improve the reuse of components.
- 2. Excellent client cache to improve user experience.
In addition, combined with the type detection of Flow framework, Relay can do type verification well according to the schema provided by the back end, avoiding some potential bugs.
conclusion
From the above introduction and analysis, I believe you have a general understanding of GraphQL and Relay. In my opinion, Relay is more suitable for applications with a large variety of front-end data display categories, such as social networking sites. But the specific application in the project, or need to be combined with the actual needs to decide.
The resources
GraphQL Concepts Visualized GraphQL and Relay analysis