Nutmeg Pumpkin Macchiato

    @rxdi/graphqj
    TypeScript icon, indicating that this package has built-in type declarations

    0.7.56 • Public • Published

    @rxdi/graphqj

    Create easy Graphql server from json, yml, graphql, js or ts files

    Features

    • Graphql Voyager available
    • Helps with prototyping MVP
    • In about a minute you have working graphql API
    • Graphiql included in the pack for easy development
    • Watch and rebuild GQL Schema dynamically without restarting server
    • It provides HotRealod of Graphql Schema without rebuilding or restarting application.Everyting is happening on Runtime.

    What is @rxdi/graphqj

    • Tool for creating Graphql backend from Different source for testing purposes and Client MVP's

    What is not @rxdi/graphqj

    • A Production ready server (it is created only for MVP's)

    For production ready server check @gapi/core

    Installation

    npm i -g @rxdi/graphqj

    Configuration

    Define gj.json or execute gj init,

    gj init by default creates basic configuration with json

    Available config templates:

    gj init {advanced | es6 | typescript | jml}

    Basic configuration

    {
      "$mode": "basic",
      "$resolvers": {
        "findUser": {
          "name": "Kristiyan Tachev",
          "email": "test@gmail.com",
          "phone": 414141,
          "arrayOfNumbers": [515151, 412414],
          "arrayOfStrings": ["515151", "412414"]
        }
      }
    }

    Advanced configuration

    {
      "$mode": "advanced",
      "$types": {
        "User": {
          "name": "String",
          "email": "String",
          "phone": "number",
          "arrayOfNumbers": "number[]",
          "arrayOfStrings": "string[]"
        }
      },
      "$args": {
        "UserPayload": {
          "userId":"String!",
          "userId2":"String",
          "userId3":"String!",
          "userId4":"String",
        }
      },
      "$resolvers": {
        "findUser": {
          "type": "User",
          "args": {
            "userId":"String!",
            "userId":"String",
          },
          "resolve": {
            "name": "Kristiyan Tachev",
            "email": "test@gmail.com",
            "phone": 414141,
            "arrayOfNumbers": [515151, 412414],
            "arrayOfStrings": ["515151", "412414"]
          }
        },
        "findUserWithPayloadRequired": {
          "type": "User",
          "args": {
            "payload":"UserPayload!",
          },
          "resolve": {
            "name": "Kristiyan Tachev",
            "email": "test@gmail.com",
            "phone": 414141,
            "arrayOfNumbers": [515151, 412414],
            "arrayOfStrings": ["515151", "412414"]
          }
        },
      }
    }

    Schema:

    type Query {
      findUser: User
      status: StatusQueryType
    }
    
    type StatusQueryType {
      status: String
    }
    
    type User {
      name: String
      email: String
      phone: Int
      arrayOfNumbers: [Int]
    }

    Query:

    query {
      findUser {
        name
        email
        phone
        arrayOfNumbers
        arrayOfStrings
      }
    }

    Result:

    {
      "data": {
        "findUser": {
          "name": "Kristiyan Tachev",
          "email": "test@gmail.com",
          "phone": 414141,
          "arrayOfNumbers": [
            515151,
            412414
          ],
          "arrayOfStrings": [
            "515151",
            "412414"
          ]
        }
      }
    }

    Starting server

    This command will look for gj.{json | js | ts | yml} configuration inside working directory

    gj
    

    Changing port

    Default port is 9000

    gj --port 5000
    

    Hot reload of Bundles (Beta)

    gj --hot-reload
    

    Build client side application inside Configuration file (Beta)

    gj --client
    

    Generating schema.graphql from JSON

    gj --generate
    

    Spawn random PORT on every start

    gj --random
    

    Try experimental HOT Module reload when developing client side application

    gj --client --hot-reload
    

    Advanced configuration

    Typescript

    To be able to run config with typescript you need to install @gapi/cli globally This will transpile our typescript file to javascript and load it automatically

    npm i -g @gapi/cli

    Filename: gj.ts

    export default {
      $mode: 'advanced',
      $types: {
        user: {
          name: 'String',
          email: 'String',
          phone: 'Number',
          arrayOfNumbers: 'Number[]',
          arrayOfStrings: 'String[]'
        }
      },
      $resolvers: {
        findUser: {
          type: 'user',
          args: {
            userId: "String!",
            userId2: "String",
          },
          resolve: async (root, payload: { userId: string; userId2?: string }) => ({
            name: 'Kristiyan Tachev',
            email: 'test@gmail.com',
            phone: 4141423,
            arrayOfNumbers: [515151, 412414],
            arrayOfStrings: ['515151', '412414']
          })
        }
      }
    };

    ES6

    Filename: gj.js

    export default {
      $mode: 'advanced',
      $types: {
        user: {
          name: 'String',
          email: 'String',
          phone: 'Number',
          arrayOfNumbers: 'Number[]',
          arrayOfStrings: 'String[]'
        }
      },
      $resolvers: {
        findUser: {
          type: 'user',
          args: {
            userId: "String!",
            userId2: "String",
          },
          resolve: async (root, payload: { userId: string; userId2?: string }) => ({
            name: 'Kristiyan Tachev',
            email: 'test@gmail.com',
            phone: 4141423,
            arrayOfNumbers: [515151, 412414],
            arrayOfStrings: ['515151', '412414']
          })
        }
      }
    };

    YML

    Filename: gj.yml

    $mode: advanced
    $types:
      User:
        name: String
        email: String
        phone: Number
        arrayOfNumbers: Number[]
        arrayOfStrings: String[]
    
    $args:
      UserPayload:
        userId: String!
        userId2: String
        userId3: String
        userId4: String
    
    $resolvers:
      findUser:
        type: User
        args:
          payload: UserPayload
        resolve:
          name: Kristiyan Tachev
          email: test@gmail.com
          phone: 414141
          arrayOfNumbers: 
            - 515151
            - 412414
          arrayOfStrings:
            - '515151'
            - '412414'
    
      findUser2:
        type: User
        args:
          payload: UserPayload
        resolve:
          name: Kristiyan Tachev
          email: test@gmail.com
          phone: 414141
          arrayOfNumbers: 
            - 515152
            - 412414
          arrayOfStrings:
            - '515151'
            - '412414'

    Loading existing generated schema

    Filename: gj.json

    {
      "$schema": "./schema.graphql"
    }

    Or

    gj --schema ./schema.graphql

    Graphql Voyager

    Open http://localhost:9000/voyager

    Aliases

    graphqj, gg, gj

    Exclude

    Exclude .gj folder inside your .gitignore or .dockerignore files

    Folder .gj is working directory when we store transpiled typescript configuration file

    Experimental 📡

    $mode: advanced
    $directives: ./directives.ts
    $externals:
      - map: 🛰
        file: ./interceptors.ts
      - map: 🛡️
        file: ./guards.ts
      - map: 🕵️
        file: ./modifiers.ts
      - map: 
        file: ./helpers/moment.js
    
    $types:
      User:
        name: String => {🕵️OnlyAdmin}
        email: String => {🛰LoggerInterceptor}
        phone: Number => {🛡️IsLogged}
        arrayOfNumbers: Number[] => {🕵️OnlyAdmin}
        arrayOfStrings: String[]
        createdAt: String => {⌛fromNow}
    
    $args:
      UserPayload:
        userId: String!
        userId2: String
        userId3: String
        userId4: String
    
    $resolvers:
      findUser:
        type: User
        args:
          payload: UserPayload
        resolve:
          name: Kristiyan Tachev
          email: test@gmail.com
          phone: 414141
          arrayOfNumbers:
            - 515151
            - 412414
          arrayOfStrings:
            - '515151'
            - '412414'
    $views:
      home:
        query: findUser
        props: User
        output: UserPayload
        html: |
          <bla-component></bla-component>
          {userId} {name} {email} {phone} {createdAt}
          A rich framework for building applications and services with GraphQL and Apollo inspired by Angular

    Moment helper

    import moment from 'moment';
    
    export function fromNow() {
      return moment('20111031', 'YYYYMMDD').fromNow();
    }

    Chaining multiple $externals is quite easy

    email: String => {🛰LoggerInterceptor} => {🛡️IsLogged} => {🕵️OnlyAdmin}

    Magics

    With Syringe 💉 operator you can inject yml, js, ts, json, graphql and html files,

    $views:
      home:
        query: findUser2
        payload: UserPayload
        html: 💉./my.html

    You can compose anything inside gj.yml

    $mode: advanced
    $directives: ./directives.ts
    $externals:
      - map: 🛰
        file: ./interceptors.ts
      - map: 🛡️
        file: ./guards.ts
      - map: 🕵️
        file: ./modifiers.ts
    
    $types: 💉./types.yml
    $args: 💉./args.yml
    $resolvers: 💉./resolvers.yml
    
    $views:
      home:
        query: findUser2
        payload: UserPayload
        html: 💉./test.html

    Even defining Graphql resolvers is simply easy

    $mode: advanced
    $resolvers:
      findUser:
        type: User
        args:
          payload: UserPayload
        resolve: 💉./findUser.ts

    Resolver

    import { Observable } from 'rxjs';
    import { IUserType } from '@api';
    
    export async function findUser(root, payload, context, info):
      | Promise<Observable<IUserType>>
      | Promise<IUserType>
      | Observable<IUserType>
      | IUserType {
      return {
        name: 'dada',
        email: 'dada',
        phone: 13131,
        arrayOfNumbers: [111, 222],
        arrayOfStrings: ['dada', 'dada']
      };
    }

    Guard

    import { Observable } from 'rxjs';
    
    export async function IsLogged(
      chainable$: Observable<any>,
      root,
      payload,
      context,
      descriptor
    ) {
      if (!context.user) {
        throw new Error('Unauthorized');
      }
    }

    Interceptor

    import { tap } from 'rxjs/operators';
    import { Observable } from 'rxjs';
    
    export async function LoggerInterceptor(
      chainable$: Observable<any>,
      root,
      payload,
      context,
      descriptor
    ) {
      console.log('Before...');
      const now = Date.now();
      return chainable$.pipe(
        tap(() => console.log(`After... ${Date.now() - now}ms`))
      );
    }

    Modifier

    import { map } from 'rxjs/operators';
    import { Observable } from 'rxjs';
    
    export async function OnlyAdmin(
      chainable$: Observable<any>,
      root,
      payload,
      context,
      descriptor
    ) {
      return chainable$.pipe(map(() => null));
    }

    Directives

    import {
      DirectiveLocation,
      GraphQLCustomDirective,
      GraphQLNonNull,
      GraphQLString
    } from '@gapi/core';
    
    export async function toUppercase() {
      return new GraphQLCustomDirective({
        name: 'toUpperCase',
        description: 'change the case of a string to uppercase',
        locations: [DirectiveLocation.FIELD],
        resolve: async resolve => (await resolve()).toUpperCase()
      });
    }
    
    export async function AddTextDirective() {
      return new GraphQLCustomDirective({
        name: 'AddTextDirective',
        description: 'change the case of a string to uppercase',
        locations: [DirectiveLocation.FIELD],
        args: {
          inside: {
            type: new GraphQLNonNull(GraphQLString),
            description: 'the times to duplicate the string'
          },
          outside: {
            type: new GraphQLNonNull(GraphQLString),
            description: 'the times to duplicate the string'
          }
        },
        resolve: async (
          resolve,
          root,
          args
        ) => args.inside + (await resolve()) + args.outside
      });
    }

    Possible query

    {
      findUser {
        name
        email @toUpperCase @AddTextDirective(inside: "dada", outside: "dadada")
        phone
        arrayOfNumbers
        arrayOfStrings
      }
    }

    Omg YML

    Defining javascript function in yml

    $omg: !!js/function >
      function foobar() {
        return 'Wow! JS-YAML Rocks!';
      }

    Defining JS function in resolver

    $resolvers:
      findUser:
        type: User
        args:
          payload: UserPayload
        resolve: !!js/function >
          function foobar(root, payload, context, info) {
            console.log('OMG')
            return {
              "name": "Kristiyan Tachev",
              "email": "test@gmail.com",
              "phone": 414141,
              "arrayOfNumbers": [515151, 412414],
              "arrayOfStrings": ['515151', '412414']
            }
          }

    Possible flows

    seq:
      # Ordered sequence of nodes
      Block style: !!seq
      - Mercury   # Rotates - no light/dark sides.
      - Venus     # Deadliest. Aptly named.
      - Earth     # Mostly dirt.
      - Mars      # Seems empty.
      - Jupiter   # The king.
      - Saturn    # Pretty.
      - Uranus    # Where the sun hardly shines.
      - Neptune   # Boring. No rings.
      - Pluto     # You call this a planet?
      Flow style: !!seq [ Mercury, Venus, Earth, Mars,      # Rocks
                          Jupiter, Saturn, Uranus, Neptune, # Gas
                          Pluto ]                           # Overrated

    Will create the following json object

    {
       "seq":{
          "Block style":[
             "Mercury",
             "Venus",
             "Earth",
             "Mars",
             "Jupiter",
             "Saturn",
             "Uranus",
             "Neptune",
             "Pluto"
          ],
          "Flow style":[
             "Mercury",
             "Venus",
             "Earth",
             "Mars",
             "Jupiter",
             "Saturn",
             "Uranus",
             "Neptune",
             "Pluto"
          ]
       }
    }

    Dependencies can be injected also

    Define inside $externals following:

    $externals:
      - map: 🕵️
        file: ./my-functions.js

    Where ./my-functions.js looks like this

    export async function test() {
      return {};
    }
    export async function test2() {
      return {};
    }
    export async function test3() {
      return {};
    }

    Then you can inject these functions and use them

    findUser:
      deps: [{ provide: 🕵️, map: 'myFunctions'}]
      type: User
      args:
        payload: UserPayload
      resolve: !!js/function >
        function foobar(root, payload, context, info) {
          console.log(this.myFunctions.test()) // {}
          console.log(this.myFunctions.test2()) // {}
          console.log(this.myFunctions.test3()) // {}
          return {
            "name": "Kristiyan Tachev",
            "email": "test@gmail.com",
            "phone": 414141,
            "arrayOfNumbers": [515151, 412414],
            "arrayOfStrings": ['515151', '412414']
          }
        }
    
    findUser2: 💉./resolvers/findUser.resolver.yml

    Possible view configuration

    $mode: advanced
    # $imports:
    #   - 💉./examples/mix/hamburger/server/hamburger.server.module.ts
    # $components:
    #   - 💉./examples/mix/hamburger/client/hamburger.client.module.ts
    
    $types:
      User:
        name: String
        email: String
        phone: Number
        arrayOfNumbers: Number[]
        arrayOfStrings: String[]
        arrayOfStrings2: String[]
        users: User[]
    $args:
      UserPayload:
        name: String!
        pesho: String
    
    $resolvers:
      findUser:
        type: User
        args:
          userId: UserPayload
        resolve: !!js/function >
          function foobar(root, payload, context, info) {
            return {
              "name": "Кристиян Тачев",
              "arrayOfStrings": ["dada", "dada"],
              "email": "kristiqn.tachev@gmail.com",
              "phone": 876667537
            }
          }
    
    $views:
      app:
        components:
        html: |
          <style>
            .spacer {
              flex: 1 3 auto;
            }
            .container {
              display: flex;
            }
            ul {
              list-style-type: none;
              margin: 0;
              padding: 0;
              overflow: hidden;
              background-color: #f3f3f3;
              cursor: pointer;
            }
            li {
              float: left;
            }
            li a {
              display: block;
              color: #666;
              text-align: center;
              padding: 14px 16px;
              text-decoration: none;
            }
            li a:hover:not(.active) {
              background-color: #ddd;
            }
            li a.active {
              color: white;
              background-color: #4caf50;
            }
            .footer {
              position: fixed;
              left: 0;
              bottom: 0;
              width: 100%;
              background-color: #03a9f4;
              color: white;
              text-align: center;
            }
          </style>
          <ul class="container" slot="header">
            <li><a href="/">Home</a></li>
            <li><a href="/gosho">Gosho</a></li>
            <li><a href="/gosho444">Gosho444</a></li>
            <li><a href="/dadada">1</a></li>
            <li><a href="/dadada">2</a></li>
            <li><a href="/dadada">3</a></li>
            <li><a href="/dadada">4</a></li>
            <li><a href="/dadada">5</a></li>
            <span class="spacer"></span>
          </ul>
    
          <div class="footer" slot="footer">
            <p>Footer</p>
          </div>
    
      home:
        query: |
          query findUser {
            findUser {
              name
              email
              phone
              arrayOfStrings
            }
          }
        output: UserPayload
        policy: network-only
        html: |
          Welcome to Home component
          <p>Name: {findUser.name}</p>
          <p>Email: {findUser.email}</p>
          <p>Phone: {findUser.phone}</p>
          {findUser.arrayOfStrings}
          <div>
            <div *let="x" of="findUser.arrayOfStrings">
              <div *template style="background-color: red">
                {{ x }}
                <hamburger-component type="3dx" active="true"></hamburger-component>
              </div>
            </div>
          </div>
    
          <div style="background-color: red">
            <hamburger-component type="3dx" active=true enableBackendStatistics=${true}></hamburger-component>
          </div>
    
      not-found:
        html: |
          Not found
      gosho:
        query: findUser
        html: |
          Welcome to Gosho
          <p>Name: {findUser.name}</p>
          <p>Email: {findUser.email}</p>
          <p>Phone: {findUser.phone}</p>
    
      dadada:
        html: |
          Welcome to Dadada
    

    Keywords

    none

    Install

    npm i @rxdi/graphqj

    DownloadsWeekly Downloads

    54

    Version

    0.7.56

    License

    none

    Unpacked Size

    5.98 MB

    Total Files

    154

    Last publish

    Collaborators

    • rxdi