GraphQL is a standard for declaring data structures and methods of data access that serves as a middleware layer between the client and the server. If you’ve never heard about GraphQL, here is a couple of useful online resources: here, here and here.
In this article, I will tell you how you can use GraphQL in your projects based on InterSystems technologies.
InterSystems platforms currently support several methods of creating client/server applications:
- REST
- WebSocket
- SOAP
So what’s the advantage of GraphQL? What benefits does it provide compared with REST, for example?
GraphQL has several types of requests:
- query - server requests for obtaining data, similar to GET requests recommended for fetching data using REST.
- mutation - this type is responsible for server-side data changes, similar to POST (PUT, DELETE) requests in REST.
Both mutation and query can return data – this comes in handy if you want to request updated data from the server immediately after performing a mutation. - subscriptions - the same query type that will output data. The only difference is that a query is launched by a page rendered on the client side while subscriptions are activated by mutations.
Key features and advantages of GraphQL
It’s up to the client to decide what data should be returned
One of the key features of GraphQL is that the structure and volume of returned data are defined by the client application. The client application specifies what data it wants to receive using a declarative, graph-like structure that closely resembles the JSON format. The response structure corresponds to that of the query.
Here is how a simple GraphQL query looks:
{
Sample_Company {
Name
}
}
A response in the JSON format:
{
"data": {
"Sample_Company": [
{
"Name": "CompuSoft Associates"
},
{
"Name": "SynerTel Associates"
},
{
"Name": "RoboGlomerate Media Inc."
},
{
"Name": "QuantaTron Partners"
}
]
}
}
Single endpoint
When using GraphQL to work with data, we always connect to a single endpoint, GQL server, and get different data by changing the structure, fields, and parameters of our queries. REST, in contrast, uses multiple endpoints.
Let’s compare REST with GraphQL using a simple example:
Let’s assume that we need to load a user’s content. If we are using REST, we need to send three queries to the server:
- Get the user’s data by their id
- Use their id to load their posts
- Use their id to get a list of their followers/subscribers
Below is a REST map corresponding to these queries:
<Route Url="/user/:id" Method="GET" Call="GetUserByID"/>
<Route Url="/user/:id/posts" Method="GET" Call="GetUserPostsByID"/>
<Route Url="/user/:id/follovers" Method="GET" Call="GetUserFolloversByID"/>
In order to get a new data set, we will need to update this REST map with a new endpoint.
GraphQL handles this with a single query. To do that, just specify the following in the request body:
{
operationName: null, //a query can have a name ( query TestName(...){...} )
query: "query {
User(id: "ertg439frjw") {
name
posts {
title
}
followers(last: 3) {
name
}
}
}",
variables: null // initialization of the variables used in the query
}
A REST map corresponding to this query:
<Route Url="/graphql" Method="POST" Call="GraphQL"/>
Note that this is the only endpoint on the server.
Installing GraphQL and GraphiQL
In order to start using GraphQL, you need to complete a few steps:
- Download the latest release from GitHub and import it to the necessary namespace
- Go to the system management portal and create a new web application based on your InterSystems Data Platform product (Caché, Ensemble or IRIS):
- Name - /
- Namespace - for example, SAMPLES
- Handler class - GraphQL.REST.Main
- GraphiQL — a shell for testing GraphQL queries. Download the latest build or build from the source on your own.
- Create a new web application:
- Name - /graphiql
- Namespace - for example, SAMPLES
- Physical path to CSP files - **C:\InterSystems\GraphiQL**
Let’s take a look at the result
Go to the following link in your browser http://localhost:57772/graphiql/index.html (localhost — server, 57772 — port)
I hope everything is clear with the Query and Response namespaces. A Schema is a document that is generated for all stored classes in a namespace.
The schema contains:
- Classes
- Properties, arguments, and their types
- Descriptions of all of the above generated from comments
Let’s take a closer look at a schema for the Sample_Company class:
GraphiQL also supports automatic code completion that can be activated by pressing the Ctrl + Space key combination:
Queries
Queries can be simple and complex for several sets of data. Below is a sample query for data from to different classes, Sample_Person and Sample_Company:
Filtering
At the moment, only strict equality is supported:
Pagination
Pagination is supported through 4 functions that can be combined to achieve the necessary result:
- after: n – all records with id greater than n
- before: n – all records with id smaller than n
- first: n – first n records
- last: n – last n records
Visibility areas
In most situations, the business logic of an application dictates that particular clients only have access to particular namespace classes (role-based permissions). Based on that, you may need to limit class visibility for a client:
- All classes in the namespace (GraphQL.Scope.All)
- Classes inherited from a superclass (GraphQL.Scope.Superclass)
- Classes belonging to a particular package (GraphQL.Scope.Package)
In order to change the method of visibility restriction, open the studio, switch to the necessary namespace, and open the GraphQL.Settings class. It has a SCOPECLASS parameter with the default value of GraphQL.Scope.All — this is the class containing the description of the class visibility interface in the namespace:
To change class visibility restrictions, you need to set one of the values provided above: GraphQL.Scope.Package or GraphQL.Scope.Superclass.
If you picked GraphQL.Scope.Package, you will also need to go to that class and change the value of the Package parameter to the name of the necessary package – for instance, Sample. This will make all the stored classes from this package fully available:
If you picked GraphQL.Scope.Superclass, simply inherit from this class once again in the necessary classes::
Currently supported
Queries:
- Basic
- Embedded objects
- Only many to one relation
- List of simple types
- List of objects
Currently under development
Queries:
- Embedded objects
- Support of relations of all types
- Filtering
- Support of inequalities
Plans
- Mutaions
- Aliases
- Directives
- Fragments
→ Link to the project repository
→ Link to the demo server
Issues Pull Requests are very welcome.
Keep an eye on our project updates!
Just for the record, GraphQL access to Cache has been supported in EWD.js and EWD 3 (the fore-runners to QEWD.js) since 2015:
https://groups.google.com/forum/#!searchin/enterprise-web-developer-comm...
https://groups.google.com/forum/#!searchin/enterprise-web-developer-comm...
And, any stats, Rob? Is it the most popular API in EWD and EWD3? And why it is not supported in QEWD?
As I release my code as Open Source, I've no idea of usage stats. There's growing interest in the use of GraphQL in the community I work within these days, for all sorts of reasons.
QEWD is a re-badging of EWD 3, so yes, it is supported in QEWD - which of course is straightforward since QEWD is JavaScript/Node.js and can just use the Facebook JavaScript GraphQL module.
Just making the point that GraphQL and support for it in Cache isn't something new, as those who follow our work at M/Gateway will know :-)
Rob
Actually I'm impressed how quickly you guys introduce in QEWD new trendy approaches for development. Though there is a difference here: what @Gevorg Arutunyan published supports Caché Objects and I doubt if QEWD does. Is there any Caché Objects support/mapping in QEWD?
QEWD supports Cache Objects for those who wish to use them via the cache.node APIs for Cache Objects.:
https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY...
Your handler logic (which runs in a QEWD Worker process) has access to these APIs via the "this.db" object, eg this.db.invoke_classmethod()
The following post is a bit old and refers to how you used these APIs in EWD.js. However, change the invocation to this.db.xxx() and the examples should work in QEWD:
https://groups.google.com/forum/#!searchin/enterprise-web-developer-comm...
Personally I prefer to use the Cache database as a persistent JSON database / Document Database, via the ewd-document-store abstraction that is used by QEWD, and integrate that storage into JavaScript Objects. But Cache Objects will work with QEWD and GraphQL just fine
Rob
Hi Rob,
I have searched and read through all training materials only for this answer. Probably, it is a good idea to have a separate slide which shows how QEWD can make use of cache object scripts from the handler methods with examples.
This may be useful for:
1. Applications written in Cache object scripts over the years with business logic that is not easy to change in few years of development.
2. Applications using a middle layer and they want to replace it with QEWD to make use of micro-service architecture.
3. New development mainly focusing on back-end logic in Cache database with object oriented programming.
4. And of course, to make use of QEWD while enjoying the Cache object oriented programming style rather than using it as a document store (I think Cache is not mainly used for document store, but because of its value added features and strengths).
Also, to adopt QEWD in Cache based ERP applications, integration of QEWD with Cahce object script is vital.
Any training materials, slides or GitHub source with working examples to use QEWD and cache object scripts is highly recommended.
Thanks,
Jose
After import an error occurred as below.
---------------------------
Studio
---------------------------
ERROR #6301: SAX XML Parser Error: invalid XML encoding declaration '' while processing Anonymous Stream at line 1 offset 32
> ERROR #5490: Error running generator for method 'DispatchMap:GraphQL.REST.AbstractREST'
ERROR: %CSP.REST.cls(DispatchMap) of generated code compiling subclass 'GraphQL.REST.AbstractREST'
> ERROR #5030: An error occurred while compiling class 'GraphQL.REST.AbstractREST'
---------------------------
OK
---------------------------
I am using Cache 2017.2.2.
Any pointers will be a great help.
Thanks,
Strange, as that should not be hit for AbstractREST at all.
Does you class looks the same as in repo?
Just compiled successfully on:
Cache for Windows (x86-64) 2017.2 (Build 744U) Fri Sep 29 2017 10:58:27 EDT
Yes, same version. Just downloaded from repo and imported GraphQL.xml.
Version: Cache for Windows (x86-64) 2017.2.2 (Build 865) Mon Jun 25 2018 10:45:31 EDT
Also in Cache for Windows (x86-64) 2017.2 (Build 744) Fri Sep 29 2017 11:10:05 EDT
and getting the same error when import and compile GraphQL.xml.
You use 8bit Caché. Maybe you need to convert GraphQL.xml into your local encoding.
Or use Unicode Caché.
Thanks Eduard, now it is working in two different versions.
1. Cache for Windows (x86-64) 2017.2.2 (Build 865U_SU) Mon Jul 9 2018 14:31:14 EDT
USER>Write $system.Version.IsUnicode()
1
2. Cache for Windows (x86-64) 2018.1 (Build 179) Tue Sep 4 2018 00:04:51 EDT
USER>Write $system.Version.IsUnicode()
0
Really interesting stuff that I would like to introduce but there seem to be no new developments for a long time now. Has this development fizzled out?
What features are you most interested in?
Unless its not documented, I think he meant features like mutations and subscriptions.
Specially mutations, anything else have a workaround.
Mutations, mainly.
Is GraphQL support, or will it become, an official part of the product?
I have not heard about such plans, maybe when this technology becomes more stable and widely adopted.
Extremely uneducated question:
If we have a global with its nice tree structure (like for example that Persons tree built in Globals Introduction by InterSystems Learning Services) without specific schema (e.g. class definition), would graphql be able to be used to make specific condition/wild-card based queries?
Current implementation resolves GraphQL query into SQL, so classes/tables are required.
This is our advantage as we don't need to write resolvers manually.
That said, if you want to provide custom schema - you can, as our GraphQL implementation includes GraphQL parser so you can use parsed AST to write your own resolver.
This looks very promising. I've got the testing shell up and running returning data from the Sample namespace but how do i "generate a schema for all classes in the namespace" as mentioned in your instructions? My Documentation Explorer just reports "NO SCHEMA AVAILABLE"
What does running this code
write ##class(GraphQL.Utils.Schema).GetSchema().%ToJSON()
return for you?
It generates tons of JSON looking data. Is there a trick to getting the GraphiQL to get to the schema? Any settings or such?
This is the schema.
Do you uses GraphiQL packaged with the repo?
yes, I did
No worries, I got it working after a fresh reinstall. Probably did something wrong the first time around