Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

https://github.com/victorteokw/graphql-joker

GraphQL Joker is the ultimate GraphQL scaffolding tool.
https://github.com/victorteokw/graphql-joker

Last synced: 7 days ago
JSON representation

GraphQL Joker is the ultimate GraphQL scaffolding tool.

Lists

README

        

# Deprecated

This package is deprecated in favor of [TEO](https://github.com/teocloud/teo).

# GraphQL Joker
[![NPM version][npm-image]][npm-url]
[![Build Status][travis-image]][travis-url]
[![Test Coverage][cov-image]][cov-url]
[![Dependency Status][daviddm-image]][daviddm-url]
[![License][license-image]][license-url]
[![PR Welcome][pr-image]][pr-url]

GraphQL Joker is the ultimate GraphQL scaffolding tool.

It automates coding process to save your precious time, enhance your work and
life experience. In other words, GraphQL Joker write code for you with commands
you specified.

With GraphQL Joker, you can create a full-fledged backend server with your
complex app logic and running API in less than 3 minutes.

# Documentation
* [Motivation](#motivation)
* [Design Concept](#design-concept)
* [Installation](#installation)
* [Create an GraphQL Project](#create-an-graphql-project)
* [Generate Resources](#generate-resources)
* [Primitive Types](#primitive-types)
* [Array Type](#array-type)
* [Reference Types](#reference-types)
* [Upload Type](#upload-type)
* [Type Modifiers](#type-modifiers)
* [Default Values](#default-values)
* [Nested Structure](#nested-structure)
* [Enums](#enums)
* [Reusable Nestables](#reusable-nestables)
* [Destroy Resources](#destroy-resources)
* [Generate Uploader](#generate-uploader)
* [Integrate with Existing Project](#integrate-with-existing-project)
* [Customize GraphQL Joker Behavior](#customize-graphql-joker-behavior)
* [Issues and Helps](#issues-and-helps)
* [Roadmap](#roadmap)
* [License](#license)

# Motivation

When working on GraphQL projects, we need to define database schema and GraphQL
twice. We need to create resolvers for the standardized API. A lot of copying
and pasting are going on. It's not elegant to copy code around and find-replace
all occurrences. It's also error prone. And sometimes causing unnoticeable
errors which wastes time.

Wouldn't be nice if we could have a tool just like Ruby on Rails' scaffold tool
to generate code for us?

# Design Concept

GraphQL Joker is designed to provide features at least Ruby on Rails scaffold
tool has. Aka, generate boilerplate business logic code as much as possible for
you.

However, unlike Ruby on Rails, GraphQL Joker is not a full-fledged framework and
will never provide a framework for you. It focus on business logic generation.
Although GraphQL Joker also has project generation feature, it's trying to hook
up the industry standard and battle-tested libraries and components together for
you. And it's configurable. To split features into small core chunks and make
them combinable and adaptable is a good practice and especially popular in
node.js ecosystem, GraphQL Joker embraces this practice. That's what makes
GraphQL Joker outstanding and what makes GraphQL Joker really a flexible and
configurable scaffolding tool.

# Installation

GraphQL Joker is a general command line tool, thus you should install it
globally.

```bash
npm install -g graphql-joker
```

# Create an GraphQL Project

To create an GraphQL project, use `joker app` command.

```bash
joker app my-new-app
```

This will generate your app in 'my-new-app' folder. If you don't specify app
name, the app will be created at your current working directory.

Options:
- `--port` On which port this app is listening on.
- `--git-init` Automatically run 'git init' after project generated.
- `--skip-install` Do not install dependencies.
- `--eslint-config` Changing the default eslint config.
- `--main` Changing the entry filename.

To change default eslint config being used:

```bash
joker app my-new-app --eslint-config=your-config
```

To automatically run `git init`:

```bash
joker app my-new-app --git-init
```

# Generate Resources

API resource generation is the core feature of GraphQL Joker. It's syntax is
rather simple and extensible. It follows this basic style:

``` bash
joker resource ModelName[/optionalPluralVariableName] \
primitiveField[[:Type[typeModifiers]]:defaultValue]... \
referenceField[[:ReferenceType[typeModifiers]]:foreignKey]...
```

This arguments specification is obscure to see. Let's see some examples.

Let's say you have a model named user, and user has a name, an age and also a
list of posts. And you have a model named post, it has title, content and
author. Just type like this:

``` bash
joker resource User name:String age:Int posts:[Post]:author
joker resource Post title:String content:String author:User
```

Here we specified our first model 'User', with following fields:
- `name` which is a String
- `age` which is an Int
- `posts` which is a list of Posts through foreign key named author

We defined our second model named 'Post', with following fields:
- `title` which is a String
- `content` which is also a String
- `author` which references to User

This creates six files in total, three for User and three for Post. The three
files are mongoose model, GraphQL schema and GraphQL resolver.

The autogenerated models/User.js looks like this:
```js
const mongoose = require('mongoose');
const { Schema } = mongoose;

const userSchema = new Schema({
name: String,
age: Number
}, {
timestamps: true,
collection: 'users'
});

module.exports = mongoose.model('User', userSchema);
```

The autogenerated schemas/User.gql looks like this:
```graphql
type User {
_id: ID!
name: String
age: Int
posts: [Post]
createdAt: Date
updatedAt: Date
}

input UserInput {
name: String
age: Int
}

type Query {
user(_id: ID!): User
users: [User]
}

type Mutation {
createUser(input: UserInput): User
updateUser(_id: ID!, input: UserInput): User
deleteUser(_id: ID!): User
}
```

The autogenerated resolvers/User.js looks like this:
```js
module.exports = {
User: {
async posts(root, _, { Post }) {
return await Post.find({ author: root._id });
}
},
Query: {
async user(root, { _id }, { User }) {
return await User.findById(_id);
},
async users(root, { _ }, { User }) {
return await User.find();
}
},
Mutation: {
async createUser(root, { input }, { User }) {
return await User.create(input);
},
async updateUser(root, { _id, input }, { User }) {
return await (await User.findById(_id)).set(input).save();
},
async deleteUser(root, { _id }, { User }) {
return await (await User.findById(_id)).remove();
}
}
};
```

Besides your schema definition, 5 API are created for you. Those are:
- `users` query all users
- `user` query a user by id
- `createUser` create a new user
- `updateUser` modify an existing user
- `deleteUser` delete an existing user

Now you can CRUD your resources through API.

## Primitive Types

GraphQL Joker supports a wide range of primitive types:
* `String` string type
* `Int` integer type
* `Float` float type
* `Boolean` bool type
* `Date` date type
* `Enum` enum type, the type specifier has a different syntax
* `File` upload typem the type specifier has a different syntax
* `Mixed` mixed type includes string, int, float, boolean, date, array and
objects

When you are defining a field with type mentioned above, GraphQL Joker will
treat them as primitive types. When you refer to a type that is not included in
the list, GraphQL Joker will treat it as a referecing to another model.

``` bash
joker resource User disabled:Boolean name:String description:Mixed spouse:User
```

In the above example, obviously `disabled`, `name` and `description` are
primitive types. `spouse` is a reference type which references to `User`.

## Array Type

Surround a type with a pair of [], you get an array of that type, for example:

```
joker resource User spouse:User friends:[User] favoriteSayings:[String]
```

The field `friends` is an array of `User`s. And the field `favoriteSayings` is
an array of `String`s.

## Reference Types

There are several ways to implement your own reference types.

### one-to-one

The simplest case is one-to-one relation ship.

```bash
joker resource User address:Address
joker resource Address user:User:address
```

In this case, we save the reference into user model, and on address model, we
use the foreign key on user model to fetch the user value.

### one-to-many

We have two ways to implement this relationship.

```bash
joker resource User posts:[Post]:owner
joker resource Post user:User:owner
```

This is the most common case. We save the reference on the 'many' side, and
fetch on the 'many' side model.

```bash
joker resource User posts:[Post]
joker resource Post user:User:[posts]
```

In this case, we are saving the references on the 'one' side, and on 'many'
side, we use a pair of [] to indicate it's an array. Be careful of performance
when you are doing this way.

### many-to-many

In simple cases, we can just do like this.

```bash
joker resource User courses:[Course]
joker resource Course users:[User]:[courses]
```

If there are tons of records, then you may want to use association table.

```bash
joker resource Favorite user:User course:Course
joker resource User courses:[Course]:Favorite
joker resource Course users:[User]:Favorite
```

In this case, we specified a relationship that is have many ... through ...

## Upload Type

To create an uploading field, use `...Uploader` as type name. See the following
example:

```bash
joker resource User avatar:AvatarUploader
```

To create an uploader, see [Generate Uploader](#generate-uploader)

## Type Modifiers

In the real world practices, fields should be validated. For example, You may
want a user's email to match designated format and to be required and unique.
You can specify type modifiers.

``` bash
joker resource User 'email:String/.*@.*\..*/!$'
```

In the above example, `/.*@.*\..*/` means that this field matches this regexp,
`!` means required, and `$` means unique.

Existing type modifiers includes:
- `!` required
- `^` index
- `$` unique
- `/regexp/` string only, matches the regexp or not
- `<=n` max for number types, maxlength for string type
- `>=n` min for number types, minlength for string type

## Default Values

You can specify default value to a primitive field with the following syntax.

``` bash
joker resource Post 'title:String!:Untitled' 'lastUpdate:Date!:`Date.now`'
```

Here, title's default value is `'Untitled'`, and lastUpdate's default value is
`Date.now`. It's a calculated default value, so surround with a pair of
back ticks.

## Nested Structure

To create nested structure, use the following syntax:

``` bash
joker resource User posts:[{ title:String content:String comments:[{ \
commenter:User content:String }] }] email:String password:String settings:{ \
sms:Boolean email:Boolean pushNotification:Boolean }
```

Specify type as `{` or `[{`, you are going into a nested context. All field
defined after this goes into the nested structure. Use plain `}` and `}]` tokens
to jump out the nesting context.

## Enums

To create enum fields, use enum syntax like this:

``` bash
joker resource User 'gender:Enum(male,female)!'
```

## Reusable Nestables

GraphQL Joker supports reusable nestables and referencing them.

``` bash
joker nestable Address line1:String line2:String country:String region:String
joker resource User address:addressSchema name:String
```

Specify the lowercase nestable name append by 'Schema', joker will treat the
type as a subschema reference.

## Destroy Resources

If you mistakenly generated something or you spell something wrongly, use the
'destroy' command to delete the autogenerated files. Just append destroy with
the original command, it automatically destroys the generated content.

``` bash
joker destroy resource User name:String
```

## Generate Uploader

To generate an uploader, use `joker uploader` command.

```bash
joker uploader FileUploader extends AliOSSUploader bucket=your-bucket-name region=your-region
```

This generates an base file uploader for you.

# Integrate with Existing Project

GraphQL Joker is designed to be a generic tool. It does not require a project to
be a GraphQL Joker project.

## Customize GraphQL Joker Behavior

Create a file called `.jokerrc.json` in project's root directory. And filling it
like this:

```json
{
"schemaDir": "graphql",
"resolverDir": "graphql",
"test": false
}
```

GraphQL Joker will generate schema files and resolver files into graphql
directory, and will not generate unit tests.

# Issues and Helps

GraphQL Joker is not mature yet. If you find anything uncomfortable or confuses
you. Any discuss, issue and pull request are welcome.

# Roadmap

GraphQL Joker is an ambitious project and it still has a long way to go.

- Version 0.8
- Basic sequelize support
- Version 0.9
- CLI user experience
- use eslint to transform user generated code if available
- configurability
- dependencies reliability
- Version 0.10
- query filter, sorting and pagination feature

# License

[The GNU General Public License v3.0](license-url).

[npm-image]: https://badge.fury.io/js/graphql-joker.svg
[npm-url]: https://npmjs.org/package/graphql-joker
[travis-image]: https://travis-ci.org/zhangkaiyulw/graphql-joker.svg?branch=master
[travis-url]: https://travis-ci.org/zhangkaiyulw/graphql-joker
[cov-image]: https://codecov.io/gh/zhangkaiyulw/graphql-joker/branch/master/graph/badge.svg
[cov-url]: https://codecov.io/gh/zhangkaiyulw/graphql-joker
[daviddm-image]: https://david-dm.org/zhangkaiyulw/graphql-joker.svg?theme=shields.io
[daviddm-url]: https://david-dm.org/zhangkaiyulw/graphql-joker
[license-image]: https://img.shields.io/github/license/zhangkaiyulw/graphql-joker.svg
[license-url]: https://github.com/zhangkaiyulw/graphql-joker/blob/master/LICENSE
[pr-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
[pr-url]: https://github.com/zhangkaiyulw/graphql-joker/blob/master/CONTRIBUTING.md