GRAPHQL Service Development Guide _GRAPHQL

Source: Internet
Author: User
Tags data structures getmessage throwable tojson

https://www.v2ex.com/t/355632


Recent projects on the use of GRAPHQL, summed up some experience to share with you. You are welcome to make comments:

The article I also sent in the Jane book @@ 戳 here GRAPHQL Service Development Guide

July 2015, Facebook issued GRAPHQL cloth and open source GRAPHQL, GRAPHQL as responsible for the front-end interaction protocol, a good solution to the single back-end service in the face of multiple front end (Android, IOS, Mobile web, PC W EB) scenario, can provide different data for the same scenario to meet the needs of client application display.

GRAPHQL is a JSON-like language that has its own set of syntax for requesting data specified by the client or for an incremental deletion, while the server side encapsulates the data according to the client's request and returns it to the front end in JSON format. GRAPHQL's syntax can refer to Http://graphql.org/learn.

We assume that there is now an electrical business service that requires both IOS, Android, and PC Web three clients, which support online trading of multiple categories of merchandise. Users of this electrical business can browse the list of items according to different categories on any client, view the details of the goods, select the goods to put them in the shopping cart and place orders and purchase them. After the purchase, the goods are sent by courier to the address of the user when the order is completed.

We will implement the GRAPHQL based BFF service through the framework Graphql-java to meet the data requests of three kinds of clients. To abstract out a reasonable data structure

The

