{"id":28962761,"url":"https://github.com/selmeci/dgraph-tonic","last_synced_at":"2025-06-24T03:12:10.179Z","repository":{"id":40823071,"uuid":"246580805","full_name":"selmeci/dgraph-tonic","owner":"selmeci","description":"Async/Sync gRPC client for Dgraph DB","archived":false,"fork":false,"pushed_at":"2023-03-14T09:54:41.000Z","size":348,"stargazers_count":84,"open_issues_count":4,"forks_count":8,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-22T03:08:45.513Z","etag":null,"topics":["async","dgraph","dgraph-client","grpc","rust","sync"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/selmeci.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-03-11T13:37:13.000Z","updated_at":"2025-02-01T21:57:31.000Z","dependencies_parsed_at":"2025-04-21T13:47:15.339Z","dependency_job_id":null,"html_url":"https://github.com/selmeci/dgraph-tonic","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/selmeci/dgraph-tonic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/selmeci%2Fdgraph-tonic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/selmeci%2Fdgraph-tonic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/selmeci%2Fdgraph-tonic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/selmeci%2Fdgraph-tonic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/selmeci","download_url":"https://codeload.github.com/selmeci/dgraph-tonic/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/selmeci%2Fdgraph-tonic/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261505039,"owners_count":23168948,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","dgraph","dgraph-client","grpc","rust","sync"],"created_at":"2025-06-24T03:12:09.463Z","updated_at":"2025-06-24T03:12:10.160Z","avatar_url":"https://github.com/selmeci.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# An async/sync rust client for Dgraph DB\n\n[![Build Status](https://travis-ci.org/selmeci/dgraph-tonic.svg?branch=master)](https://travis-ci.org/selmeci/dgraph-tonic)\n[![Latest Version](https://img.shields.io/crates/v/dgraph-tonic.svg)](https://crates.io/crates/dgraph-tonic)\n[![Docs](https://docs.rs/dgraph-tonic/badge.svg)](https://docs.rs/dgraph-tonic)\n[![Coverage Status](https://coveralls.io/repos/github/selmeci/dgraph-tonic/badge.svg?branch=master)](https://coveralls.io/github/selmeci/dgraph-tonic?branch=master)\n\nDgraph async/sync Rust client which communicates with the server using [gRPC](https://grpc.io/)  build with [Tonic](https://github.com/hyperium/tonic).\n\nBefore using this client, it is highly recommended to go through [tour.dgraph.io] and [docs.dgraph.io] to understand how to run and work with Dgraph.\n\n[docs.dgraph.io]: https://docs.dgraph.io\n[tour.dgraph.io]: https://tour.dgraph.io\n\n## Table of contents\n\n- [Installation](#install)\n- [Supported Versions](#supported-versions)\n- [Using a client](#using-a-client)\n  - [Create a client](#create-a-client)\n  - [Create a TLS client](#create-a-tls-client)\n  - [Create a Slash GraphQL client](#create-a-slash-graphql-client)\n  - [Create a Sync client](#create-a-sync-client)\n  - [Alter the database](#alter-the-database)\n  - [Create a transaction](#create-a-transaction)\n  - [Run a mutation](#run-a-mutation)\n  - [Run a query](#run-a-query)\n  - [Running an Upsert: Query + Mutation](#running-an-upsert-query--mutation)\n  - [Running a Conditional Upsert](#running-a-conditional-upsert)\n  - [Commit a transaction](#commit-a-transaction)\n  - [Access Control Lists](#access-control-lists)\n  - [Check version](#check-version)\n- [Examples](#examples)\n- [Integration tests](#integration-tests)\n- [Contributing](#contributing)\n- [Thanks to](#thanks-to)\n\n## Installation\n\n`dgraph-tonic` is available on crates.io. Add the following dependency to your `Cargo.toml`.\n\n```toml\n[dependencies]\ndgraph-tonic = \"0.11\"\n```\n\nDefault feature is `dgraph-1-1`.\n\nAll avaiable features can be activeted with:\n\n```toml\n[dependencies]\ndgraph-tonic = {version = \"0.11\", features = [\"all\"]}\n```\n\nIf you want to use Dgraph v1.0.x, add this dependency:\n\n```toml\n[dependencies]\ndgraph-tonic = { version = \"0.11\", features = [\"dgraph-1-0\"], default-features = false }\n```\n\nSupported features:\n\n- *acl*: Enable client with authentification.\n- *all*: enable tls, acl and sync features with dgraph-1-1\n- *dgraph-1-0*: Enable client for Dgraph v1.0.x\n- *dgraph-1-1*: Enable client for Dgraph v1.1.x and v20.03.x\n- *dgraph-21-03*: Enable client for Dgraph v21.03.x\n- *slash-ql*: Enable client for [Slash GraphQL](https://dgraph.io/slash-graphql) service\n- *tls*: Enable secured TlsClient\n- *sync*: Enable synchronous Client\n\n## Supported Versions\n\nDepending on the version of Dgraph that you are connecting to, you will have to use a different feature of this client (*dgraph-1-0* is default version).\n\n| Dgraph version |      feature       |\n|:--------------:|:------------------:|\n|     1.0.X      |    *dgraph-1-0*    |\n|     1.1.X      |    *dgraph-1-1*    |\n|     1.2.X      |    *dgraph-1-1*    |\n|    20.03.X     |    *dgraph-1-1*    |\n|    21.03.X     |   *dgraph-21-03*   |\n\nNote: Only API breakage from **dgraph-1-0* to *dgraph-1-1* is in the function `MutatedTxn.mutate()`. This function returns a `Assigned` type in *dgraph-1-0* but a `Response` type in *dgraph-1-1*.\n\n## Using a client\n\n### Create a client\n\n`Client` object can be initialised by passing a one endpoint uri or vector of endpoints uris. Connecting to multiple Dgraph servers in the same cluster allows for better distribution of workload.\n\nThe following code snippet shows it with just one endpoint.\n\n```rust\nuse dgraph_tonic::Client;\n\nfn main() {\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n}\n```\n\nor you can initialize new client with a multiple endpoints. Client cannot be create with empty endpoints vector.\n\n```rust\nuse dgraph_tonic::Client;\n\nfn main() {\n  let client = Client::new(vec![\"http://127.0.0.1:9080\",\"http://127.0.0.1:19080\"]).expect(\"Dgraph client\");\n}\n```\n\nClient can be also initialized with custom [endpoint configuration.](https://docs.rs/tonic/0.8.3/tonic/transport/struct.Endpoint.html)\n```rust\nuse dgraph_tonic::{Endpoint, EndpointConfig, Client};\n\nuse std::time::Duration;\n\n#[derive(Debug, Default)]\nstruct EndpointWithTimeout {}\n\nimpl EndpointConfig for EndpointWithTimeout {\n    fn configure_endpoint(\u0026self, endpoint: Endpoint) -\u003e Endpoint {\n        endpoint.timeout(Duration::from_secs(5))\n    }\n}\n\nfn main() {\n  let endpoint_config = EndpointWithTimeout::default();\n  let client = Client::new_with_endpoint_config(\"http://127.0.0.1:19080\",endpoint_config).expect(\"Dgraph client\");\n}\n```\n\n\n### Create a TLS client\n\nAlternatively, secure tls client is avaible in `tls` feature:\n\n```toml\n[dependencies]\ndgraph-tonic = { version = \"0.11\", features = [\"tls\"] }\n```\n\n```rust\nuse dgraph_tonic::TlsClient;\n\n#[tokio::main]\nasync fn main() {\n  let server_root_ca_cert = tokio::fs::read(\"path/to/ca.crt\").await.expect(\"CA cert\");\n  let client_cert = tokio::fs::read(\"path/to/client.crt\").await.expect(\"Client cert\");\n  let client_key = tokio::fs::read(\"path/to/ca.key\").await.expect(\"Client key\");\n  let client = TlsClient::new(\n    vec![\"http://192.168.0.10:19080\", \"http://192.168.0.11:19080\"],\n    server_root_ca_cert,\n    client_cert,\n    client_key)\n  .expect(\"Dgraph TLS client\");\n}\n```\n\nAll certs must be in `PEM` format.\n\n### Multi-tenancy\n\nIn [multi-tenancy](https://dgraph.io/docs/enterprise-features/multitenancy) environments, `dgraph-tonic` provides a new method `login_into_namespace()`, which will allow the users to login to a specific namespace.\n\nIn order to create a client, and make the client login into namespace `123`:\n\n```rust\nuse dgraph_tonic::Client;\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n   let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n   let logged = client.login_into_namespace(\"groot\", \"password\", 123).await.expect(\"Logged into namespace\");\n   Ok(())\n}\n```\n\nIn the example above, the client logs into namespace `123` using username `groot` and password `password`.\nOnce logged in, the client can perform all the operations allowed to the `groot` user of namespace `123`.\n\n### Create a Slash GraphQL client\n\nIf your Slash GraphQL endpoint is `https://app.eu-central-1.aws.cloud.dgraph.io/graphql` than connection endpoint for gRPC client is `http://app.grpc.eu-central-1.aws.cloud.dgraph.io:443`\n\nClient is avaible in `slash-ql` feature:\n\n```toml\n[dependencies]\ndgraph-tonic = { version = \"0.11\", features = [\"slash-ql\"] }\n```\n\n```rust\nuse dgraph_tonic::TlsClient;\n\n#[tokio::main]\nasync fn main() {\n  let client = TlsClient::for_slash_ql(\n    \"http://app.grpc.eu-central-1.aws.cloud.dgraph.io:443\",\n    \"API_KEY\")\n  .expect(\"Slash GraphQL client\");\n}\n```\n\n### Create a Sync client\n\nAlternatively, synchronous clients (Tls, Acl) are avaible with `sync` feature in `dgraph_tonic::sync` module:\n\n```toml\n[dependencies]\ndgraph-tonic = { version = \"0.11\", features = [\"sync\"] }\n```\n\n```rust\nuse dgraph_tonic::sync::{Mutate, Client};\n\nfn main() {\n  let p = Person {\n    uid:  \"_:alice\".into(),\n    name: \"Alice\".into(),\n  };\n  let mut mu = Mutation::new();\n  mu.set_set_json(\u0026p).expect(\"JSON\");\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  let mut txn = client.new_mutated_txn();\n  let response = txn.mutate(mu).expect(\"Mutated\");\n  txn.commit().expect(\"Transaction is commited\");\n}\n```\n\nAll sync clients support same functionalities as async versions.\n\n### Alter the database\n\nTo set the schema, create an instance of `Operation` and use the `Alter` endpoint.\n\n```rust\nuse dgraph_tonic::Client;\n\n#[tokio::main]\nasync fn main() {\n  let op = Operation {\n    schema: \"name: string @index(exact) .\".into(),\n    ..Default::default()\n  };\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  let response = client.alter(op).await.expect(\"Schema set\");\n  //you can set schema directly with\n  client.set_schema(\"name: string @index(exact) .\").await.expect(\"Schema is not updated\");\n}\n```\n\nStarting Dgraph version 20.03.0, indexes can be computed in the background. You can find more details [here](https://docs.dgraph.io/master/query-language/#indexes-in-background).\n\n```rust\nuse dgraph_tonic::Client;\n\n#[tokio::main]\nasync fn main() {\n  let op = Operation {\n    schema: \"name: string @index(exact) .\".into(),\n    ..Default::default()\n  };\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  let response = client.alter(op).await.expect(\"Schema set\");\n  //you can set schema directly with\n  client.set_schema_in_background(\"name: string @index(exact) .\").await.expect(\"Schema is not updated\");\n}\n```\n\n`Operation` contains other fields as well, including `DropAttr` and `DropAll`. `DropAll` is useful if you wish to discard all the data, and start from a clean slate, without bringing the instance down. `DropAttr` is used to drop all the data related to a predicate.\n\nIf you want to drop all data in DB, you can use:\n\n```rust\nuse dgraph_tonic::Client;\n\n#[tokio::main]\nasync fn main() {\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  client.drop_all().await.expect(\"Data not dropped\");\n}\n```\n\n\n### Create a transaction\n\nTransaction is modeled with [The Typestate Pattern in Rust](http://cliffle.com/blog/rust-typestate/). The typestate pattern is an API design pattern that encodes information about an object's run-time state in its compile-time type. This principle allows us to identify some type of errors, like mutation in read only transaction, during compilation. Transaction types are:\n\n- *Default*: can be transformed into ReadOnly, BestEffort, Mutated. Can perform `query` and `query_with_vars` actions.\n- *ReadOnly*: useful to increase read speed because they can circumvent the usual consensus protocol. Can perform `query` and `query_with_vars` actions defined in `Query` trait.\n- *BestEffort*: Read-only queries can optionally be set as best-effort. Using this flag will ask the Dgraph Alpha to try to get timestamps from memory on a best-effort basis to reduce the number of outbound requests to Zero. This may yield improved latencies in read-bound workloads where linearizable reads are not strictly needed. Can permorm `query` and `query_with_vars` actions.\n- *Mutated*: can perform all actions as default transaction and can modify data in DB. Can be created only from default transaction.\n\nClient provides several factory methods for transactions. These operations incur no network overhead.\n\n```rust\nuse dgraph_tonic::Client;\n\n#[tokio::main]\nasync fn main() {\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  let txn = client.new_txn();\n  let read_only = client.new_read_only_txn();\n  let best_effort = client.new_best_effort_txn();\n  let mutated = client.new_mutated_txn();\n}\n\n```\n\nOnly for Mutated transaction must be always called `txn.dicard().await?` or `txn.commit().await?` function before txn variable is dropped.\n\nBecause internal state of transaction depends on used variant (plain, tls, plain + acl, tls + acl x read only, mutated) you should use traits in your structures for transaction parameters.\n```rust\nstruct MyTxn\u003cQ: Query, M: Mutate\u003e {\n    readonly_txn: Q,\n    mutated_txn: M,\n   \n}\n```\n\nor as returning parameter from function\n```rust\nfn my_query_operation() -\u003e impl Query {\n    //your code\n}\n\nfn my_mutation_operation() -\u003e impl Mutate {\n    //your code\n}\n```\n\nIf you cannot use generics or traits object you can use predefined exported transaction types: *Txn, TxnReadOnly, TxnBestEffort, TxnMutated, TxnTls, TxnTlsReadOnly, TxnTlsBestEffort, TxnTlsMutated, TxnAcl, TxnAclReadOnly, TxnAclBestEffort, TxnAclMutated, TxnAclTls, TxnAclTlsReadOnly, TxnAclTlsBestEffort, TxnAclTlsMutated*\n\n### Run a mutation\n\n`txn.mutate(mu).await?` runs a mutation. It takes in a `Mutation` object. You can set the data using JSON or RDF N-Quad format. There exist helper functions for JSON format (`mu.set_set_json(), mu.set_delete_json()`). All mutation operations are defined in `Mutate` trait.\n\nExample:\n\n```rust\nuse dgraph_tonic::{Mutate, Client};\nuse serde::{Serialize, Deserialize};\n\n#[derive(Serialize, Deserialize, Default, Debug)]\nstruct Person {\n  uid: String,\n  name: String,\n}\n\n#[tokio::main]\nasync fn main() {\n  let p = Person {\n    uid:  \"_:alice\".into(),\n    name: \"Alice\".into(),\n  };\n  let mut mu = Mutation::new();\n  mu.set_set_json(\u0026p).expect(\"JSON\");\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  let mut txn = client.new_mutated_txn();\n  let response = txn.mutate(mu).await.expect(\"Mutated\");\n  txn.commit().await.expect(\"Transaction is commited\");\n}\n```\n\nNote: Only API breakage from *dgraph-1-0* to *dgraph-1-1* is in the function `MutatedTxn.mutate()`. This function returns a `Assigned` type in *dgraph-1-0* but a `Response` type in *dgraph-1-1*.\n\nSometimes, you only want to commit a mutation, without querying anything further. In such cases, you can use `txn.mutate_and_commit_now(mu)` to indicate that the mutation must be immediately committed. Txn object is being consumed in this case.\n\nIn `dgraph-1-0` a `Mutation::with_ignored_index_conflict()` can be applied on a `Mutation` object to not run conflict detection over the index, which would decrease the number of transaction conflicts and aborts. However, this would come at the cost of potentially inconsistent upsert operations. This flag is avaliable only in *dgraph-1-0*.\n\n#### How to get assigned UIDs from response\n\nYou can [specify your own key for returned uid](https://dgraph.io/docs/mutations/#setting-literal-values) like:\n\n```rust\nuse dgraph_tonic::{Mutate, Client};\nuse serde::{Serialize, Deserialize};\nuse serde_json::json;\n\n#[tokio::main]\nasync fn main() {\n  let p = json!({\n    \"uid\": \"_:diggy\",\n    \"name\": \"diggy\",\n    \"food\": \"pizza\"\n  });\n  let mut mu = Mutation::new();\n  mu.set_set_json(\u0026p).expect(\"JSON\");\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  let mut txn = client.new_mutated_txn();\n  let response = txn.mutate(mu).await.expect(\"Mutated\");\n  txn.commit().await.expect(\"Transaction is commited\");\n  println!(\"{:?}\", response.uids);\n}\n```\n\nAssigned uids are returned in response property `uids`. Exmple printlns something like:\n\n```json\n{\"diggy\": \"0xc377\"}\n\n```\n\n### Run a query\n\nYou can run a query by calling `txn.query(q)`. You will need to pass in a GraphQL+- query string. If you want to pass an additional map of any variables that you might want to set in the query, call `txn.query_with_vars(q, vars)` with the variables map as second argument. All query operations are defined in `Query` trait.\n\nLet's run the following query with a variable \\$a:\n\n```console\nquery all($a: string) {\n  all(func: eq(name, $a))\n  {\n    uid\n    name\n  }\n}\n```\n\n`Response` provides function `try_into()` which can be used for transforming returned JSON into coresponding struct object which implements serde `Deserialize` traits.\n\n```rust\nuse dgraph_tonic::{Client, Query};\nuse serde::Deserialize;\n\n#[derive(Deserialize, Debug)]\nstruct Person {\n  uid: String,\n  name: String,\n}\n\n#[derive(Deserialize, Debug)]\nstruct Persons {\n  all: Vec\u003cPerson\u003e\n}\n\n#[tokio::main]\nasync fn main() {\n  let q = r#\"query all($a: string) {\n    all(func: eq(name, $a)) {\n      uid\n      name\n    }\n  }\"#;\n  let mut vars = HashMap::new();\n  vars.insert(\"$a\", \"Alice\");\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  let mut txn = client.new_read_only_txn();\n  let response = txn.query_with_vars(q, vars).await.expect(\"Response\");\n  let persons: Persons = resp.try_into().except(\"Persons\");\n  println!(\"Persons: {:?}\", persons);\n}\n```\n\nWhen running a schema query, the schema response is found in the `Schema` field of `Response`.\n\n```rust\nuse dgraph_tonic::Client;\n\n#[tokio::main]\nasync fn main() {\n  let q = r#\"schema(pred: [name]) {\n    type\n    index\n    reverse\n    tokenizer\n    list\n    count\n    upsert\n    lang\n  }\"#;\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  let mut txn = client.new_read_only_txn();\n  let response = txn.query(q).await.expect(\"Response\");\n  println!(\"{:#?}\", response.schema);\n}\n```\n\n#### Stream/Iterator\n\nThis functions are avaiable in `experimental` feature.\n\nSometimes you cannot fetch all desired data from query at once because it can be huge response which can results into gRPC error, etc. . In this case you can transfrom your query into stream `Stream\u003cItem = Result\u003cT, Error\u003e\u003e` (or iterator in sync mode) which will query your data in a chunks with defined capacity. Exist some limitations how you must defined your query:\n\n1. your query must accept `$first: string, $offset: string` input arguments.\n2. items for stream/iterator must be returned in block with `items` name.\n\nStream/Iterator ends when:\n\n- query returns no items,\n- query returns error which is last item returned from stream,\n- query response cannot be deserialized into `Vec\u003cT\u003e`.\n\n```rust\nuse std::collections::HashMap;\nuse failure::Error;\nuse futures::pin_mut;\nuse futures::stream::StreamExt;\nuse dgraph_tonic::{Client, Response, Query};\nuse serde::Deserialize;\n\n#[derive(Deserialize, Debug)]\nstruct Person {\n  uid: String,\n  name: String,\n}\n\n#[tokio::main]\nasync fn main() {\n  let query = r#\"query stream($first: string, $offset: string, $name: string) {\n         items(func: eq(name, $name), first: $first, offset: $offset) {\n             uid\n             name\n         }\n     }\"#;\n  let mut vars = HashMap::new();\n  vars.insert(\"$name\", \"Alice\");\n  let client = client().await;\n  let stream = client.new_read_only_txn().into_stream_with_vars(query, vars, 100);\n  pin_mut!(stream);\n  let alices: Vec\u003cResult\u003cPerson, Error\u003e\u003e = stream.collect().await;\n}\n```\n\nIf you don't want to specify input vars, you can call `client.new_read_only_txn().into_stream(query, 100)`.\n\n_Sync_ read only transaction can be transformed into iterator with `client.new_read_only_txn().into_iter(query, 100)` and `client.new_read_only_txn().into_iter_with_vars(query, vars, 100)`\n\n### Running an Upsert: Query + Mutation\n\nAvaibale since `dgraph-1-1`.\n\nThe `txn.upsert(query, mutation)` function allows you to run upserts consisting of one query and one or more mutations. Query variables could be defined with `txn.upsert_with_vars(query, vars, mutation)`. If you would like to commit upsert operation you can use `txn.upsert_with_vars_and_commit_now()`. Txn object is being consumed in this case.\n\nTo know more about upsert, we highly recommend going through the docs at https://docs.dgraph.io/mutations/#upsert-block.\n\n```rust\nuse dgraph_tonic::{Mutate, Client};\n\n#[tokio::main]\nasync fn main() {\n  let q = r#\"\n    query {\n        user as var(func: eq(email, \"wrong_email@dgraph.io\"))\n    }\"#;\n  let mut mu = Mutation::new();\n  mu.set_set_nquads(r#\"uid(user) \u003cemail\u003e \"correct_email@dgraph.io\" .\"#);\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  let txn = client.new_mutated_txn();\n  // Upsert: If wrong_email found, update the existing data or else perform a new mutation.\n  let response = txn.upsert(q, mu).await.expect(\"Upserted data\");\n  tnx.commit().await.expect(\"Committed\");\n}\n```\n\nYou can upsert with one mutation or vector of mutations. If you would like to commit upsert operation you can use `txn.upsert_and_commit_now()`. Txn object is being consumed in this case.\n\n### Running a Conditional Upsert\n\nAvaibale since `dgraph-1-1`.\n\nThe upsert block allows specifying a conditional mutation block using an `@if` directive. The mutation is executed only when the specified condition is true. If the condition is false, the mutation is silently ignored.\n\nSee more about Conditional Upsert [Here](https://docs.dgraph.io/mutations/#conditional-upsert).\n\n```rust\nuse dgraph_tonic::{Client, Mutate};\n\n#[tokio::main]\nasync fn main() {\n  let q = r#\"\n    query {\n        user as var(func: eq(email, \"wrong_email@dgraph.io\"))\n    }\"#;\n\n  let mut mu = Mutation::new();\n  mu.set_set_nquads(r#\"uid(user) \u003cemail\u003e \"correct_email@dgraph.io\" .\"#);\n  mu.set_cond(\"@if(eq(len(user), 1))\");\n\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  let txn = client.new_mutated_txn();\n  let response = txn.upsert(q, vec![mu]).await.expect(\"Upserted data\");\n  tnx.commit().await.expect(\"Committed\");\n}\n```\n\n### Commit a transaction\n\nA mutated transaction can be committed using the `txn.commit()` method. If your transaction consisted solely of calls to `txn.query` or `txn.query_with_vars`, and no calls to `txn.mutate`, then calling `txn.commit` is not necessary.\n\nAn error will be returned if other transactions running concurrently modify the same data that was modified in this transaction. It is up to the user to retry transactions when they fail.\n\n```rust\nuse dgraph_tonic::{Client, Mutate};\n\n#[tokio::main]\nasync fn main() {\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  let mut txn = client.new_mutated_txn();\n  // Perform some queries and mutations.\n\n  //than commit\n  let res = txn.commit().await;\n  if res.is_err() {\n    // Retry or handle error\n  }\n}\n```\n\n### Access Control Lists\n\nThis enterprise Dgraph feature which can be activated with:\n\n```toml\n[dependencies]\ndgraph-tonic = { version = \"0.11\", features = [\"acl\"] }\n```\n\n[Access Control List (ACL)](https://dgraph.io/docs/enterprise-features/#access-control-lists) provides access protection to your data stored in Dgraph. When the ACL feature is turned on, a client must authenticate with a username and password before executing any transactions, and is only allowed to access the data permitted by the ACL rules.\n\nBoth, `Client` and `TlsClient` can be logged in with `login(user_id,password)` function. Original client is consumed and return instance of `AclClient` which allows token refreshing with `refresh_login()` function.\n\n```rust\nuse dgraph_tonic::Client;\n\n#[tokio::main]\nasync fn main() {\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  let logged_in_client = client.login(\"groot\", \"password\").await.expect(\"Logged in\");\n  // use client\n\n  //than refresh token\n  logged_in_client.refresh_login().await.except(\"Refreshed access token\");\n}\n\n```\n\n### Check version\n\n```rust\nuse dgraph_tonic::Client;\n\n#[tokio::main]\nasync fn main() {\n  let client = Client::new(\"http://127.0.0.1:19080\").expect(\"Dgraph client\");\n  let version = client.check_version().await.expect(\"Version\");\n  println!(\"{:#?}\", version);\n}\n\n```\n\nOutput:\n\n```console\nVersion {\n    tag: \"v20.03.0\",\n}\n```\n\n## Examples\n\n- [simple][]: Quickstart example of using dgraph-tonic.\n- [tls][]: Example of using dgraph-tonic with a Dgraph cluster secured with TLS.\n\n[simple]: ./examples/simple\n[tls]: ./examples/tls\n\n## Integration tests\n\nTests require Dgraph running on `localhost:19080`. For the convenience there are two `docker-compose.yaml` files, depending on Dgraph you are testing against, prepared in the root directory:\n\n```bash\ndocker-compose -f docker-compose-1-X.yaml up -d\n```\n\nSince we are working with a database, tests also need to be run in a single thread to prevent aborts. Feature flags are used depending on version of Dgraph you are using. Eg.:\n\n```bash\ncargo test --no-default-features --features dgraph-1-1 -- --test-threads=1\n```\n\n## Contributing\n\nContributions are welcome. Feel free to raise an issue, for feature requests, bug fixes and improvements.\n\nRun these commands, before you create pull request:\n\n```bash\nrustup component add rustfmt\ncargo fmt\n```\n\n## Release checklist\n\nThese have to be done with both Dgraph 1.0 and Dgraph 1.1+:\n\n- Run tests\n- Try examples\n\nUpdate the version and publish crate:\n\n- Update tag in Cargo.toml\n- Update tag in README.md\n- `git tag v0.X.X`\n- `git push origin v0.X.X`\n- Write release log on GitHub\n- `cargo publish`\n\n## Thanks to\n\n[![eDocu]](https://www.edocu.com/)\n\n[edocu]: ./eDocu_Blue.png","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fselmeci%2Fdgraph-tonic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fselmeci%2Fdgraph-tonic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fselmeci%2Fdgraph-tonic/lists"}