easy-dgraph: Create DGraph GraphQL on the Fly!
Tags: dgraph, graphql, database
Published: 2021-05-31 13:18:43

**UPDATE 1/3/22**

I have created a second package [j-dgraph](https://github.com/jdgamble555/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](https://firebase.google.com/docs/firestore/query-data/get-data) and [Supabase.io](https://supabase.io/docs/reference/javascript/limit), 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](https://github.com/jdgamble555/angular-fire-dgraph) using the package.

**Package Note:** This package was built upon [json-to-graphql-query](https://github.com/dupski/json-to-graphql-query), so view that package for more rules.  To just use that package, simply run:

```typescript
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

```typescript
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](https://dgraph.io/docs/graphql/overview/) 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**...

```typescript
__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](https://github.com/dupski/json-to-graphql-query) for more details.

**VALID Statement**
```javascript
  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
```typescript
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
```typescript
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
```typescript
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:

```typescript
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.

```typescript
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](https://github.com/jdgamble555/easy-dgraph/blob/master/src/lib/easy-dgraph.ts).

### Usage

Post usage questions here, so it can be available to anyone to see.

### Example

See my [angular-fire-dgraph](https://github.com/jdgamble555/angular-fire-dgraph) for an example module.

### Source Code

You can see the [source code](https://github.com/jdgamble555/easy-dgraph/blob/master/src/lib/easy-dgraph.ts) 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:

```typescript
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](https://github.com/jdgamble555/angular-fire-dgraph/blob/master/src/app/core/dgraph.service.ts) for an example on how to make this happen. You need to extend the original module like so: 

```typescript
// 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](https://discuss.dgraph.io/t/announcing-easy-dgraph-create-dgraph-graphql-on-the-fly/14375) 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