Support DQL variables in mutations

Moved from GitHub dgraph/4615

Posted by F21:

Experience Report

What you wanted to do

I want to perform a mutation in an upsert block using user-provided data safely.

What you actually did

upsert ($name: string){
      query {
        var(func: eq(xid, "http://schema.org/Person")) {
          Type as uid
        }
        var(func: eq(<http://schema.org/name>, "Robin Wright")) {
          Person as uid
        }
      }
      mutation {
          set {
           uid(Person) <xid> "https://www.themoviedb.org/person/32-robin-wright" .
           uid(Person) <http://schema.org/type> uid(Type) .
           uid(Person) <http://schema.org/name> $name .
           uid(Person) <dgraph.type> "Person" .
          }
      }
    }

Why that wasn’t great, with examples

GraphQL variables are not supported in mutations, so it’s impossible to safely mutate user-provided data without error-prone validation.

Any external references to support your case

None at the moment.

1 Like

miko commented :

I also support this request. The rationale is the same as for GraphQL variables in queries: https://docs.dgraph.io/query-language/#graphql-variables

MichaelJCompton commented :

This looks like a reasonable feature request.

I’m tagging it as accepted, which means it will get into our backlog of features to consider.

If we decide to implement it, we’ll post an update here.

I just ran into this as well. Do you know if there has been a decision about whether this will be implemented?

1 Like

@hardik do you have any idea?

1 Like

Just to provide some more context. In my specific case I needed to pass a Uid variable. This is pretty easy to validate before concatenating with the query string.

In cases where the variable is some sort of free-form string originating from a client, it would be very difficult to ensure the upsert query isn’t being injected. Essentially all the same reasons GraphQL variables are supported in standard queries.

But like I said, for Uids this isn’t much of an issue beyond having to prepare the requests a little differently.

1 Like

+1 for this feature.

Would it be possible to make it more clear in docs that upsert variables aren’t currently supported? (I spent a lot of time trying to debug this today when I came across the problem in pydgraph and then reproduced a minimal example using raw curl requests.)

Minimal Example

Set the schema using GraphQL:

# schema.graphql
type Project {
    id: String! @id
    name: String @search(by: [term])
    description: String @search(by: [term])
}

Create the schema:

curl -X POST localhost:8080/admin/schema --data-binary '@schema.graphql'

Create inital data:

curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d $'
    {
      "set": [
        {
          "Project.id": "06b49118-a34a-4254-b1a1-49e3096676aa",
          "Project.name": "test",
          "Project.description": "test",
          "dgraph.type": "Project"
        },
        {
          "Project.id": "8296209e-9e3e-4170-892f-14ca17281eca",
          "Project.name": "test",
          "Project.description": "test",
          "dgraph.type": "Project"
        }
      ]
    }' | jq

Demonstrate the problem:

First example fails with variable

curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d '
{
  "query": "query foo($id: string) { bar(func: eq(Project.id, $id )) {v as uid} }",
  "delete": {
    "uid": "uid(v)"
  },
  "variables": {"$id": "8296209e-9e3e-4170-892f-14ca17281eca"}
}' | jq

The response bar query is blank:

{
  "data": {
    "code": "Success",
    "message": "Done",
    "queries": {
      "bar": []
    },
    "uids": {}
  },
  "extensions": {
    "server_latency": {
      "parsing_ns": 44507,
      "processing_ns": 837772,
      "encoding_ns": 29162,
      "assign_timestamp_ns": 757242,
      "total_ns": 1750146
    },
    "txn": {
      "start_ts": 21328,
      "commit_ts": 21329
    }
  }
}

Second example works with a hard-coded data

curl -H "Content-Type: application/json" -X POST localhost:8080/mutate?commitNow=true -d '
{
  "query": "query foo($id: string) { bar(func: eq(Project.id, \"8296209e-9e3e-4170-892f-14ca17281eca\" )) {v as uid} }",
  "delete": {
    "uid": "uid(v)"
  },
  "variables": {"$id": "8296209e-9e3e-4170-892f-14ca17281eca"}
}' | jq

The response bar query is populated:

{
  "data": {
    "code": "Success",
    "message": "Done",
    "queries": {
      "bar": [
        {
          "uid": "0x4ec9"
        }
      ]
    },
    "uids": {}
  },
  "extensions": {
    "server_latency": {
      "parsing_ns": 85802,
      "processing_ns": 1154781,
      "encoding_ns": 50707,
      "assign_timestamp_ns": 507082,
      "total_ns": 1828617
    },
    "txn": {
      "start_ts": 21309,
      "commit_ts": 21310,
      "preds": [
        "1-Project.description",
        "1-Project.id",
        "1-Project.name",
        "1-dgraph.type"
      ]
    }
  }
}

Hey, this issue is to track DQL Vars on mutations. I think there is one about the RAW HTTP requests. Currently Dgo supports DQL Vars on Upsert Block. The other clients I’m not sure about.

Apologies, I thought this was relevant as the examples are using DQL queries and the mutate endpoint.

It is the same topic, but different things. We have a kind of support for DQL Vars in Upsert Block mutation, but there’s no concept of DQL vars in the mutation block which is the point of the user F21.