"By the power combined I am... FullstackJS" 💥 😎
WARNING: this is an ongoing work. Don't use in production yet!
Server Module: folder with module name containing
- gql folder with .gql files
- resolvers.js (Graphql resolvers)
- routes.js (Koa routes)
Client Module: folder with module name containing:
- Routes.js
Role based + Policy based authorization using:
- User - The authenticated object
- Role - Group for permissions and policies
- Resource - Currently supports Graphql resolver and Koa Router's path
- Permission - Access (allow/deny) for Role/Resource
- Policy (or hook) - IN-code function, allows to use input args for fine-grained permissions
- Policy Bypass - Allows to bypass/enforce policies for given roles
- Cache for faster resolution
- KnexJS - For database connections and migrations
- ObjectionJS - ORM
- Apollo Server - GraphQL server
- Koa - NodeJS web framework
- ReactJS - React app created with CRA
- Semantic UI - CSS framework
- Batteries included: Backend starter with Authorization Management UI
$ cd server
$ npm install
$ npm i knex -g
$ cp .env.example .env # Edit .env with your configuration
$ knex migrate:latest
$ mkdir cache
$ node updateCache.js
$ knex seed:run
$ node updateCache.js
$ node app.js &
Open in browser http://localhost:4000/graphql
Enter query and run:
query getHello { getHello(name: "world") { name } }
$ cd client
$ npm install
$ cp .env.example .env # Edit .env with your configuration
$ npm run start
$ cd client
$ npm run build
$ npm run deploy
Open http://localhost:4000
Login with (look at the code)
1. Server Module Structure
/root
../modules
../../enabled
../../../helloworld
../../../../gql
../../../../../queries.gql
../../../../../types.gql
../../../../HelloWorld.js
../../../../resolvers.js
../../../../routes.js
1.1. queries.gql
getHello(name: String!): Hello
1.2. types.gql
type Hello {
name: String
}
At this point, the framework will generate the following schema.gql automatically:
/cache/schema.gql
Queries {
getHello(name: String!): Hello
}
type Hello {
name: String
}
1.3. HelloWorld.js
class HelloWorld {
static async talkTo(name) {
return new Promise(resolve => {
resolve(`Hello ${name}!`);
});
}
}
module.exports = HelloWorld;
1.4. resolvers.js
const HelloWorld = require('./HelloWorld');
module.exports = {
Query: {
getHello: async (root, args, context) => {
const name = await HelloWorld.talkTo(args.name);
return { name };
}
}
}
1.5. routes.js
module.exports = (app, router) => {
router.get('/hello/:name', async (ctx, next) => {
ctx.body = 'Hello ' + ctx.params.name;
});
}
2. Client Module Structure
/src
../modules
../../welcome
../../../Routes.js
2.1 Routes.js
import React, { Component } from 'react';
import { Route } from 'react-router-dom';
import Welcome from './Welcome';
class Routes extends Component {
render() {
return (
<Route exact path="/" component={Welcome} />
);
}
}
export default Routes;
Module routes will be loaded using the Loadable lib