Sgqlc
Simple GraphQL Client
Install / Use
/learn @profusion/SgqlcREADME
sgqlc - Simple GraphQL Client
.. image:: https://github.com/profusion/sgqlc/actions/workflows/ci.yml/badge.svg
:target: https://github.com/profusion/sgqlc/actions/workflows/ci.yml
.. image:: https://coveralls.io/repos/github/profusion/sgqlc/badge.svg?branch=master
:target: https://coveralls.io/github/profusion/sgqlc?branch=master
Introduction
------------
This package offers an easy to use `GraphQL <http://graphql.org>`_
client. It's composed of the following modules:
- ``sgqlc.types``: declare GraphQL in Python, base to generate and
interpret queries. Submodule ``sgqlc.types.datetime`` will
provide bindings for ``datetime`` and ISO 8601, while
``sgqlc.types.relay`` will expose ``Node``, ``PageInfo`` and
``Connection``.
- ``sgqlc.operation``: use declared types to generate and
interpret queries.
- ``sgqlc.endpoint``: provide access to GraphQL endpoints, notably
``sgqlc.endpoint.http`` provides ``HTTPEndpoint`` using
``urllib.request.urlopen()``.
What's GraphQL?
===============
Straight from http://graphql.org:
**A query language for your API**
GraphQL is a query language for APIs and a runtime for fulfilling
those queries with your existing data. GraphQL provides a complete
and understandable description of the data in your API, gives
clients the power to ask for exactly what they need and nothing
more, makes it easier to evolve APIs over time, and enables
powerful developer tools.
It was created by Facebook based on their problems and solutions using
`REST <https://en.wikipedia.org/wiki/Representational_state_transfer>`_
to develop applications to consume their APIs. It was publicly
announced at
`React.js Conf 2015 <https://reactjs.org/blog/2015/02/20/introducing-relay-and-graphql.html>`_
and started to gain traction since then. Right now there are big names
transitioning from REST to GraphQL:
`Yelp <https://www.yelp.com/developers/graphql/guides/intro>`_
`Shopify <https://help.shopify.com/api/storefront-api/graphql>`_
and `GitHub <https://developer.github.com/v4/>`_, that did an
excellent
`post <https://githubengineering.com/the-github-graphql-api/>`_
to explain why they changed.
A short list of advantages over REST:
- Built-in schema, with documentation, strong typing and
introspection. There is no need to use
`Swagger <https://swagger.io>`_ or any other external tools to play
with it. Actually GraphQL provides a standard in-browser IDE for
exploring GraphQL endpoints: https://github.com/graphql/graphiql;
- Only the fields that you want. The queries must explicitly select which
fields are required, and that's all you're getting. If more fields
are added to the type, they **won't break** the API, since the new
fields won't be returned to old clients, as they didn't ask for such
fields. This makes much easier to keep APIs stable and **avoids
versioning**. Standard REST usually delivers all available fields in
the results, and when new fields are to be included, a new API
version is added (reflected in the URL path, or in an HTTP header);
- All data in one request. Instead of navigating hypermedia-driven
RESTful services, like discovering new ``"_links": {"href"...`` and
executing a new HTTP request, with GraphQL you specify nested
queries and let the whole navigation be done by the server. This
reduces latency **a lot**;
- The resulting JSON object matches the given query exactly; if
you requested ``{ parent { child { info } } }``, you're going to
receive the JSON object ``{"parent": {"child": {"info": value }}}``.
From GitHub's
`Migrating from REST to GraphQL <https://developer.github.com/v4/guides/migrating-from-rest/>`_
one can see these in real life::
$ curl -v https://api.github.com/orgs/github/members
[
{
"login": "...",
"id": 1234,
"avatar_url": "https://avatars3.githubusercontent.com/u/...",
"gravatar_id": "",
"url": "https://api.github.com/users/...",
"html_url": "https://github.com/...",
"followers_url": "https://api.github.com/users/.../followers",
"following_url": "https://api.github.com/users/.../following{/other_user}",
"gists_url": "https://api.github.com/users/.../gists{/gist_id}",
"starred_url": "https://api.github.com/users/.../starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/.../subscriptions",
"organizations_url": "https://api.github.com/users/.../orgs",
"repos_url": "https://api.github.com/users/.../repos",
"events_url": "https://api.github.com/users/.../events{/privacy}",
"received_events_url": "https://api.github.com/users/.../received_events",
"type": "User",
"site_admin": true
},
...
]
brings the whole set of member information, however you just want name
and avatar URL::
query {
organization(login:"github") { # select the organization
members(first: 100) { # then select the organization's members
edges { # edges + node: convention for paginated queries
node {
name
avatarUrl
}
}
}
}
}
Likewise, instead of 4 HTTP requests::
curl -v https://api.github.com/repos/profusion/sgqlc/pulls/9
curl -v https://api.github.com/repos/profusion/sgqlc/pulls/9/commits
curl -v https://api.github.com/repos/profusion/sgqlc/issues/9/comments
curl -v https://api.github.com/repos/profusion/sgqlc/pulls/9/reviews
A single GraphQL query brings all the needed information, and just the
needed information::
query {
repository(owner: "profusion", name: "sgqlc") {
pullRequest(number: 9) {
commits(first: 10) { # commits of profusion/sgqlc PR #9
edges {
node { commit { oid, message } }
}
}
comments(first: 10) { # comments of profusion/sgqlc PR #9
edges {
node {
body
author { login }
}
}
}
reviews(first: 10) { # reviews of profusion/sgqlc/ PR #9
edges { node { state } }
}
}
}
}
Motivation to create `sgqlc`
============================
As seen above, writing GraphQL queries is very easy, and it is equally easy to
interpret the results. So **what was the rationale to create sgqlc?**
- GraphQL has its domain-specific language (DSL), and mixing two
languages is always painful, as seen with SQL + Python, HTML +
Python... Being able to write just Python in Python is much
better. Not to say that GraphQL naming convention is closer to
Java/JavaScript, using ``aNameFormat`` instead of Python's
``a_name_format``.
- Navigating dict-of-stuff is a bit painful:
``d["repository"]["pullRequest"]["commits"]["edges"]["node"]``,
since these are valid Python identifiers, we better write:
``repository.pull_request.commits.edges.node``.
- Handling new ``scalar`` types. GraphQL allows one to define new scalar
types, such as ``Date``, ``Time`` and ``DateTime``. Often these are
serialized as ISO 8601 strings and the user must parse them in their
application. We offer ``sgqlc.types.datetime`` to automatically
generate ``datetime.date``, ``datetime.time`` and
``datetime.datetime``.
- Make it easy to write dynamic queries, including nested. As seen,
GraphQL can be used to fetch lots of information in one go; however
if what you need (arguments and fields) changes based on some
variable, such as user input or cached data, then you need to
concatenate strings to compose the final query. This can be error
prone and servers may block you due to invalid queries. Some tools
"solve" this by parsing the query locally before sending it to
server. However usually the indentation is screwed and reviewing it
is painful. We change that approach: use
``sgqlc.operation.Operation`` and it will always generate valid
queries, which can be printed out and properly indented. Bonus point
is that it can be used to later interpret the JSON results into native
Python objects.
- Usability improvements whenever needed. For instance
`Relay <https://facebook.github.io/relay/>`_ published their
`Cursor Connections Specification <https://facebook.github.io/relay/graphql/connections.htm>`_
and its widely used. To load more data, you need to extend the
previous data with newly fetched information, updating not only the
nodes and edges, but also page information. This is done
automatically by ``sgqlc.types.relay.Connection``.
It also helps with code-generation, ``sgqlc-codegen`` can generate both
the classes matching a GraphQL Schema or functions to return
``sgqlc.operation.Operation`` based on executable documents
GraphQL Domain Specific Language (DSL).
Installation
------------
Automatic::
pip install sgqlc
From source using ``pip``::
pip install .
Usage
-----
To reach a GraphQL endpoint using synchronous `HTTPEndpoint` with a
hand-written query (see more at ``examples/basic/01_http_endpoint.py``):
.. code-block:: python
from sgqlc.endpoint.http import HTTPEndpoint
url = 'http://server.com/graphql'
headers = {'Authorization': 'bearer TOKEN'}
query = 'query { ... }'
variables = {'varName': 'value'}
endpoint = HTTPEndpoint(url, headers)
data = endpoint(query, variables)
To make an asynchronous query, use the `httpx` library with `HTTPXEndpoint`
(see more at ``examples/basic/04_httpx_endpoint.py``):
.. code-block:: python
from sgqlc.endpoint.httpx import HTTPXEndpoint
import httpx
async def main():
url = 'http://server.com/graphql'
headers = {'Authorization': 'bearer TOKEN'}
query = 'query { ... }'
variables = {'varName': 'value'}
endpoint = HTTPXEndpoint(url, headers, client=httpx.AsyncClient())
data = await endpoint(query, variables)
However, writing GraphQL queries and later interpretin
Related Skills
node-connect
335.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
claude-opus-4-5-migration
82.5kMigrate prompts and code from Claude Sonnet 4.0, Sonnet 4.5, or Opus 4.1 to Opus 4.5
frontend-design
82.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
model-usage
335.2kUse CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.
