github.com/dgraph-io/dgraph@v1.2.8/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 [](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 325 age 326 dob 327 married 328 raw 329 friends 330 loc 331 school 332 } 333 334 type Loc { 335 type 336 coords 337 } 338 339 type Institution { 340 name 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 DType: []string{"Person"}, 360 Age: 26, 361 Married: true, 362 Location: loc{ 363 Type: "Point", 364 Coords: []float64{1.1, 2}, 365 }, 366 Dob: &dob, 367 Raw: []byte("raw_bytes"), 368 Friends: []Person{{ 369 Name: "Bob", 370 Age: 24, 371 }, { 372 Name: "Charlie", 373 Age: 29, 374 }}, 375 School: []School{{ 376 Name: "Crown Public School", 377 }}, 378 } 379 380 mu := &api.Mutation{ 381 CommitNow: true, 382 } 383 pb, err := json.Marshal(p) 384 if err != nil { 385 log.Fatal(err) 386 } 387 388 mu.SetJson = pb 389 assigned, err := dg.NewTxn().Mutate(ctx, mu) 390 if err != nil { 391 log.Fatal(err) 392 } 393 394 // Assigned uids for nodes which were created would be returned in the resp.AssignedUids map. 395 variables := map[string]string{"$id": assigned.Uids["alice"]} 396 q := `query Me($id: string){ 397 me(func: uid($id)) { 398 name 399 dob 400 age 401 loc 402 raw_bytes 403 married 404 dgraph.type 405 friend @filter(eq(name, "Bob")){ 406 name 407 age 408 dgraph.type 409 } 410 school { 411 name 412 dgraph.type 413 } 414 } 415 }` 416 417 resp, err := dg.NewTxn().QueryWithVars(ctx, q, variables) 418 if err != nil { 419 log.Fatal(err) 420 } 421 422 type Root struct { 423 Me []Person `json:"me"` 424 } 425 426 var r Root 427 err = json.Unmarshal(resp.Json, &r) 428 if err != nil { 429 log.Fatal(err) 430 } 431 // fmt.Printf("Me: %+v\n", r.Me) 432 // R.Me would be same as the person that we set above. 433 434 fmt.Println(string(resp.Json)) 435 // 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"]}]}]} 436 437 438 ``` 439 440 441 ## Java 442 443 The official Java client [can be found here](https://github.com/dgraph-io/dgraph4j) 444 and it fully supports Dgraph v1.0.x. Follow the instructions in the 445 [README](https://github.com/dgraph-io/dgraph4j#readme) 446 to get it up and running. 447 448 We also have a [DgraphJavaSample] project, which contains an end-to-end 449 working example of how to use the Java client. 450 451 [DgraphJavaSample]:https://github.com/dgraph-io/dgraph4j/tree/master/samples/DgraphJavaSample 452 453 ## JavaScript 454 455 The official JavaScript client [can be found here](https://github.com/dgraph-io/dgraph-js) 456 and it fully supports Dgraph v1.0.x. Follow the instructions in the 457 [README](https://github.com/dgraph-io/dgraph-js#readme) to get it up and running. 458 459 We also have a [simple example](https://github.com/dgraph-io/dgraph-js/tree/master/examples/simple) 460 project, which contains an end-to-end working example of how to use the JavaScript client, 461 for Node.js >= v6. 462 463 ## Python 464 465 The official Python client [can be found here](https://github.com/dgraph-io/pydgraph) 466 and it fully supports Dgraph v1.0.x and Python versions >= 2.7 and >= 3.5. Follow the 467 instructions in the [README](https://github.com/dgraph-io/pydgraph#readme) to get it 468 up and running. 469 470 We also have a [simple example](https://github.com/dgraph-io/pydgraph/tree/master/examples/simple) 471 project, which contains an end-to-end working example of how to use the Python client. 472 473 ## Unofficial Dgraph Clients 474 475 {{% notice "note" %}} 476 These third-party clients are contributed by the community and are not officially supported by Dgraph. 477 {{% /notice %}} 478 479 ### C\# 480 481 - https://github.com/AlexandreDaSilva/DgraphNet 482 - https://github.com/MichaelJCompton/Dgraph-dotnet 483 484 ### Dart 485 486 - https://github.com/katutz/dgraph 487 488 ### Elixir 489 490 - https://github.com/liveforeverx/dlex 491 - https://github.com/ospaarmann/exdgraph 492 493 ### JavaScript 494 495 - https://github.com/ashokvishwakarma/dgraph-orm 496 - https://github.com/gverse/gverse 497 498 ### Rust 499 500 - https://github.com/Swoorup/dgraph-rs 501 502 ## Raw HTTP 503 504 {{% notice "warning" %}} 505 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. 506 {{% /notice %}} 507 508 It's also possible to interact with Dgraph directly via its HTTP endpoints. 509 This allows clients to be built for languages that don't have access to a 510 working gRPC implementation. 511 512 In the examples shown here, regular command line tools such as `curl` and 513 [`jq`](https://stedolan.github.io/jq/) are used. However, the real intention 514 here is to show other programmers how they could implement a client in their 515 language on top of the HTTP API. For an example of how to build a client on top 516 of gRPC, refer to the implementation of the Go client. 517 518 Similar to the Go client example, we use a bank account transfer example. 519 520 ### Create the Client 521 522 A client built on top of the HTTP API will need to track three pieces of state 523 for each transaction. 524 525 1. A start timestamp (`start_ts`). This uniquely identifies a transaction, 526 and doesn't change over the transaction lifecycle. 527 528 2. The set of keys modified by the transaction (`keys`). This aids in 529 transaction conflict detection. 530 531 Every mutation would send back a new set of keys. The client must merge them 532 with the existing set. Optionally, a client can de-dup these keys while 533 merging. 534 535 3. The set of predicates modified by the transaction (`preds`). This aids in 536 predicate move detection. 537 538 Every mutation would send back a new set of preds. The client must merge them 539 with the existing set. Optionally, a client can de-dup these keys while 540 merging. 541 542 ### Alter the database 543 544 The `/alter` endpoint is used to create or change the schema. Here, the 545 predicate `name` is the name of an account. It's indexed so that we can look up 546 accounts based on their name. 547 548 ```sh 549 $ curl -X POST localhost:8080/alter -d \ 550 'name: string @index(term) . 551 type Person { 552 name 553 }' 554 ``` 555 556 If all goes well, the response should be `{"code":"Success","message":"Done"}`. 557 558 Other operations can be performed via the `/alter` endpoint as well. A specific 559 predicate or the entire database can be dropped. 560 561 To drop the predicate `name`: 562 563 ```sh 564 $ curl -X POST localhost:8080/alter -d '{"drop_attr": "name"}' 565 ``` 566 567 To drop the type `Film`: 568 ```sh 569 $ curl -X POST localhost:8080/alter -d '{"drop_op": "TYPE", "drop_value": "Film"}' 570 ``` 571 572 To drop all data and schema: 573 ```sh 574 $ curl -X POST localhost:8080/alter -d '{"drop_all": true}' 575 ``` 576 577 To drop all data only (keep schema): 578 ```sh 579 $ curl -X POST localhost:8080/alter -d '{"drop_op": "DATA"}' 580 ``` 581 582 ### Start a transaction 583 584 Assume some initial accounts with balances have been populated. We now want to 585 transfer money from one account to the other. This is done in four steps: 586 587 1. Create a new transaction. 588 589 1. Inside the transaction, run a query to determine the current balances. 590 591 2. Perform a mutation to update the balances. 592 593 3. Commit the transaction. 594 595 Starting a transaction doesn't require any interaction with Dgraph itself. 596 Some state needs to be set up for the transaction to use. The `start_ts` 597 can initially be set to 0. `keys` can start as an empty set. 598 599 **For both query and mutation if the `start_ts` is provided as a path parameter, 600 then the operation is performed as part of the ongoing transaction. Otherwise, a 601 new transaction is initiated.** 602 603 ### Run a query 604 605 To query the database, the `/query` endpoint is used. Remember to set the `Content-Type` header 606 to `application/graphql+-` in order to ensure that the body of the request is correctly parsed. 607 608 To get the balances for both accounts: 609 610 ```sh 611 $ curl -H "Content-Type: application/graphql+-" -X POST localhost:8080/query -d $' 612 { 613 balances(func: anyofterms(name, "Alice Bob")) { 614 uid 615 name 616 balance 617 } 618 }' | jq 619 620 ``` 621 622 The result should look like this: 623 624 ```json 625 { 626 "data": { 627 "balances": [ 628 { 629 "uid": "0x1", 630 "name": "Alice", 631 "balance": "100" 632 }, 633 { 634 "uid": "0x2", 635 "name": "Bob", 636 "balance": "70" 637 } 638 ] 639 }, 640 "extensions": { 641 "server_latency": { 642 "parsing_ns": 70494, 643 "processing_ns": 697140, 644 "encoding_ns": 1560151 645 }, 646 "txn": { 647 "start_ts": 4, 648 } 649 } 650 } 651 ``` 652 653 Notice that along with the query result under the `data` field is additional 654 data in the `extensions -> txn` field. This data will have to be tracked by the 655 client. 656 657 For queries, there is a `start_ts` in the response. This `start_ts` will need to 658 be used in all subsequent interactions with Dgraph for this transaction, and so 659 should become part of the transaction state. 660 661 ### Run a Mutation 662 663 Now that we have the current balances, we need to send a mutation to Dgraph 664 with the updated balances. If Bob transfers $10 to Alice, then the RDFs to send 665 are: 666 667 ``` 668 <0x1> <balance> "110" . 669 <0x1> <dgraph.type> "Balance" . 670 <0x2> <balance> "60" . 671 <0x2> <dgraph.type> "Balance" . 672 ``` 673 674 Note that we have to refer to the Alice and Bob nodes by UID in the RDF format. 675 676 We now send the mutations via the `/mutate` endpoint. We need to provide our 677 transaction start timestamp as a path parameter, so that Dgraph knows which 678 transaction the mutation should be part of. We also need to set `Content-Type` 679 header to `application/rdf` in order to specify that mutation is written in 680 rdf format. 681 682 ```sh 683 $ curl -H "Content-Type: application/rdf" -X POST localhost:8080/mutate?startTs=4 -d $' 684 { 685 set { 686 <0x1> <balance> "110" . 687 <0x1> <dgraph.type> "Balance" . 688 <0x2> <balance> "60" . 689 <0x2> <dgraph.type> "Balance" . 690 } 691 } 692 ' | jq 693 ``` 694 695 The result: 696 697 ```json 698 { 699 "data": { 700 "code": "Success", 701 "message": "Done", 702 "uids": {} 703 }, 704 "extensions": { 705 "server_latency": { 706 "parsing_ns": 50901, 707 "processing_ns": 14631082 708 }, 709 "txn": { 710 "start_ts": 4, 711 "keys": [ 712 "2ahy9oh4s9csc", 713 "3ekeez23q5149" 714 ], 715 "preds": [ 716 "1-balance" 717 ] 718 } 719 } 720 } 721 ``` 722 723 We get some `keys`. These should be added to the set of `keys` stored in the 724 transaction state. We also get some `preds`, which should be added to the set of 725 `preds` stored in the transaction state. 726 727 ### Committing the transaction 728 729 {{% notice "note" %}} 730 It's possible to commit immediately after a mutation is made (without requiring 731 to use the `/commit` endpoint as explained in this section). To do this, add 732 the parameter `commitNow` in the URL `/mutate?commitNow=true`. 733 {{% /notice %}} 734 735 Finally, we can commit the transaction using the `/commit` endpoint. We need the 736 `start_ts` we've been using for the transaction along with the `keys` and the 737 `preds`. If we had performed multiple mutations in the transaction instead of 738 just one, then the keys and preds provided during the commit would be the union 739 of all keys and preds returned in the responses from the `/mutate` endpoint. 740 741 The `preds` field is used to abort the transaction in cases where some of the 742 predicates are moved. This field is not required and the `/commit` endpoint also 743 accepts the old format, which was a single array of keys. 744 745 ```sh 746 $ curl -X POST localhost:8080/commit?startTs=4 -d $' 747 { 748 "keys": [ 749 "2ahy9oh4s9csc", 750 "3ekeez23q5149" 751 ], 752 "preds": [ 753 "1-balance" 754 ] 755 }' | jq 756 ``` 757 758 The result: 759 760 ```json 761 { 762 "data": { 763 "code": "Success", 764 "message": "Done" 765 }, 766 "extensions": { 767 "txn": { 768 "start_ts": 4, 769 "commit_ts": 5 770 } 771 } 772 } 773 ``` 774 The transaction is now complete. 775 776 If another client were to perform another transaction concurrently affecting 777 the same keys, then it's possible that the transaction would *not* be 778 successful. This is indicated in the response when the commit is attempted. 779 780 ```json 781 { 782 "errors": [ 783 { 784 "code": "Error", 785 "message": "Transaction has been aborted. Please retry." 786 } 787 ] 788 } 789 ``` 790 791 In this case, it should be up to the user of the client to decide if they wish 792 to retry the transaction. 793 794 ### Aborting the transaction 795 To abort a transaction, use the same `/commit` endpoint with the `abort=true` parameter 796 while specifying the `startTs` value for the transaction. 797 798 ```sh 799 $ curl -X POST "localhost:8080/commit?startTs=4&abort=true" | jq 800 ``` 801 802 The result: 803 804 ```json 805 { 806 "code": "Success", 807 "message": "Done" 808 } 809 ``` 810 811 ### Compression via HTTP 812 813 Dgraph supports gzip-compressed requests to and from Dgraph Alphas for `/query`, `/mutate`, and `/alter`. 814 815 Compressed requests: To send compressed requests, set the HTTP request header 816 `Content-Encoding: gzip` along with the gzip-compressed payload. 817 818 Compressed responses: To receive gzipped responses, set the HTTP request header 819 `Accept-Encoding: gzip` and Alpha will return gzipped responses. 820 821 Example of a compressed request via curl: 822 823 ```sh 824 $ curl -X POST \ 825 -H 'Content-Encoding: gzip' \ 826 -H "Content-Type: application/rdf" \ 827 localhost:8080/mutate?commitNow=true --data-binary @mutation.gz 828 ``` 829 830 Example of a compressed request via curl: 831 832 ```sh 833 $ curl -X POST \ 834 -H 'Accept-Encoding: gzip' \ 835 -H "Content-Type: application/graphql+-" \ 836 localhost:8080/query -d $'schema {}' | gzip --decompress 837 ``` 838 839 Example of a compressed request and response via curl: 840 841 ```sh 842 $ zcat query.gz # query.gz is gzipped compressed 843 { 844 all(func: anyofterms(name, "Alice Bob")) { 845 uid 846 balance 847 } 848 } 849 ``` 850 851 ```sh 852 $ curl -X POST \ 853 -H 'Content-Encoding: gzip' \ 854 -H 'Accept-Encoding: gzip' \ 855 -H "Content-Type: application/graphql+-" \ 856 localhost:8080/query --data-binary @query.gz | gzip --decompress 857 ``` 858 859 {{% notice "note" %}} 860 Curl has a `--compressed` option that automatically requests for a compressed response (`Accept-Encoding` header) and decompresses the compressed response. 861 862 ```sh 863 $ curl -X POST --compressed -H "Content-Type: application/graphql+-" localhost:8080/query -d $'schema {}' 864 ``` 865 {{% /notice %}} 866 867 ### Health Check and Alpha Info 868 869 `/health` returns HTTP status code 200 if the worker is running, HTTP 503 otherwise. 870 The body of the response contains information about the running alpha and its version. 871 872 ```sh 873 $ curl localhost:8080/health 874 ``` 875 876 ```json 877 { 878 "version": "v1.1.0", 879 "instance": "alpha", 880 "uptime": 1928423 881 } 882 ``` 883 884 Here, `uptime` is in nanoseconds (type `time.Duration` in Go). 885 886 ### Run a query in JSON format 887 888 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. 889 890 This query: 891 892 ``` 893 { 894 balances(func: anyofterms(name, "Alice Bob")) { 895 uid 896 name 897 balance 898 } 899 } 900 ``` 901 902 Should be escaped to this: 903 904 ```sh 905 curl -H "Content-Type: application/json" localhost:8080/query -XPOST -d '{ 906 "query": "{\n balances(func: anyofterms(name, \"Alice Bob\")) {\n uid\n name\n balance\n }\n }" 907 }' | python -m json.tool | jq 908 ```