API

GraphQL

Query and change records with GraphQL, using the same permissions and query object as REST.

Each workspace has one GraphQL endpoint. Every collection gets its own fields, named after the collection. Permissions are the same as REST.

Endpoint

POST /api/v1/workspaces/:workspace/graphql

Send query and optional variables as JSON, with your token.

curl -X POST 'http://127.0.0.1:8080/api/v1/workspaces/YOUR_WORKSPACE_ID/graphql' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  --data '{"query":"query ($query: JSON) { records_deals(limit: 20, query: $query) { id title stage amount } }","variables":{"query":{"filters":[{"field":"stage","operator":"eq","value":"Lead"}]}}}'
const res = await fetch(`http://127.0.0.1:8080/api/v1/workspaces/${workspaceId}/graphql`, {
	method: 'POST',
	headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
	body: JSON.stringify({
		query: `query ($query: JSON) {
      records_deals(limit: 20, query: $query) { id title stage amount }
    }`,
		variables: { query: { filters: [{ field: 'stage', operator: 'eq', value: 'Lead' }] } }
	})
});
const { data } = await res.json();
payload, _ := json.Marshal(map[string]any{
	"query": `query ($query: JSON) { records_deals(limit: 20, query: $query) { id title stage amount } }`,
	"variables": map[string]any{"query": map[string]any{
		"filters": []map[string]any{{"field": "stage", "operator": "eq", "value": "Lead"}},
	}},
})
req, _ := http.NewRequest("POST", "http://127.0.0.1:8080/api/v1/workspaces/"+workspaceID+"/graphql", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
res = requests.post(
    f"http://127.0.0.1:8080/api/v1/workspaces/{workspace_id}/graphql",
    headers={"Authorization": f"Bearer {token}"},
    json={
        "query": "query ($query: JSON) { records_deals(limit: 20, query: $query) { id title stage amount } }",
        "variables": {"query": {"filters": [{"field": "stage", "operator": "eq", "value": "Lead"}]}},
    },
)
data = res.json()["data"]

Response:

{
	"data": {
		"records_deals": [
			{
				"id": "adb696e5c90257c2c313d635f892a776",
				"title": "Warehouse analytics",
				"stage": "Lead",
				"amount": 7300000
			}
		]
	},
	"extensions": { "meta": { "request_id": "...", "response_time": 6.3 } }
}

Metadata sits in extensions.meta instead of meta.

Fields per collection

For a collection named deals:

FieldTypeWhat it does
records_deals(limit, offset, query)queryList records. query takes the query object.
record_deals(id)queryGet one record.
create_deals(input)mutationCreate a record.
update_deals(id, input)mutationUpdate some fields.
delete_deals(id)mutationDelete a record.

Joined fields are available as _related when the query has joins.

Mutations

mutation {
	create_deals(input: { title: "Cafe loyalty app", stage: "Lead", amount: 4500000 }) {
		id
	}
}
mutation {
	update_deals(id: "YOUR_RECORD_ID", input: { stage: "Qualified" }) {
		id
		stage
	}
}

Mutations need the same write access as REST. Studio’s API reference shows ready-made queries for each of your collections and a console to run them.

Next steps

Buy me a coffee