npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@rxdi/graphqj

v0.7.56

Published

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

Downloads

680

Readme

@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": "[email protected]",
      "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": "[email protected]",
        "phone": 414141,
        "arrayOfNumbers": [515151, 412414],
        "arrayOfStrings": ["515151", "412414"]
      }
    },
    "findUserWithPayloadRequired": {
      "type": "User",
      "args": {
        "payload":"UserPayload!",
      },
      "resolve": {
        "name": "Kristiyan Tachev",
        "email": "[email protected]",
        "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": "[email protected]",
      "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: '[email protected]',
        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: '[email protected]',
        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: [email protected]
      phone: 414141
      arrayOfNumbers: 
        - 515151
        - 412414
      arrayOfStrings:
        - '515151'
        - '412414'

  findUser2:
    type: User
    args:
      payload: UserPayload
    resolve:
      name: Kristiyan Tachev
      email: [email protected]
      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: [email protected]
      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": "[email protected]",
          "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": "[email protected]",
        "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": "[email protected]",
          "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