github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/wiki/content/clients/index.md (about)

     1  +++
     2  date = "2017-03-20T19:35:35+11:00"
     3  title = "Clients"
     4  +++
     5  
     6  ## Implementation
     7  
     8  Clients can communicate with the server in two different ways:
     9  
    10  - **Via [gRPC](http://www.grpc.io/).** Internally this uses [Protocol
    11    Buffers](https://developers.google.com/protocol-buffers) (the proto file
    12  used by Dgraph is located at
    13  [api.proto](https://github.com/dgraph-io/dgo/blob/master/protos/api.proto)).
    14  
    15  - **Via HTTP.** There are various endpoints, each accepting and returning JSON.
    16    There is a one to one correspondence between the HTTP endpoints and the gRPC
    17  service methods.
    18  
    19  
    20  It's possible to interface with Dgraph directly via gRPC or HTTP. However, if a
    21  client library exists for you language, this will be an easier option.
    22  
    23  {{% notice "tip" %}}
    24  For multi-node setups, predicates are assigned to the group that first sees that
    25  predicate. Dgraph also automatically moves predicate data to different groups in
    26  order to balance predicate distribution. This occurs automatically every 10
    27  minutes. It's possible for clients to aid this process by communicating with all
    28  Dgraph instances. For the Go client, this means passing in one
    29  `*grpc.ClientConn` per Dgraph instance. Mutations will be made in a round robin
    30  fashion, resulting in an initially semi random predicate distribution.
    31  {{% /notice %}}
    32  
    33  ### Transactions
    34  
    35  Dgraph clients perform mutations and queries using transactions. A
    36  transaction bounds a sequence of queries and mutations that are committed by
    37  Dgraph as a single unit: that is, on commit, either all the changes are accepted
    38  by Dgraph or none are.
    39  
    40  A transaction always sees the database state at the moment it began, plus any
    41  changes it makes --- changes from concurrent transactions aren't visible.
    42  
    43  On commit, Dgraph will abort a transaction, rather than committing changes, when
    44  a conflicting, concurrently running transaction has already been committed.  Two
    45  transactions conflict when both transactions:
    46  
    47  - write values to the same scalar predicate of the same node (e.g both
    48    attempting to set a particular node's `address` predicate); or
    49  - write to a singular `uid` predicate of the same node (changes to `[uid]` predicates can be concurrently written); or
    50  - write a value that conflicts on an index for a predicate with `@upsert` set in the schema (see [upserts]({{< relref "howto/index.md#upserts">}})).
    51  
    52  When a transaction is aborted, all its changes are discarded.  Transactions can be manually aborted.
    53  
    54  ## Go
    55  
    56  [![GoDoc](https://godoc.org/github.com/dgraph-io/dgo?status.svg)](https://godoc.org/github.com/dgraph-io/dgo)
    57  
    58  The Go client communicates with the server on the gRPC port (default value 9080).
    59  
    60  The client can be obtained in the usual way via `go get`:
    61  
    62  ```sh
    63  # Requires at least Go 1.11
    64  export GO111MODULE=on
    65  go get -u -v github.com/dgraph-io/dgo/v2
    66  ```
    67  
    68  The full [GoDoc](https://godoc.org/github.com/dgraph-io/dgo) contains
    69  documentation for the client API along with examples showing how to use it.
    70  
    71  ### Create the client
    72  
    73  To create a client, dial a connection to Dgraph's external gRPC port (typically
    74  9080). The following code snippet shows just one connection. You can connect to multiple Dgraph Alphas to distribute the workload evenly.
    75  
    76  ```go
    77  func newClient() *dgo.Dgraph {
    78  	// Dial a gRPC connection. The address to dial to can be configured when
    79  	// setting up the dgraph cluster.
    80  	d, err := grpc.Dial("localhost:9080", grpc.WithInsecure())
    81  	if err != nil {
    82  		log.Fatal(err)
    83  	}
    84  
    85  	return dgo.NewDgraphClient(
    86  		api.NewDgraphClient(d),
    87  	)
    88  }
    89  ```
    90  
    91  The client can be configured to use gRPC compression:
    92  
    93  ```go
    94  func newClient() *dgo.Dgraph {
    95  	// Dial a gRPC connection. The address to dial to can be configured when
    96  	// setting up the dgraph cluster.
    97  	dialOpts := append([]grpc.DialOption{},
    98  		grpc.WithInsecure(),
    99  		grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name)))
   100  	d, err := grpc.Dial("localhost:9080", dialOpts...)
   101  
   102  	if err != nil {
   103  		log.Fatal(err)
   104  	}
   105  
   106  	return dgo.NewDgraphClient(
   107  		api.NewDgraphClient(d),
   108  	)
   109  }
   110  
   111  ```
   112  
   113  ### Alter the database
   114  
   115  To set the schema, set it on a `api.Operation` object, and pass it down to
   116  the `Alter` method.
   117  
   118  ```go
   119  func setup(c *dgo.Dgraph) {
   120  	// Install a schema into dgraph. Accounts have a `name` and a `balance`.
   121  	err := c.Alter(context.Background(), &api.Operation{
   122  		Schema: `
   123  			name: string @index(term) .
   124  			balance: int .
   125  		`,
   126  	})
   127  }
   128  ```
   129  
   130  `api.Operation` contains other fields as well, including drop predicate and drop
   131  all. Drop all is useful if you wish to discard all the data, and start from a
   132  clean slate, without bringing the instance down.
   133  
   134  ```go
   135  	// Drop all data including schema from the dgraph instance. This is useful
   136  	// for small examples such as this, since it puts dgraph into a clean
   137  	// state.
   138  	err := c.Alter(context.Background(), &api.Operation{DropOp: api.Operation_ALL})
   139  ```
   140  
   141  The old way to send a drop all operation is still supported but will be eventually
   142  deprecated. It's shown below for reference.
   143  
   144  ```go
   145  	// Drop all data including schema from the dgraph instance. This is useful
   146  	// for small examples such as this, since it puts dgraph into a clean
   147  	// state.
   148  	err := c.Alter(context.Background(), &api.Operation{DropAll: true})
   149  ```
   150  
   151  Starting with version 1.1, `api.Operation` also supports a drop data operation.
   152  This operation drops all the data but preserves the schema. This is useful when
   153  the schema is large and needs to be reused, such as in between unit tests.
   154  
   155  ```go
   156  	// Drop all data including schema from the dgraph instance. This is useful
   157  	// for small examples such as this, since it puts dgraph into a clean
   158  	// state.
   159  	err := c.Alter(context.Background(), &api.Operation{DropOp: api.Operation_DATA})
   160  ```
   161  
   162  ### Create a transaction
   163  
   164  Dgraph supports running distributed ACID transactions. To create a
   165  transaction, just call `c.NewTxn()`. This operation incurs no network call.
   166  Typically, you'd also want to call a `defer txn.Discard()` to let it
   167  automatically rollback in case of errors. Calling `Discard` after `Commit` would
   168  be a no-op.
   169  
   170  ```go
   171  func runTxn(c *dgo.Dgraph) {
   172  	txn := c.NewTxn()
   173  	defer txn.Discard()
   174  	...
   175  }
   176  ```
   177  
   178  #### Read-Only Transactions
   179  
   180  Read-only transactions can be created by calling `c.NewReadOnlyTxn()`. Read-only
   181  transactions are useful to increase read speed because they can circumvent the
   182  usual consensus protocol. Read-only transactions cannot contain mutations and
   183  trying to call `txn.Commit()` will result in an error. Calling `txn.Discard()`
   184  will be a no-op.
   185  
   186  Read-only queries can optionally be set as best-effort. Using this flag will ask
   187  the Dgraph Alpha to try to get timestamps from memory on a best-effort basis to
   188  reduce the number of outbound requests to Zero. This may yield improved
   189  latencies in read-bound workloads where linearizable reads are not strictly
   190  needed.
   191  
   192  ### Run a query
   193  
   194  You can run a query by calling `txn.Query`. The response would contain a `JSON`
   195  field, which has the JSON encoded result. You can unmarshal it into Go struct
   196  via `json.Unmarshal`.
   197  
   198  ```go
   199  	// Query the balance for Alice and Bob.
   200  	const q = `
   201  		{
   202  			all(func: anyofterms(name, "Alice Bob")) {
   203  				uid
   204  				balance
   205  			}
   206  		}
   207  	`
   208  	resp, err := txn.Query(context.Background(), q)
   209  	if err != nil {
   210  		log.Fatal(err)
   211  	}
   212  
   213  	// After we get the balances, we have to decode them into structs so that
   214  	// we can manipulate the data.
   215  	var decode struct {
   216  		All []struct {
   217  			Uid     string
   218  			Balance int
   219  		}
   220  	}
   221  	if err := json.Unmarshal(resp.GetJson(), &decode); err != nil {
   222  		log.Fatal(err)
   223  	}
   224  ```
   225  
   226  ### Run a mutation
   227  
   228  `txn.Mutate` would run the mutation. It takes in a `api.Mutation` object,
   229  which provides two main ways to set data: JSON and RDF N-Quad. You can choose
   230  whichever way is convenient.
   231  
   232  To use JSON, use the fields SetJson and DeleteJson, which accept a string
   233  representing the nodes to be added or removed respectively (either as a JSON map
   234  or a list). To use RDF, use the fields SetNquads and DeleteNquads, which accept
   235  a string representing the valid RDF triples (one per line) to added or removed
   236  respectively. This protobuf object also contains the Set and Del fields which
   237  accept a list of RDF triples that have already been parsed into our internal
   238  format. As such, these fields are mainly used internally and users should use
   239  the SetNquads and DeleteNquads instead if they are planning on using RDF.
   240  
   241  We're going to continue using JSON. You could modify the Go structs parsed from
   242  the query, and marshal them back into JSON.
   243  
   244  ```go
   245  	// Move $5 between the two accounts.
   246  	decode.All[0].Bal += 5
   247  	decode.All[1].Bal -= 5
   248  
   249  	out, err := json.Marshal(decode.All)
   250  	if err != nil {
   251  		log.Fatal(err)
   252  	}
   253  
   254  	_, err := txn.Mutate(context.Background(), &api.Mutation{SetJson: out})
   255  ```
   256  
   257  Sometimes, you only want to commit mutation, without querying anything further.
   258  In such cases, you can use a `CommitNow` field in `api.Mutation` to
   259  indicate that the mutation must be immediately committed.
   260  
   261  ### Commit the transaction
   262  
   263  Once all the queries and mutations are done, you can commit the transaction. It
   264  returns an error in case the transaction could not be committed.
   265  
   266  ```go
   267  	// Finally, we can commit the transactions. An error will be returned if
   268  	// other transactions running concurrently modify the same data that was
   269  	// modified in this transaction. It is up to the library user to retry
   270  	// transactions when they fail.
   271  
   272  	err := txn.Commit(context.Background())
   273  ```
   274  
   275  ### Complete Example
   276  
   277  This is an example from the [GoDoc](https://godoc.org/github.com/dgraph-io/dgo). It shows how to to create a Node with name Alice, while also creating her relationships with other nodes. Note `loc` predicate is of type `geo` and can be easily marshalled and unmarshalled into a Go struct. More such examples are present as part of the GoDoc.
   278  
   279  ```go
   280  type School struct {
   281  	Name string `json:"name,omitempty"`
   282  	DType []string `json:"dgraph.type,omitempty"`
   283  }
   284  
   285  type loc struct {
   286  	Type   string    `json:"type,omitempty"`
   287  	Coords []float64 `json:"coordinates,omitempty"`
   288  }
   289  
   290  // If omitempty is not set, then edges with empty values (0 for int/float, "" for string, false
   291  // for bool) would be created for values not specified explicitly.
   292  
   293  type Person struct {
   294  		Uid      string     `json:"uid,omitempty"`
   295  		Name     string     `json:"name,omitempty"`
   296  		Age      int        `json:"age,omitempty"`
   297  		Dob      *time.Time `json:"dob,omitempty"`
   298  		Married  bool       `json:"married,omitempty"`
   299  		Raw      []byte     `json:"raw_bytes,omitempty"`
   300  		Friends  []Person   `json:"friend,omitempty"`
   301  		Location loc        `json:"loc,omitempty"`
   302  		School   []School   `json:"school,omitempty"`
   303  		DType    []string   `json:"dgraph.type,omitempty"`
   304  }
   305  
   306  conn, err := grpc.Dial("127.0.0.1:9080", grpc.WithInsecure())
   307  if err != nil {
   308  	log.Fatal("While trying to dial gRPC")
   309  }
   310  defer conn.Close()
   311  
   312  dc := api.NewDgraphClient(conn)
   313  dg := dgo.NewDgraphClient(dc)
   314  
   315  op := &api.Operation{}
   316  op.Schema = `
   317  	name: string @index(exact) .
   318  	age: int .
   319  	married: bool .
   320  	loc: geo .
   321  	dob: datetime .
   322  
   323  type Person {
   324    name: string
   325    age: int
   326    dob: string
   327    married: bool
   328    raw: string
   329    friends: [uid]
   330    loc: [uid]
   331    school: [uid]
   332   }
   333  
   334  type Loc {
   335    type: string
   336    coords: float
   337   }
   338  
   339  type Institution {
   340    name: string
   341   }
   342  
   343  `
   344  
   345  ctx := context.Background()
   346  err = dg.Alter(ctx, op)
   347  if err != nil {
   348  	log.Fatal(err)
   349  }
   350  
   351  dob := time.Date(1980, 01, 01, 23, 0, 0, 0, time.UTC)
   352  // While setting an object if a struct has a Uid then its properties in the graph are updated
   353  // else a new node is created.
   354  // In the example below new nodes for Alice, Bob and Charlie and school are created (since they
   355  // dont have a Uid).
   356  p := Person{
   357      Uid:     "_:alice",
   358  	Name:    "Alice",
   359  	Age:     26,
   360  	Married: true,
   361  	Location: loc{
   362  		Type:   "Point",
   363  		Coords: []float64{1.1, 2},
   364  	},
   365  	Dob: &dob,
   366  	Raw: []byte("raw_bytes"),
   367  	Friends: []Person{{
   368  		Name: "Bob",
   369  		Age:  24,
   370  	}, {
   371  		Name: "Charlie",
   372  		Age:  29,
   373  	}},
   374  	School: []School{{
   375  		Name: "Crown Public School",
   376  	}},
   377  }
   378  
   379  mu := &api.Mutation{
   380  	CommitNow: true,
   381  }
   382  pb, err := json.Marshal(p)
   383  if err != nil {
   384  	log.Fatal(err)
   385  }
   386  
   387  mu.SetJson = pb
   388  assigned, err := dg.NewTxn().Mutate(ctx, mu)
   389  if err != nil {
   390  	log.Fatal(err)
   391  }
   392  
   393  // Assigned uids for nodes which were created would be returned in the resp.AssignedUids map.
   394  variables := map[string]string{"$id": assigned.Uids["alice"]}
   395  q := `query Me($id: string){
   396  	me(func: uid($id)) {
   397  		name
   398  		dob
   399  		age
   400  		loc
   401  		raw_bytes
   402  		married
   403  		dgraph.type
   404  		friend @filter(eq(name, "Bob")){
   405  			name
   406  			age
   407  			dgraph.type
   408  		}
   409  		school {
   410  			name
   411  			dgraph.type
   412  		}
   413  	}
   414  }`
   415  
   416  resp, err := dg.NewTxn().QueryWithVars(ctx, q, variables)
   417  if err != nil {
   418  	log.Fatal(err)
   419  }
   420  
   421  type Root struct {
   422  	Me []Person `json:"me"`
   423  }
   424  
   425  var r Root
   426  err = json.Unmarshal(resp.Json, &r)
   427  if err != nil {
   428  	log.Fatal(err)
   429  }
   430  // fmt.Printf("Me: %+v\n", r.Me)
   431  // R.Me would be same as the person that we set above.
   432  
   433  fmt.Println(string(resp.Json))
   434  // Output: {"me":[{"name":"Alice","dob":"1980-01-01T23:00:00Z","age":26,"loc":{"type":"Point","coordinates":[1.1,2]},"raw_bytes":"cmF3X2J5dGVz","married":true,"dgraph.type":["Person"],"friend":[{"name":"Bob","age":24,"dgraph.type":["Person"]}],"school":[{"name":"Crown Public School","dgraph.type":["Institution"]}]}]}
   435  
   436  
   437  ```
   438  
   439  
   440  ## Java
   441  
   442  The official Java client [can be found here](https://github.com/dgraph-io/dgraph4j)
   443  and it fully supports Dgraph v1.0.x. Follow the instructions in the
   444  [README](https://github.com/dgraph-io/dgraph4j#readme)
   445  to get it up and running.
   446  
   447  We also have a [DgraphJavaSample] project, which contains an end-to-end
   448  working example of how to use the Java client.
   449  
   450  [DgraphJavaSample]:https://github.com/dgraph-io/dgraph4j/tree/master/samples/DgraphJavaSample
   451  
   452  ## JavaScript
   453  
   454  The official JavaScript client [can be found here](https://github.com/dgraph-io/dgraph-js)
   455  and it fully supports Dgraph v1.0.x. Follow the instructions in the
   456  [README](https://github.com/dgraph-io/dgraph-js#readme) to get it up and running.
   457  
   458  We also have a [simple example](https://github.com/dgraph-io/dgraph-js/tree/master/examples/simple)
   459  project, which contains an end-to-end working example of how to use the JavaScript client,
   460  for Node.js >= v6.
   461  
   462  ## Python
   463  
   464  The official Python client [can be found here](https://github.com/dgraph-io/pydgraph)
   465  and it fully supports Dgraph v1.0.x and Python versions >= 2.7 and >= 3.5. Follow the
   466  instructions in the [README](https://github.com/dgraph-io/pydgraph#readme) to get it
   467  up and running.
   468  
   469  We also have a [simple example](https://github.com/dgraph-io/pydgraph/tree/master/examples/simple)
   470  project, which contains an end-to-end working example of how to use the Python client.
   471  
   472  ## Unofficial Dgraph Clients
   473  
   474  {{% notice "note" %}}
   475  These third-party clients are contributed by the community and are not officially supported by Dgraph.
   476  {{% /notice %}}
   477  
   478  ### C\#
   479  
   480  - https://github.com/AlexandreDaSilva/DgraphNet
   481  - https://github.com/MichaelJCompton/Dgraph-dotnet
   482  
   483  ### Dart
   484  
   485  - https://github.com/katutz/dgraph
   486  
   487  ### Elixir
   488  
   489  - https://github.com/liveforeverx/dlex
   490  - https://github.com/ospaarmann/exdgraph
   491  
   492  ### Rust
   493  
   494  - https://github.com/Swoorup/dgraph-rs
   495  
   496  ## Raw HTTP
   497  
   498  {{% notice "warning" %}}
   499  Raw HTTP needs more chops to use than our language clients. We wrote this guide to help you build a Dgraph client in a new language.
   500  {{% /notice %}}
   501  
   502  It's also possible to interact with Dgraph directly via its HTTP endpoints.
   503  This allows clients to be built for languages that don't have access to a
   504  working gRPC implementation.
   505  
   506  In the examples shown here, regular command line tools such as `curl` and
   507  [`jq`](https://stedolan.github.io/jq/) are used. However, the real intention
   508  here is to show other programmers how they could implement a client in their
   509  language on top of the HTTP API. For an example of how to build a client on top
   510  of gRPC, refer to the implementation of the Go client.
   511  
   512  Similar to the Go client example, we use a bank account transfer example.
   513  
   514  ### Create the Client
   515  
   516  A client built on top of the HTTP API will need to track three pieces of state
   517  for each transaction.
   518  
   519  1. A start timestamp (`start_ts`). This uniquely identifies a transaction,
   520     and doesn't change over the transaction lifecycle.
   521  
   522  2. The set of keys modified by the transaction (`keys`). This aids in
   523     transaction conflict detection.
   524  
   525       Every mutation would send back a new set of keys. The client must merge them
   526       with the existing set. Optionally, a client can de-dup these keys while
   527       merging.
   528  
   529  3. The set of predicates modified by the transaction (`preds`). This aids in
   530     predicate move detection.
   531  
   532       Every mutation would send back a new set of preds. The client must merge them
   533       with the existing set. Optionally, a client can de-dup these keys while
   534       merging.
   535  
   536  ### Alter the database
   537  
   538  The `/alter` endpoint is used to create or change the schema. Here, the
   539  predicate `name` is the name of an account. It's indexed so that we can look up
   540  accounts based on their name.
   541  
   542  ```sh
   543  $ curl -X POST localhost:8080/alter -d 'name: string @index(term) .'
   544  ```
   545  
   546  If all goes well, the response should be `{"code":"Success","message":"Done"}`.
   547  
   548  Other operations can be performed via the `/alter` endpoint as well. A specific
   549  predicate or the entire database can be dropped.
   550  
   551  To drop the predicate `name`:
   552  ```sh
   553  $ curl -X POST localhost:8080/alter -d '{"drop_attr": "name"}'
   554  ```
   555  
   556  To drop the type `Film`:
   557  ```sh
   558  $ curl -X POST localhost:8080/alter -d '{"drop_op": "TYPE", "drop_value": "Film"}'
   559  ```
   560  
   561  To drop all data and schema:
   562  ```sh
   563  $ curl -X POST localhost:8080/alter -d '{"drop_all": true}'
   564  ```
   565  
   566  To drop all data only (keep schema):
   567  ```sh
   568  $ curl -X POST localhost:8080/alter -d '{"drop_op": "DATA"}'
   569  ```
   570  
   571  ### Start a transaction
   572  
   573  Assume some initial accounts with balances have been populated. We now want to
   574  transfer money from one account to the other. This is done in four steps:
   575  
   576  1. Create a new transaction.
   577  
   578  1. Inside the transaction, run a query to determine the current balances.
   579  
   580  2. Perform a mutation to update the balances.
   581  
   582  3. Commit the transaction.
   583  
   584  Starting a transaction doesn't require any interaction with Dgraph itself.
   585  Some state needs to be set up for the transaction to use. The `start_ts`
   586  can initially be set to 0. `keys` can start as an empty set.
   587  
   588  **For both query and mutation if the `start_ts` is provided as a path parameter,
   589  then the operation is performed as part of the ongoing transaction. Otherwise, a
   590  new transaction is initiated.**
   591  
   592  ### Run a query
   593  
   594  To query the database, the `/query` endpoint is used. Remember to set the `Content-Type` header
   595  to `application/graphql+-` in order to ensure that the body of the request is correctly parsed.
   596  
   597  To get the balances for both accounts:
   598  
   599  ```sh
   600  $ curl -H "Content-Type: application/graphql+-" -X POST localhost:8080/query -d $'
   601  {
   602    balances(func: anyofterms(name, "Alice Bob")) {
   603      uid
   604      name
   605      balance
   606    }
   607  }' | jq
   608  
   609  ```
   610  
   611  The result should look like this:
   612  
   613  ```json
   614  {
   615    "data": {
   616      "balances": [
   617        {
   618          "uid": "0x1",
   619          "name": "Alice",
   620          "balance": "100"
   621        },
   622        {
   623          "uid": "0x2",
   624          "name": "Bob",
   625          "balance": "70"
   626        }
   627      ]
   628    },
   629    "extensions": {
   630      "server_latency": {
   631        "parsing_ns": 70494,
   632        "processing_ns": 697140,
   633        "encoding_ns": 1560151
   634      },
   635      "txn": {
   636        "start_ts": 4,
   637      }
   638    }
   639  }
   640  ```
   641  
   642  Notice that along with the query result under the `data` field is additional
   643  data in the `extensions -> txn` field. This data will have to be tracked by the
   644  client.
   645  
   646  For queries, there is a `start_ts` in the response. This `start_ts` will need to
   647  be used in all subsequent interactions with Dgraph for this transaction, and so
   648  should become part of the transaction state.
   649  
   650  ### Run a Mutation
   651  
   652  Now that we have the current balances, we need to send a mutation to Dgraph
   653  with the updated balances. If Bob transfers $10 to Alice, then the RDFs to send
   654  are:
   655  
   656  ```
   657  <0x1> <balance> "110" .
   658  <0x2> <balance> "60" .
   659  ```
   660  
   661  Note that we have to refer to the Alice and Bob nodes by UID in the RDF format.
   662  
   663  We now send the mutations via the `/mutate` endpoint. We need to provide our
   664  transaction start timestamp as a path parameter, so that Dgraph knows which
   665  transaction the mutation should be part of. We also need to set `Content-Type`
   666  header to `application/rdf` in order to specify that mutation is written in
   667  rdf format.
   668  
   669  ```sh
   670  $ curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?startTs=4 -d $'
   671  {
   672    set {
   673      <0x1> <balance> "110" .
   674      <0x2> <balance> "60" .
   675    }
   676  }
   677  ' | jq
   678  ```
   679  
   680  The result:
   681  
   682  ```json
   683  {
   684    "data": {
   685      "code": "Success",
   686      "message": "Done",
   687      "uids": {}
   688    },
   689    "extensions": {
   690      "server_latency": {
   691        "parsing_ns": 50901,
   692        "processing_ns": 14631082
   693      },
   694      "txn": {
   695        "start_ts": 4,
   696        "keys": [
   697          "2ahy9oh4s9csc",
   698          "3ekeez23q5149"
   699        ],
   700        "preds": [
   701          "1-balance"
   702        ]
   703      }
   704    }
   705  }
   706  ```
   707  
   708  We get some `keys`. These should be added to the set of `keys` stored in the
   709  transaction state. We also get some `preds`, which should be added to the set of
   710  `preds` stored in the transaction state.
   711  
   712  ### Committing the transaction
   713  
   714  {{% notice "note" %}}
   715  It's possible to commit immediately after a mutation is made (without requiring
   716  to use the `/commit` endpoint as explained in this section). To do this, add
   717  the parameter `commitNow` in the URL `/mutate?commitNow=true`.
   718  {{% /notice %}}
   719  
   720  Finally, we can commit the transaction using the `/commit` endpoint. We need the
   721  `start_ts` we've been using for the transaction along with the `keys` and the
   722  `preds`. If we had performed multiple mutations in the transaction instead of
   723  just one, then the keys and preds provided during the commit would be the union
   724  of all keys and preds returned in the responses from the `/mutate` endpoint.
   725  
   726  The `preds` field is used to abort the transaction in cases where some of the
   727  predicates are moved. This field is not required and the `/commit` endpoint also
   728  accepts the old format, which was a single array of keys.
   729  
   730  ```sh
   731  $ curl -X POST localhost:8080/commit?startTs=4 -d $'
   732  {
   733    "keys": [
   734  		"2ahy9oh4s9csc",
   735  		"3ekeez23q5149"
   736  	],
   737    "preds": [
   738      "1-balance"
   739  	]
   740  }' | jq
   741  ```
   742  
   743  The result:
   744  
   745  ```json
   746  {
   747    "data": {
   748      "code": "Success",
   749      "message": "Done"
   750    },
   751    "extensions": {
   752      "txn": {
   753        "start_ts": 4,
   754        "commit_ts": 5
   755      }
   756    }
   757  }
   758  ```
   759  The transaction is now complete.
   760  
   761  If another client were to perform another transaction concurrently affecting
   762  the same keys, then it's possible that the transaction would *not* be
   763  successful.  This is indicated in the response when the commit is attempted.
   764  
   765  ```json
   766  {
   767    "errors": [
   768      {
   769        "code": "Error",
   770        "message": "Transaction has been aborted. Please retry."
   771      }
   772    ]
   773  }
   774  ```
   775  
   776  In this case, it should be up to the user of the client to decide if they wish
   777  to retry the transaction.
   778  
   779  ### Aborting the transaction
   780  To abort a transaction, use the same `/commit` endpoint with the `abort=true` parameter
   781  while specifying the `startTs` value for the transaction.
   782  
   783  ```sh
   784  $ curl -X POST "localhost:8080/commit?startTs=4&abort=true" | jq
   785  ```
   786  
   787  The result:
   788  
   789  ```json
   790  {
   791    "code": "Success",
   792    "message": "Done"
   793  }
   794  ```
   795  
   796  ### Compression via HTTP
   797  
   798  Dgraph supports gzip-compressed requests to and from Dgraph Alphas for `/query`, `/mutate`, and `/alter`.
   799  
   800  Compressed requests: To send compressed requests, set the HTTP request header
   801  `Content-Encoding: gzip` along with the gzip-compressed payload.
   802  
   803  Compressed responses: To receive gzipped responses, set the HTTP request header
   804  `Accept-Encoding: gzip` and Alpha will return gzipped responses.
   805  
   806  Example of a compressed request via curl:
   807  
   808  ```sh
   809  $ curl -X POST \
   810    -H 'Content-Encoding: gzip' \
   811    -H "Content-Type: application/rdf" \
   812    localhost:8080/mutate?commitNow=true --data-binary @mutation.gz
   813  ```
   814  
   815  Example of a compressed request via curl:
   816  
   817  ```sh
   818  $ curl -X POST \
   819    -H 'Accept-Encoding: gzip' \
   820    -H "Content-Type: application/graphql+-" \
   821    localhost:8080/query -d $'schema {}' | gzip --decompress
   822  ```
   823  
   824  Example of a compressed request and response via curl:
   825  
   826  ```sh
   827  $ zcat query.gz # query.gz is gzipped compressed
   828  {
   829    all(func: anyofterms(name, "Alice Bob")) {
   830      uid
   831      balance
   832    }
   833  }
   834  ```
   835  
   836  ```sh
   837  $ curl -X POST \
   838    -H 'Content-Encoding: gzip' \
   839    -H 'Accept-Encoding: gzip' \
   840    -H "Content-Type: application/graphql+-" \
   841    localhost:8080/query --data-binary @query.gz | gzip --decompress
   842  ```
   843  
   844  {{% notice "note" %}}
   845  Curl has a `--compressed` option that automatically requests for a compressed response (`Accept-Encoding` header) and decompresses the compressed response.
   846  
   847  ```sh
   848  $ curl -X POST --compressed -H "Content-Type: application/graphql+-" localhost:8080/query -d $'schema {}'
   849  ```
   850  {{% /notice %}}
   851  
   852  ### Health Check and Alpha Info
   853  
   854  `/health` returns HTTP status code 200 if the worker is running, HTTP 503 otherwise.
   855  The body of the response contains information about the running alpha and its version.
   856  
   857  ```sh
   858  $ curl localhost:8080/health
   859  ```
   860  
   861  ```json
   862  {
   863    "version": "v1.1.0",
   864    "instance": "alpha",
   865    "uptime": 1928423
   866  }
   867  ```
   868  
   869  Here, `uptime` is in nanoseconds (type `time.Duration` in Go).
   870  
   871  ### Run a query in JSON format
   872  
   873  The HTTP API also accepts requests in JSON format. For queries you have the keys "query" and "variables". The JSON format is required to set [GraphQL Variables]({{< relref "query-language/index.md#graphql-variables" >}}) with the HTTP API.
   874  
   875  This query:
   876  
   877  ```
   878  {
   879    balances(func: anyofterms(name, "Alice Bob")) {
   880      uid
   881      name
   882      balance
   883    }
   884  }
   885  ```
   886  
   887  Should be escaped to this:
   888  
   889  ```sh
   890  curl -H "Content-Type: application/json" localhost:8080/query -XPOST -d '{
   891      "query": "{\n balances(func: anyofterms(name, \"Alice Bob\")) {\n uid\n name\n balance\n }\n }"
   892  }' | python -m json.tool | jq
   893  ```