npm

Ready to take your JavaScript development to the next level? Meet npm Enterprise - the ultimate in enterprise JavaScript.Learn more »

@rxdi/graphqj

0.7.30 • 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

Generating schema.graphql from JSON

gj --generate

Spawn random PORT on every start

gj --random

Watch for changes with specific config

gj --config ./gj.{json|js|ts|yml} --watch
gj --config ./gj.yml --watch

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

Keywords

none

install

npm i @rxdi/graphqj

Downloadsweekly downloads

408

version

0.7.30

license

none

homepage

github.com

repository

Gitgithub

last publish

collaborators

  • avatar
Report a vulnerability