GRAPHQL requires the server-side to predetermine a series of data structures, while the client displays the required fields for the requirements-selective query based on the defined data structure. Therefore, when using GRAPHQL, the first step is to abstract a reasonable data structure according to the business scenario. Here we can use the Domain-driven design method to model the data for the user scenario, and select the data that the user needs to know from the data to hide the data that the user should not be aware of. We can get the following data structure:

 class Category {private String ID;
	private String name;
Private list<product> Product;
	Class Product {private String ID;
	private String name;
	Private String description;
	private String thumbnail;
Private List<sku> SKUs;
	Class Sku {private String ID;
	Private list<optionpair> options;
	Private Integer stock;
Private BigDecimal Price;
	Class Optionpair {private String key;
private String value;
	Class Order {private String ID;
	Private String UserName;
	Private String Usermobile;
	Private String address;
	Private Orderstatus status;
	Private BigDecimal Price;
	Private list<orderline> OrderLines;
	Private Date Createtime;
	Private Date Purchasetime;
Private Date Finishtime;
	Class Orderline {private String skuid;
	private String name;
	Private Integer quantity;
Private BigDecimal Price; }

The data structure contains the ID of the entity can be used as aggregate Root. The client can query the data by graphql the portal from the entity listed above. Using Graphql-java to implement the Scalartype in the basic type Graphql-java of the service side

In Graphql-java, in addition to the most basic types described in the GRAPHQL document Graphqlint, Graphqlfloat, graphqlstring, Graphqlboolean, Graphqlid, It also includes Graphqllong, Graphqlbiginteger, Graphqlbigdecimal, Graphqlbyte, Graphqlshort and Graphqlchar for easy developer use.

It should be noted that when Graphqlid is selected for field, only string and integer values are accepted and converted to string delivery, whereas the ID of the database default definition is long, which can be an error if you use Graphqlid.

In Graphql-java, you can also customize scalartype such as defining graphqldate and serialized it deserialized as timestamp:

public static final Graphqlscalartype graphqldate = new Graphqlscalartype ("date", "Built-in date as timestamp", New Coerci Ng () {
	@Override public
	Long serialize (Object input) {
		if (input instanceof date) {return
			((date) input) . GetTime ();
		return null;
	}

	@Override public
	Date parsevalue (Object input) {
		if (input instanceof long) {return
			new Date (long) input);
		}
		return null;
	}

	@Override Public
	Date parseliteral (Object input) {
		if (input instanceof intvalue) {return
			new Date (( intvalue) input). GetValue (). Longvalue ());
		return null;
	}
});
Graphqlenumtype

Before building a GRAPHQL data structure, you first need to do some basic preparation, such as converting the enum type defined in the Java data structure to Graphqlenumtype

Create Graphqlenumtype you can use function newenum, such as Orderstatus:

	Private Graphqlenumtype Orderstatusenum = NewEnum ().
		name ("Orderstatus").
		description ("Order status")
		. Value ("OPEN", Orderstatus.open, "unpaid order")
		. Value ("CLOSED", orderstatus.closed, "CLOSED order")
		. Value ( "Cancelled", orderstatus.cancelled, "cancelled order")
		. Value ("Fulfilled", orderstatus.fulfilled, "finished Order ")
		. Build ();

function Value Declaration:

Public Builder Value (string name) public Builder value (string name
, Object value) public
Builder value (String na Me, object value, string description) public
Builder Value (string name, object value, string description, String Depr Ecationreason)

When only name is passed, name is value. Graphqlobjecttype

Now we can define our data structure as graphqlobjecttype. Each field defined in Graphqlobjecttype can be obtained from the front end, so you should not define a field here that you do not want to be fetched by the front end, just for example

Graphqlobjecttype Orderlinetype = NewObject (). Name ("Orderline"). field (field-> field.type (graphqlid). Name (" ProductId ")). field (field-> field.type (graphqlid). Name (" Skuid "). field (field-> field.type (graphqlstring). Name ("ProductName")). field (field-> field.type (graphqlstring). Name ("SKUName"). field (Field-> Field.type (
				
graphqlint). Name ("Quantity"). field (field-> field.type (graphqlbigdecimal). Name (' price '). Build (); Graphqlobjecttype OrderType = NewObject (). Name (' order '). Description ("Order"). field (field-> Field.type (graphqlid . Name ("id"). field (field-> field.type (graphqlstring). Name ("UserName"). field (Field-> Field.type ( graphqlstring). Name ("Usermobile"). field (field-> field.type (graphqlstring). Name ("Address"). field (Field-> Field.type (orderstatusenum). Name ("status"). field (field-> field.type (new Graphqllist (Orderlinetype)). Name (" OrderLines ")). field (field-> field.type (graphqldate). Name (" Purchasetime ")). FIeld (Field-> field.type (graphqldate). Name ("Finishtime"). field (field-> field.type (graphqldate). Name ("
 Timecreated ")). Build ();

If Graphqlobjecttype field name and entity field type are the same, Graphql-java automatically does mapping. Querying queries with parameters

Usually we create a node for the query, and all the clients using GRAPHQL to start the query with the node

	Public Graphqlobjecttype Getquerytype () {return
		newobject ()
			. Name ("QueryType")
			. field (Field-> Field.type (OrderType). Name ("Order").
				argument (argument-> argument.name ("id"). Type (graphqlid).
				Datafetcher (Dynamicdatafetcher::orderfetcher))
			. Build ();

Here we declare a field called Order of type OrderType in QueryType this object, obtain order need argument ID, and declare order data fetcher.

	Public order Orderfetcher (Datafetchingenvironment env) {
		String id = env.getargument ("id");
		return GetOrder (ID);
	}

Orderfetcher receives a datafetchingenvironment type parameter where the GetArgument method of the parameter can be used to get the corresponding incoming parameter, or you can use the GetSource to call data fetcher Data structures for the current layer, such as product:

Graphqlobjecttype ProductType = NewObject ().
	name ("Product").
	description ("Product")
	. field (Field- > Field.type (graphqlid). Name ("id").
	field (field-> field.type (graphqlid). Name ("CategoryID").
	Field (field-> field.type (New Graphqltypereference ("category")). Name ("category")
		. Datafetcher ( productdatafetcher::categorydatafetcher))
	...
	...
	. Build ();

Public Category Categorydatafetcher (datafetchingenvironment env) {
	Product Product = (product) Env.getsource ()). Getcategoryid ();
	Return GetCategory (Product.getcategoryid ());
}

Here, we get the data structure of product through the Env.getsource () method, and find category according to the existing CategoryID.

Note the definition of ProductType, we provide CategoryID and category two field at the same time to avoid doing a data fetcher operation when the user needs to get CategoryID. Also, to avoid circular references, we use the graphqltypereference to define the type of category. Mutation

GRAPHQL also supports write operations, as with queries, we can define a node to write data, modify the data in the view of the defined Fetcher, and return the required attributes. We can use Graphqlobjecttype to define more complex incoming parameters:

private static final Graphqlinputobjecttype Inputorderlinetype = Newinputobject ()
	. Name ("Inputorderlinetype")
	. field (Field-> field.name ("ProductId"). Type (graphqlid).
	Field (field-> field.name ("Skuid"). Type ( Graphqlid)
	. field (Field-> field.name ("Quantity"). Type (graphqlint).
	Field (field-> field.name (" Price "). Type (Graphqlbigdecimal)
	. Build ();

private static final Graphqlinputobjecttype Inputordertype = Newinputobject ()
	. Name ("Inputordertype")
	. Field (Field-> field.name ("StoreId"). Type (graphqlid).
	Field (Field-> field.name ("OrderLines"). Type (new Graphqllist (Inputorderlinetype))
	. Build ();

Note that when we get the Graphqlinputobjecttype parameter in data fetcher, we get a type of LINKEDHASHMAP data. Provides the GRAPHQL API

The code for the API layer is as follows

@Component @Path ("graphql") @Consumes (Mediatype.application_json) @Produces (Mediatype.application_json) public
	
	Class Graphqlapi {private static final Logger LOG = Loggerfactory.getlogger (Graphqlapi.class);

	@Autowired private QueryType QueryType;
	
	@Autowired private Mutationtype Mutationtype;
	Private GRAPHQL getgraphql () {Return to New GRAPHQL (GetSchema ()); Private Graphqlschema GetSchema () {return Graphqlschema.newschema (). Query (Querytype.getquerytype ()). Mutatio
	N (Mutationtype.getmutationtype ()). build (); @POST public Response executeoperation (Map body, @Context Containerrequestcontext request) {string query = (string
		) Body.get ("Query");
		map<string, object> variables = (map<string, object>) body.get ("variables"); ExecutionResult ExecutionResult = GETGRAPHQL (). Execute (query, request, variables = = null?)
		Maps.newhashmap (): variables);
		map<string, object> result = new linkedhashmap<> (); if (Executionresult.geterrors ().Size () > 0) {log.warn ("graphql Command execute Error, command: {} cause: {}", Body, executionresult.geterrors ());
		Result.put ("Errors", executionresult.geterrors ());

		} result.put ("Data", Executionresult.getdata ());
	Return Response.ok (). Entity (Result). Build ();

 }

The Execute method receives three parameters, where the second argument is the context, and we pass the request directly in for subsequent permission validation. Permission validation

When the user accesses some sensitive data, we may have to authenticate the user's permissions, then we can validate it in the implementation of the data fetcher using the context passed by the above invoke execute:

Public UserInfo Userinfofetcher (datafetchingenvironment env) {
	final containerrequestcontext requestcontext  = (Containerrequestcontext) env.getcontext ();
	Using requestcontext Check permission here.
    ...

}
ErrorHandler

When the GRAPHQL command is executed, the syntax check of the GRAPHQL Schema and GRAPHQL command is performed, and all the data fetcher exceptions are handler, and the Graphqlerror list is finally converted to Executionres Ult and returns to the result. The Graphqlerror interface declaration is as follows:

Public interface Graphqlerror {

    String getMessage ();

    List<sourcelocation> getlocations ();

    ErrorType Geterrortype ();

}

Most of the time, Graphqlerror can not actually meet the needs of the actual situation. So some transformations are needed to meet the needs of the usage. Now provide a way of thinking as follows:

Private list<json> Customerror (ExecutionResult executionresult) {return
    executionresult.geterrors ()
        . Stream ()
        . Map (This::handleerror)
        . Map (This::tojson)
        . Collect (Collectors.tolist ());

Private Throwable HandleError (graphqlerror error) {
    switch (Error.geterrortype ()) {
        case Datafetchingexception: Return
            ((exceptionwhiledatafetching) error). GetException ();
        Case ValidationError: Case
        Invalidsyntax: Return
            new Exception (Error.getmessage ());
        Default: Return
            new Unknownexception ();
    }
}

Private Json Tojson (Throwable throwable) {
    final Json json = Json.read (JSON (Throwable));
    Json.delat ("StackTrace");
    Json.delat ("Localizedmessage");
    Json.delat ("cause");
    return JSON;
}
Speed of GRAPHQL

The GRAPHQL protocol allows parallel queries to be invoked while calling the query command, while mutation prohibits the use of parallel operations, which, if you want to implement query parallelism, can be configured as follows:

Threadpoolexecutor threadpoolexecutor = new Threadpoolexecutor (
        2,/* Core pool Size 2 Thread */
        2, * max pool si Ze 2 Thread * *,
        timeunit.seconds,
        new linkedblockingqueue<runnable> (),
        new Threadpoolexecutor.callerrunspolicy ());

GRAPHQL graphql = Graphql.newobject (Starwarsschema.starwarsschema)
        . Queryexecutionstrategy (New Executorserviceexecutionstrategy (Threadpoolexecutor))
        . Mutationexecutionstrategy (New Simpleexecutionstrategy ())
        . Build ();

The data Fetcher cache and other operations, you need to complete the developers themselves. Document that provides GRAPHQL

Using argument and Datafetcher, we can define a large data graph, and the front end defines the query based on the data map. You can use tool Graphdoc to generate GRAPHQL documents for front-end use. The use of Graphdoc is simple:

# Install Graphdoc
npm install-g @2fd/graphdoc

# Generate documentation from live endpoint
GRAPHDOC-E Ps://your.api.uri/graphql-o./graphql-schema

Of course, if the client uses the GRAPHQL framework as Apollo Client, test and document viewing in front-end development can also use the Apollo client Developer tools  plug-in of Chrome.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.