# GraphQL server (`/graphql`) ## What is GraphQL? From [graphql.org](https://graphql.org/): > GraphQL is a query language for APIs and a runtime for fulfilling those queries. 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. Features: - Ask for what you need, get exactly that. - Get many resources in a single request. - Describe what’s possible with a clear schema. ## Why GraphQL? GitHub provided a very concise blog of why they switched to GraphQL: > GraphQL represents a massive leap forward for API development. > Type safety, introspection, generated documentation, and predictable responses benefit both the maintainers and consumers of our platform. More so than this, GraphQL maps very well with the data structure of an AiiDA profile, and makes it very intuitive for clients to construct complex queries, for example: ```graphql { aiidaVersion aiidaEntryPointGroups nodes(filters: "node_type LIKE '%Calc%' & mtime >= 2018-02-01") { count rows(limit: 10, offset: 10) { uuid node_type mtime incoming { count } outgoing { count } } } } ``` ````{dropdown} Example response ```json { "data": { "aiidaVersion": "1.6.3", "aiidaEntryPointGroups": [ "aiida.calculations", "aiida.cmdline.data", "aiida.cmdline.data.structure.import", "aiida.cmdline.computer.configure", "aiida.data", "aiida.groups", "aiida.node", "aiida.parsers", "aiida.schedulers", "aiida.tools.calculations", "aiida.tools.data.orbitals", "aiida.tools.dbexporters", "aiida.tools.dbimporters", "aiida.transports", "aiida.workflows" ], "nodes": { "count": 17784, "rows": [ { "uuid": "0f487263-999e-42dd-a71a-66ed6c4d39ba", "node_type": "process.calculation.calcjob.CalcJobNode.", "mtime": "2018-02-03T07:18:35.129525+01:00", "incoming": { "count": 8 }, "outgoing": { "count": 5 } }, { "uuid": "cff1e914-5a34-4930-9429-9dcc6d38feb1", "node_type": "process.calculation.calcfunction.CalcFunctionNode.", "mtime": "2018-02-03T07:18:35.129813+01:00", "incoming": { "count": 2 }, "outgoing": { "count": 3 } }, { "uuid": "2be5f351-6dd7-4a68-9873-4bc1b3b581fb", "node_type": "process.calculation.calcfunction.CalcFunctionNode.", "mtime": "2018-02-03T07:18:35.129843+01:00", "incoming": { "count": 2 }, "outgoing": { "count": 3 } }, { "uuid": "3e4c9793-49f1-4165-85e1-beaba5a1c4f0", "node_type": "process.calculation.calcfunction.CalcFunctionNode.", "mtime": "2018-02-03T07:18:35.130197+01:00", "incoming": { "count": 2 }, "outgoing": { "count": 2 } }, { "uuid": "3c397478-06af-4b76-8de8-a01fa7248d13", "node_type": "process.calculation.calcfunction.CalcFunctionNode.", "mtime": "2018-02-03T07:18:35.130550+01:00", "incoming": { "count": 2 }, "outgoing": { "count": 3 } }, { "uuid": "d872a924-b831-4c91-92a9-59945544cea8", "node_type": "process.calculation.calcjob.CalcJobNode.", "mtime": "2018-02-03T07:18:35.130714+01:00", "incoming": { "count": 8 }, "outgoing": { "count": 4 } }, { "uuid": "dfe24253-9993-4abd-91c3-8ed0f2a4fd6f", "node_type": "process.calculation.calcjob.CalcJobNode.", "mtime": "2018-02-03T07:18:35.131319+01:00", "incoming": { "count": 7 }, "outgoing": { "count": 6 } }, { "uuid": "1feace00-7282-481c-bf6c-51659ef5b115", "node_type": "process.calculation.calcjob.CalcJobNode.", "mtime": "2018-02-03T07:18:35.131384+01:00", "incoming": { "count": 8 }, "outgoing": { "count": 6 } }, { "uuid": "899d7d18-4880-4942-b45b-2885ca341d55", "node_type": "process.calculation.calcfunction.CalcFunctionNode.", "mtime": "2018-02-03T07:18:35.131482+01:00", "incoming": { "count": 2 }, "outgoing": { "count": 1 } }, { "uuid": "483f69f7-1f23-43ce-b9de-1bb5598083f3", "node_type": "process.calculation.calcjob.CalcJobNode.", "mtime": "2018-02-03T07:18:35.132315+01:00", "incoming": { "count": 8 }, "outgoing": { "count": 6 } } ] } } } ``` ```` ## The GraphQL schema The current Graphql schema is: ```{aiida-graphql-schema} ``` ## Data Limits and Pagination The maximum number of rows of data returned is limited. To query this limit use: ```graphql { rowLimitMax } ``` Use the `offset` option in conjunction with `limit` in order to retrieve all the rows of data over multiple requests. For example, for pages of length 50: Page 1: ```graphql { nodes { count rows(limit: 50, offset: 0) { attributes } } } ``` Page 2: ```graphql { nodes { count rows(limit: 50, offset: 50) { attributes } } } ``` ## Filtering The `filters` option for `computers`, `comments`, `groups`, `logs`, `nodes`, and `users`, accepts a `FilterString`, which maps a string to the `filters` input of the `QueryBuilder` (see [the reference table](https://aiida.readthedocs.io/projects/aiida-core/en/v1.6.3/topics/database.html#reference-tables) for more information). For example: ```graphql { nodes(filters: "node_type ILIKE '%Calc%' & mtime >= 2018-02-01") { count } } ``` maps to: ```python QueryBuilder().append(Node, filters={"node_type": {"ilike": "%Calc%"}, "mtime": {">=": datetime(2018, 2, 1, 0, 0)}}).count() ``` The syntax is defined by the following [EBNF Grammar](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form): ````{dropdown} FilterString Syntax ```{literalinclude} ../../../aiida_restapi/static/filter_grammar.lark ``` ```` ## Query Plugins All top-level queries are plugins. A plugin is defined as a `QueryPlugin` object, which simply includes three items: - `name`: The name by which to call the query - `field`: The graphene field to return (see [graphene types reference](https://docs.graphene-python.org/en/latest/types/)) - `resolver`: The function that resolves the field. For example: ```python from aiida_restapi.graphql.plugins import QueryPlugin import graphene as gr def resolver(parent, info): return "halloworld!" myplugin = QueryPlugin( name="myQuery", field=gr.String(description="Return some data"), resolver=resolver ) ``` Would be called like: ```graphql { myQuery } ``` and return: ```json { "myQuery": "halloworld!" } ``` (TODO: loading plugins as entry points) ## REST Migration Guide This section helps AiiDA users migrate API calls between the REST API built into `aiida-core` and the GraphQL API of this plugin. Most of the listed calls are taken from the [aiida-core documentation](https://aiida.readthedocs.io/projects/aiida-core/en/latest/reference/rest_api.html). ### General ```html http://localhost:5000/api/v4/server/endpoints ``` ```html http://localhost:5000/graphql ``` It is important to note that (in contrast to REST) with GraphQL - you select the fields you want to retrieve, and - you can combine multiple queries in one request. ### Nodes ```html http://localhost:5000/api/v4/nodes?id=in=45,56,78 ``` ```graphql { nodes(filters: "id IN 45,56,78") { count rows { id } } } ``` ``` http://localhost:5000/api/v4/nodes?limit=2&offset=8&orderby=-id ``` ```graphql { nodes { rows(limit: 2, offset: 8, orderBy: "id", orderAsc: false) { id } } } ``` ``` http://localhost:5000/api/v4/nodes?attributes=true&attributes_filter=pbc1 ``` ```graphql { nodes { rows { attributes(filter: ["pbc1"]) } } } ``` ```html http://localhost:5000/api/v4/nodes/full_types ``` NOT YET SPECIFICALLY IMPLEMENTED (although this needs further investigation, because full types is basically not documented anywhere) ```html http://localhost:5000/api/v4/nodes/download_formats ``` NOT YET IMPLEMENTED ```html http://localhost:5000/api/v4/nodes/12f95e1c ``` ```graphql { node(uuid: "dee1f869-c45e-40d9-9f9c-f492f4117f13") { uuid } } ``` Partial UUIDs are not yet implemented (but you can also select using `id`). ```html http://localhost:5000/api/v4/nodes/de83b1/links/incoming?limit=2 ``` ```graphql { node(id: 1011) { incoming { rows(limit: 2) { link { label type } node { id label } } } } } ``` ```html http://localhost:5000/api/v4/nodes/de83b1/links/incoming?full_type="data.dict.Dict.|" ``` ```graphql { node(id: 1011) { incoming(filters: "node_type == 'data.dict.Dict.'") { count rows { link { label type } node { id label } } } } } ``` ```html http://localhost:5000/api/v4/nodes/a67fba41/links/outgoing?full_type="data.dict.Dict.|" ``` ```graphql { node(id: 1011) { outgoing(filters: "node_type == 'data.dict.Dict.'") { count rows { link { label type } node { id label } } } } } ``` ```html http://localhost:5000/api/v4/nodes/ffe11/contents/attributes ``` ```graphql { node(uuid: "dee1f869-c45e-40d9-9f9c-f492f4117f13") { attributes } } ``` ```html http://localhost:5000/api/v4/nodes/ffe11/contents/attributes?attributes_filter=append_text,is_local ``` ```graphql { node(uuid: "dee1f869-c45e-40d9-9f9c-f492f4117f13") { attributes(filter: ["append_text", "is_local"]) } } ``` ```html http://localhost:5000/api/v4/nodes/ffe11/contents/comments ``` ```graphql { node(id: 1011) { comments { count rows { content } } } } ``` Repository based queries are not yet implemented: ```html http://localhost:5000/api/v4/nodes/ffe11/repo/list ``` ```html http://localhost:5000/api/v4/nodes/ffe11/repo/contents?filename="aiida.in" ``` ```html http://localhost:5000/api/v4/nodes/fafdsf/download?download_format=xsf ``` ```html http://localhost:5000/api/v4/nodes?mtime>=2019-04-23 ``` ```graphql { nodes(filters: "mtime>=2019-04-23") { count rows { uuid } } } ``` ### Processes NOT YET IMPLEMENTED ```html http://localhost:5000/api/v4/processes/8b95cd85/report http://localhost:5000/api/v4/calcjobs/sffs241j/input_files ``` ### Computers ``` http://localhost:5000/api/v4/computers?limit=3&offset=2&orderby=id ``` ```graphql { computers { count rows(limit: 3, offset: 3, orderBy: "id") { id } } ``` ```html http://localhost:5000/api/v4/computers/5d490d77 ``` ```graphql { computer(uuid: "5d490d77") { label } } ``` ```html http://localhost:5000/api/v4/computers/?scheduler_type=in="slurm","pbs" ``` ```graphql { computers(filters: "scheduler_type IN slurm,pbs") { count rows { scheduler_type } } } ``` ```html http://localhost:5000/api/v4/computers?orderby=+name ``` ```graphql { computers { rows(orderBy: "name") { id } } } ``` ```html http://localhost:5000/api/v4/computers/page/1?perpage=5 ``` ```graphql { computers { rows(limit: 5) { id } } } ``` ### Users ```html http://localhost:5000/api/v4/users/ ``` ```graphql { users { count rows { id } } } ``` ```html http://localhost:5000/api/v4/users/?first_name=ilike="aii%" ``` ```graphql { users(filters: "first_name ILIKE 'aii%'") { count rows { email first_name last_name institution } } } ``` ### Groups ``` http://localhost:5000/api/v4/groups/?limit=10&orderby=-user_id ``` ```graphql { groups { count rows(limit: 10, orderBy: "user_id", orderAsc: false) { id } } } ``` ```html http://localhost:5000/api/v4/groups/a6e5b ``` ```graphql { group(uuid: "a6eb") { id label nodes { count } } } ```