How to call a GraphQL endpoint with plain HTTP

October 10, 2022

Jeremy Sarchet
Full Stack Developer

GraphQL vs REST

In a Web application using GraphQL you work with a special query language with with main methods being query and mutation, whereas in a more conventional REST style Web application, you work with “plain” HTTP methods such as GET, POST, PATCH, DELETE.

It might seem that these two approaches are worlds apart, but under the hood, GraphQL queries are themselves sent over HTTP (GraphQL servers typically handle GET and POST requests).

So, if you have a means for constructing a plain HTTP request, and if you know how to transmit your query within it, you can work with a GraphQL server using plain HTTP.

GraphQL over HTTP

A typical GraphQL server would handle requests such as:

  • GET requests, where the query is passed in via the URL
  • e.g.{user{name}}
  • POST requests of application/json content type where the query is passed in via the JSON body
  • e.g. { "query": "...", "variables": { "fruit": "apple", ... }

So, the task then is to pass your desired GraphQL query (as a valid JSON string) using the query field of the JSON body, and, if your query uses variables, to pass your dictionary of variables using the variables field.

Here’s an example GraphQL query with variables as a POST request, in the command-line via cURL:

curl --request POST \
--url '' \
--header 'Content-Type: application/json' \
--data-raw '{
  "query":"mutation($id: Int!) { createUser(userId: $id) { id } }",
  "variables": {
    "id": 123

⚠️ The query field in the JSON body needs to be a valid JSON string. That means, unfortunately, that it cannot contain newlines, and also means that you need to escape double-quote characters. So, if you have an existing GraphQL query that you want to put into the HTTP request, you’ll need to “flatten” it into one line and escape double-quotes.

Here’s a few good references to read further:

GraphQL docs

  • Serving over HTTP (with more detail on GET and POST requests to a GraphQL server)
  • GraphQL Clients (with example queries via curl on the command line and fetch from a browser)

Apollo blog

Contentful blog


Backstory: Basedash Actions

Actions give you the ability to build and run predefined Web requests using Basedash. You can define an Action to target a given endpoint (on your own server, or a third-party server) and execute a Web request with user-provided parameters. You can read all about it here in our documentation:

When you’re building an Action, you’re essentially defining the recipe for an HTTP request. You specify the HTTP method, the URL, the headers and the body. While we don’t yet offer any special GraphQL affordances in our Action builder, it’s entirely possible to build Actions which target a GraphQL endpoint. While it might not be quite so ergonomic to fit your query into the request body of your Action, it should be functional.

Working with variables

If you want to setup an Action in Basedash which executes a GraphQL query, you have a few options when it comes to working with variables for passing in arguments. Keep in mind, there’s two levels at play:

  • Arguments supplied by the user who runs an Action (which get resolved in the request body of the Action)
  • Basedash uses double-curly bracket delimiters for variables, e.g. {{ userName }}
  • Arguments supplied by the variables field of the request body (which get resolved in the GraphQL query)
  • GraphQL uses a dollar-sign sigil for variables, e.g. $userName

So, there’s variables for your Action, that can be used in the GraphQL query directly, or can be used in the GraphQL variables, which are in turn passed to the query.

Example Action: Assigning credits to a user

Imagine you have a GraphQL server with a mutation for assigning credits to a user, and you want to setup an Action in Basedash so it’s handy for your teammates to use. You want to set it up so that when they run it they’d supply a user’s email and a number of credits to make it happen.

For starters, your action would use the POST HTTP method, and you’d ensure there’s a header of Content-Type: application/json

Your action would have the following inputs:

  • Email: (with the identifier email)
  • Number of credits: (with the identifier numberOfCredits)

But when it comes to defining the body, you have a few options:

  • Interpolate the Action Inputs directly into the query (not making use of GraphQL variables) e.g.

  "query": "mutation { addCredits(email: \"{{ email }}\", qty: \"{{ numberOfCredits }}\") { credits } }"
  • Make use of GraphQL variables and interpolate the Action Inputs into the values in the variables object, e.g.

  "query": "mutation($email: String!, $quantity: Int!) { addCredits(email: $email, qty: $quantity) { credits } }",
  "variables": {
    "email": "{{ email }}",
    "quantity": "{{ numberOfCredits }}"

You can also do a bit of both. You can define a variables field in the request field and interpolate Action inputs into it, at the same time as interpolating Action inputs into the query field. (Recall, anywhere in the body where you have a double-curly-bracket delimited variable such as {{ email }}, it will be interpolated).

Which one do you find cleaner? Which one might be better for your needs? Part of it is just personal preference.

However, if you do setup and use a variables field in the body, then you do potentially get some extra benefits:

  • You might avoid having to use escape character sequences.
  • If you’re running the Action against your own GraphQL server, things may be easier to log and monitor with the benefit of having things parameterized.
  • You can leverage the typechecking of GraphQL variables.
  • Currently Action Inputs in Basedash don’t support any kind of typechecking, so numberOfCredits can’t be ensured to be an integer. But when it’s routed through the $quantity: Int! argument, then GraphQL does ensure it’s an integer.

Here’s a few references:

Example Action: Create an issue in Linear

At Basedash we use Linear as our issue tracking tool. When developing our Action feature set we created a handful of sample Actions for use for development and testing internally. One such sample Action we made targets the Linear GraphQL API and executes a mutation to create a new issue.

First, we set up an API connection like so:

Note that we've defined two HTTP headers: One for authentication, and one for the content type that GraphQL servers expect.

Once the API connection is set up, we can define an action on it, like so:

Note that the headers defined on the API connection are automatically incorporated into this Action.

And here’s the JSON body we used:

  "query":"mutation($title: String! $description: String! $assigneeId: String! $teamId: String!) { issueCreate( input: { title: $title description: $description assigneeId: $assigneeId teamId: $teamId }) { success issue { id title } } }",
  "variables": {
    "title": "{{ issueName }}",
    "description": "{{ description }}",
    "assigneeId": "1234abcd-not-a-real-user-id-56789",
    "teamId": "1234abcd-not-a-real-team-id-56789"

Some things to note:

  • The query field of the JSON body needs to be a single line with escaped double-quotes.
  • The Action takes the inputs issueName and description and results in a Linear issue being created with those values.
  • The GraphQL query also takes an assigneeId and a teamID, and those are baked into the body itself (not interpolated in). One possible way to make this more sophisticated would be to define an Action input so the user who runs the Action can provide an assigneeId.
  • This Action’s body defines both query and a variables fields (recall a few of the advantages to setting things up this way as described in the above example for assigning credits to a user)



Down the road we plan to add more special-purpose functionality for building GraphQL type Actions. The biggest pain-point at the moment is having to “flatten” the query into a single-line JSON string with escaped double quotes. Among other things, we plan to build out a suitable query editor and related UI, so things don’t have to get crammed into the plain HTTP body.

Still, Basedash Actions can be used as they currently are to target GraphQL endpoints, and it should be perfectly functional.

If you work with a GraphQL application you may be accustomed to using a specialized interface or framework such as GraphiQL or Relay. But you can use plain Web requests too.

What is Basedash?

Ship your product faster.
Worry about internal tools less.

No credit card required.

More posts like this