tailcall icon indicating copy to clipboard operation
tailcall copied to clipboard

Support for Human-Readable Flattened GraphQL Queries

Open tusharmath opened this issue 8 months ago • 13 comments

Issue Description:

To enhance the readability and potential CDN-friendliness of our GraphQL queries, I propose supporting a flattened, human-readable style of GraphQL queries sent as GET request parameters.

Sample GraphQL Schema:

type User {
  id: Int!
  name: String!
  age: Int!
  address: Address!
}

type Address {
  city: String!
  state: String!
}

type Mutation {
  updateUser(id: Int!, name: String): User!
}

type Query {
  users: [User]
}

Sample GET Request for Query:

Using the sample schema, the traditional GraphQL request would look something like:

{
  user(id: 123) {
    name
    age
    address {
      city
      state
    }
  }
}

With the flattened approach, a curl request would look like:

curl "https://example.com/api/user?id=123&select=name,age,address.city,address.state"
  • The name of the field at query is moved into the route as kebab-case.
  • Arguments such as id to the query is passed a query params.
  • A special select query param is added to control what fields need to be queried for.

Sample POST Request for Mutation:

For mutations, consider the following GraphQL mutation:

mutation {
  updateUser(id: 123, name: "John Doe") {
    name
  }
}

In the flattened style, using a POST request:

curl -X POST "https://example.com/api/update-user?id=123&select=name&name=John%20Doe"

Similar to query in mutation

  • The name of the field at mutation is moved into the route as kebab-case.
  • Arguments such as id to the query is passed a query params.
  • A special select query param is added to control what fields need to be queried for.

Comparison with Persisted Queries:

Persisted queries involve sending a generated ID instead of the entire GraphQL query to the server. This improves performance as smaller requests are sent over the network. However, a significant drawback of persisted queries is that they're not reversible. Once the client sends the persisted query ID, the server must have a mechanism to match the ID to the actual query. If there's a mismatch or if the query associated with the ID is lost, it can't be recreated from the ID alone.

In contrast, our proposed flattened query parameter approach retains human-readability. It makes the content of the request transparent, while still being concise. This human-readable format has potential advantages in debugging and understanding traffic logs, without having to look up persisted query IDs.

Integration with CDN:

CDNs can be set up to cache GET requests based on URL. The flattened, human-readable style allows for effective caching of GraphQL queries at the CDN level. Since the queries are in the URL, CDNs can easily cache the response associated with each unique URL. This is an advantage over POST requests, which are typically not cached by CDNs, leading to potential performance benefits.

Technical Requirements

  • This approach should be controlled via a setting on @server
  • It should have 100% code coverage for tests.

Considerations

  • The select param can conflict with a param that the query needs. A possible solution could be to use a $ prefix for select eg: $select or just $.
  • Query Params could get very long because each field needs to be is specified. A possible solution could be to use * for eg: $select=* or $select=*,address.*. By default address might not be resolved because it's nested unless it's explicitly asked for.

I believe this approach can be a valuable addition, providing a balance between performance, readability, and CDN compatibility. Feedback and further suggestions are welcome.

tusharmath avatar Nov 01 '23 03:11 tusharmath

/bounty 300$

tusharmath avatar Nov 04 '23 08:11 tusharmath

💎 $300 bounty created by tailcallhq 🙋 If you'd like to work on this issue, comment below to get assigned 👉 To claim this bounty, submit a pull request that includes the text /claim #560 somewhere in its body 📝 Before proceeding, please make sure you can receive payouts in your country 💵 Payment arrives in your account 2-5 days after the bounty is rewarded 💯 You keep 100% of the bounty award 🙏 Thank you for contributing to tailcallhq/tailcall! 🙋‍♂️ Join our discord channel if you need help.

algora-pbc[bot] avatar Nov 04 '23 08:11 algora-pbc[bot]

/attempt #560

deepakdinesh1123 avatar Nov 04 '23 09:11 deepakdinesh1123

@deepakdinesh1123: The Tailcall Inc. team prefers to assign a single contributor to the issue rather than let anyone attempt it right away. We recommend waiting for a confirmation from a member before getting started.

algora-pbc[bot] avatar Nov 04 '23 09:11 algora-pbc[bot]

@tusharmath I went through the issue and thought of converting the query parameters to graphql query using a Jinja template

{% for query in queries %}
    query {% if variable %} ({{variable}}:{{type}}) {% endif %} {
        {% for alias in aliases %}
            {{ field_name }} {% if ids %} ({% for id in ids %} {{id}}:{{val}}, {% endfor %}) {% endif %} {
                {% if fields %}
                    {{ fields }}
                {% elif fragment %}
                    ...{{fragment}}
                {% endif %}

            }
        {% endfor %}
    }
{% endfor %}

and use tera to convert this template to graphql query, please let me know if this approach seems okay to you

deepakdinesh1123 avatar Nov 07 '23 04:11 deepakdinesh1123

I think we generate a parsed graphql query ast directly with rust code. The idea is to give better performance than graphql itself. What you are suggesting will create a layer of indirection and hence degradation in performance.

tusharmath avatar Nov 07 '23 04:11 tusharmath

I think we generate a parsed graphql query ast directly with rust code. The idea is to give better performance than graphql itself. What you are suggesting will create a layer of indirection and hence degradation in performance.

So basically you want the query to be converted into GraphQLRequest right?

ssddOnTop avatar Nov 13 '23 10:11 ssddOnTop

@tusharmath The schema can be generated dyanmically inasync_graphql and that can be used to create a graphql_request, please let me know if this approach seems okay to you.

deepakdinesh1123 avatar Nov 14 '23 10:11 deepakdinesh1123

@tusharmath not see any PR can try if assigned to me.

wrath-of-god avatar Nov 16 '23 18:11 wrath-of-god

@wrath-of-god Let me know if you need any help.

tusharmath avatar Nov 18 '23 08:11 tusharmath

@tusharmath no, it is a big change will take sometime.

wrath-of-god avatar Nov 18 '23 12:11 wrath-of-god

This is an important feature for us to close hence assigning it to @ssddOnTop because we didn't see any activity on the issue by @wrath-of-god.

tusharmath avatar Nov 24 '23 11:11 tusharmath

We need to rethink the design of this issue. Closing it for now.

tusharmath avatar Jan 04 '24 06:01 tusharmath