Jonathan Gamble
Posted on May 31, 2021
UPDATE 1/3/22
I have created a second package j-dgraph, that has all of the querying built-in like I spoke of below. It uses easy-dgraph and urql in the backend. Check it out! Similar to firebase, supabase, and prisma apis!
UPDATE 6/3/21
I completed simplified the interface! Keep reading!
Have you every had these problems in Cloud DGraph?
- Forgot to update your queries.ts file when you added or changed a field
- Forgot the rules of graphql since add has different sort of input than update etc...
- Got tired of writing overly complicated queries when what you want is simple
- Sick of remembering gql rules, when json works well in Visual Studio
Well, inspired by Firestore and Supabase.io, I wanted all the benefits of graphql (instead of sql or nosql), but all the facility of use of these competitors.
So, I wrote a typescript package that can be imported into any JS or TS Framework (Angular, React, Svelte, Vue, etc...)
Framework Note: I have only tested this in Angular, but I purposely left out automatic urql or apollo to make it simple and customizable. Everyone has their own preferences.
Here is my example angular app: angular-fire-dgraph using the package.
Package Note: This package was built upon json-to-graphql-query, so view that package for more rules. To just use that package, simply run:
new Dgraph().toGQL(q);
This will return the graphql object based on the json object q. You can set json-to-graphql-query options with:
new Dgraph().options({ pretty: true }).toGQL(q);
Or to pretty print any of your code, simply use:
new Dgraph().pretty().toGQL(q);
How it works...
Keep in mind, I did not add complicated methods, as I wanted the essence of graphql still there for understanding.
Install the package:
npm i easy-dgraph
Import the library:
import { Dgraph } from 'easy-dgraph';
Queries
Note: anything starting with __ represents options, not values
const gql = new Dgraph('task').query({
id: 1,
title: 1,
completed: 1,
user: {
email: 1
}
}).build();
So, initiate a new instance with the type you want to query. The keys are the values that you want to return. All query functions take keys for the values to return.
You can have the value equal to anything you want, even true, 0, 1, or false. As long as there is a value and it is valid json.
Type
You can set the type with
new Dgraph('task')
or
new Dgraph().type('task')
Available Query Functions:
These queries match the available GraphQL functions.
- aggregate()
- get()
- query()
- add()
- upsert()
- update()
- delete()
- customMutation()
- customQuery()
Other Functions
- cascade() - input the cascade variables, or no input
- filter() - the filter object, or just the id(s) you want to filter for mutations
- set() - the variables you want to add in json format
- first() - use __first for nested
- order() - use __order for nested
- offset() - use __offset for nested
Internal Functions Available
- operation()
- pretty()
- options()
- toGQL()
- type()
Other Variables
Just like graphql, you can filter, order, first, and offset...
__filter: { id: '0x2323s' },
__order: { desc: createdAt },
__first: 5,
__offset: 10
Because filter and order are pasted directly into graphql and not parsed, anything viable in graphql should be viable in this package. That includes all complex statements.
For nested types, simply use the __ rules. Valid Types:
- __cascade
- __filter
- __order
- __offset
- __first
Or create your own with:
- __args - for arguments
- __directives - for directives
See json-to-graphql-query for more details.
VALID Statement
id: 1,
title: 1,
completed: 1,
user: {
__filter: {
allofterms: "sit"
},
email: 1
}
}
@cascade example
__cascade: true
or
__cascade: {
fields: ['me', you']
}
Generate
After you input your variables, you can either .build()
or .buildSubscription()
for subscriptions...
Mutations
Mutations are easy as well... same variables in the function({ variable: 1 })
, and use filter()
and set()
to mutate.
Options
- filter() is the same thing as __filter. Use the latter for nested objects.
- set() is for adding data.
Add
const gql = new Dgraph('task').set({ title: 'new task', completed: true }).add({
id: true,
title: true,
completed: true,
user: {
email: true
}
}).build();
For upserts, you can use upsert()
instead of add.
Update
const id = '12345';
const gql = new Dgraph('task').filter(id).set({ completed: true }).update({
id: 1,
user: {
email: 1
}
});
Remember you can return whatever values you want using true or 1, whatever your preference is. Here, completed: true
is the value true since it is in the set()
function.
Delete
const gql = new Dgraph('task').filter('0x1234').delete();
If you don't want to return anything, leave delete()
parameters empty, although it does default the return value of msg per the docs.
Custom Queries / Mutations
Custom mutations and queries are also possible. Simply use:
- customMutation(query)
- customQuery(query)
Multiple Queries and Mutations
You can also do multiple queries and mutations. Simply chain the object with a new type()
function like so:
new Dgraph('task').query({ title: 1}).type('task', 'jonathan')
.filter('123').query({ title: 1 }).buildSubscription();
Notice you can input a second variable in type()
for the alias of the same query.
Which will produce the following:
subscription {
queryTask {
title
}
jonathan: queryTask(filter: {id: "123"}) {
title
}
}
If you need to force specifiy the operation, use operation('mutation')
, for example.
Other Options
All the options should be there. @skip and @include are not needed, since these are generated on the fly. All complex statements are just added automatically from your json code, as long as they are supported in dgraph graphql.
Every single type of mutation, subscription, or query should be generatable. Let me know if you see an option I did not notice.
Using the gql object
The gql object is just that, the code in graphql. Because it is written on the fly, you do not need the var input part of graphql. So, simply input the code into your module of choice (urql, apollo, fetch), although urql is highly recommended due to its speed and cache enhancements.
const results = await this.urql.mutation(gql);
Bugs
I imagine there are plenty of bugs, as I just wrote this this weekend. Post an issue on the github repo.
Usage
Post usage questions here, so it can be available to anyone to see.
Example
See my angular-fire-dgraph for an example module.
Source Code
You can see the source code is pretty simple, but powerful.
Even Easier Top Level
You can write a module on top of this module that allows you to do even easier calls with less code like this:
this.dgraph.type('task').query({
id: true,
title: true,
completed: true,
user: {
email: true
}
}).buildSubscription().subscribe((r: any) => {
this.tasks = r;
});
This produces the code, and runs it. See my repo here for an example on how to make this happen. You need to extend the original module like so:
// extend the original dgraph module
export class dgraph extends Dgraph {
constructor(private urql: UrqlModule) {
super();
}
async build() {
if (this._operation === 'mutation') {
return await this.urql.mutation(super.build());
}
return await this.urql.query(super.build());
}
buildSubscription() {
this.operation('subscription');
return this.urql.subscription(super.build());
}
}
Contributing
I would love any ideas for new or simpler features. Send me a pull request, PM me here.
Future Note
This original post on discuss.dgraph.io will be locked after 30 or so days from me to update, so check the forum for updates.
I was not able to list every example here, but you can see it can generate complex statements!
Thanks,
J
Posted on May 31, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.