# fastGQL **Repository Path**: www.lyc.com/fastGQL ## Basic Information - **Project Name**: fastGQL - **Description**: Realtime GraphQL on PostgreSQL / MySQL - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: dev - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 3 - **Created**: 2021-07-30 - **Last Updated**: 2021-07-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## fastGQL Realtime GraphQL on PostgreSQL / MySQL ### About This project exposes PostgreSQL / MySQL through GraphQL API, with built-in authorization engine. Inspired by [Hasura](https://hasura.io/). This is alpha-version, with some features still in development: - [x] basic operations - [x] query - [x] mutate - [x] insert - [ ] update - [ ] delete - [ ] subscription - [x] authorization engine configured with custom interceptor - [x] user-defined filtering via GraphQL arguments - [x] where - [x] order by - [x] limit - [x] offset - [x] distinct on(PostgreSQL: distinct on, MySQL: distinct) - [x] integration [graphql-spqr](https://github.com/leangen/graphql-spqr) ### Example #### Database ```postgresql CREATE TABLE addresses( id INT PRIMARY KEY, street VARCHAR(255) ); CREATE TABLE customers( id INT PRIMARY KEY, name VARCHAR(255), address INT REFERENCES addresses(id) ); INSERT INTO addresses VALUES (0, 'Danes Hill'); INSERT INTO addresses VALUES (1, 'Rangewood Road'); INSERT INTO customers VALUES (0, 'Stacey', 0); INSERT INTO customers VALUES (1, 'John', 0); INSERT INTO customers VALUES (2, 'Daniele', 1); ``` #### Simple query
Permissions JWT token data
@Data
@Entity
@Table(name = "t_user")
@AuthGroup(permissions = {
    @Permissions(name = "User", value = "query"),
    @Permissions(name = "User_by_pk", value = "query"),
    @Permissions(name = "User_aggregate", value = "update"),
    @Permissions(name = "User_insert", value = "update"),
})
public class User {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id")
  private Integer id;

  @Column(name = "name")
  private String name;
  
  @Column(name = "password")
  private String password;
  
  @Column(name = "create_time")
  private Date createTime;
  
  @Column(name = "test_id")
  @Referencing(refEntity = "Test", refField = "id")
  private Integer testId;
}
{
  "role": "default"
}
Query Response
query {
  customers {
    id
    name
    address
    address_ref {
      id
      street
      customers_on_address {
        id
      }
    }
  }
}
{
  "data": {
    "customers": [
      {
        "id": 0,
        "name": "Stacey",
        "address": 0,
        "address_ref": {
          "id": 0,
          "street": "Danes Hill",
          "customers_on_address": [
            {
              "id": 0
            },
            {
              "id": 1
            }
          ]
        }
      },
      {
        "id": 1,
        "name": "John",
        "address": 0,
        "address_ref": {
          "id": 0,
          "street": "Danes Hill",
          "customers_on_address": [
            {
              "id": 0
            },
            {
              "id": 1
            }
          ]
        }
      },
      {
        "id": 2,
        "name": "Daniele",
        "address": 1,
        "address_ref": {
          "id": 1,
          "street": "Rangewood Road",
          "customers_on_address": [
            {
              "id": 2
            }
          ]
        }
      }
    ]
  }
}
#### Query with arguments
Permissions JWT token data
permissions {
  role ('default') {
    table ('customers') {
      ops ([select]) {
          allow 'id', 'name', 'address'
      }
    }
    table ('addresses') {
      ops ([select]) {
          allow 'id', 'street'
      }
    }
  }
}
{
  "role": "default"
}
Query Response
query {
  customers (
    where:{
      name:{_in:["John", "Daniele"]},
      _or:[
        {id:{_eq:3}},
        {address:{_lt:10}}
      ]
    },
    order_by:{address_ref:{street:desc}}
  ) {
    id
    name
    address
    address_ref {
      id
      street
      customers_on_address (limit:1) {
        id
      }
    }
  }
}
{
  "data": {
    "customers": [
      {
        "id": 2,
        "name": "Daniele",
        "address": 1,
        "address_ref": {
          "id": 1,
          "street": "Rangewood Road",
          "customers_on_address": [
            {
              "id": 2
            }
          ]
        }
      },
      {
        "id": 1,
        "name": "John",
        "address": 0,
        "address_ref": {
          "id": 0,
          "street": "Danes Hill",
          "customers_on_address": [
            {
              "id": 0
            }
          ]
        }
      }
    ]
  }
}
#### Query with row access restrictions `{ it.id }` in this example is a closure which accepts JWT token data, it is equivalent to Java lambda: ```java @FunctionalInterface interface ValueGetter { Object getValue(Map jwtData); } class C { ValueGetter valueGetter = it -> it.get("id"); } ```
Permissions JWT token data
permissions {
  role ('default') {
    table ('customers') {
      ops ([select]) {
          allow 'id', 'name', 'address'
          check 'id' eq { it.id }
      }
    }
    table ('addresses') {
      ops ([select]) {
          allow 'id', 'street'
      }
    }
  }
}
{
  "role": "default",
  "id": 0
}
Query Response
query {
  customers {
    id
    name
    address_ref {
      customers_on_address {
        id
      }
    }
  }
}
{
  "data": {
    "customers": [
      {
        "id": 0,
        "name": "Stacey",
        "address_ref": {
          "customers_on_address": [
            {
              "id": 0
            }
          ]
        }
      }
    ]
  }
}
#### Mutation with preset and validity check
Permissions JWT token data
permissions {
  role ('default') {
    table ('customers') {
      ops ([select]) {
          allow 'id', 'name', 'address'
          check 'id' eq { it.id }
      }
      ops ([insert]) {
          allow 'name', 'address'
          preset 'id' to { it.id }
          check 'address' lt 100
      }
    }
  }
}
{
  "role": "default",
  "id": 3
}
Mutation Response
mutation {
  insert_customers (
    objects:{
      name:"Oscar",
      address:0
    }
  ) {
    affected_rows
  }
}
{
  "data": {
    "insert_customers": {
      "affected_rows": 1
    }
  }
}
Query Response
query {
  customers {
    id
    name
    address
  }
}
{
  "data": {
    "customers": [
      {
        "id": 3,
        "name": "Oscar",
        "address": 0
      }
    ]
  }
}
### Building and running #### Development Start Postgres container: ```shell script scripts/start_postgres.sh ``` Start FastGQL in dev mode with hot reload: ```shell script ./gradlew vertxRun ```` Go to [localhost:8080/graphiql/](http://localhost:8080/graphiql/) or query ```localhost:8080/graphql``` #### Production Build production bundle: ```shell script ./gradlew installDist ``` Execute production version: ```shell script build/install/fastgql/bin/fastgql run --conf src/main/resources/conf-postgres.json dev.fastgql.FastGQL ```