jsonql-koa
v1.6.2
Published
jsonql Koa middleware
Downloads
19
Readme
jsonql Koa middleware
This is the jsonql middleware previously published as jsonql-koa, and completely rewritten with ES6
BREAKING CHANGE
@ V1.3.10
The module use named export jsonqlKoa
, this will affect all existing code base. Please change accordingly.
import { jsonqlKoa } from 'jsonql-koa'
Please change all your code accordingly.
@ V1.6.0
We add a new property to the configuration call appDir
, and from now on, we will expect all the code will be under
this directory (instead of spread around different places). The default name of this property is jsonql
.
This will be a required field, and validate during start up time.
Installation
$ npm install jsonql-koa --save
or
$ yarn add jsonql-koa
This module export three methods:
- jsonqlKoa(config = {}): this gives you the Koa middleware, just supply the
config
(if needed, see below) - composeJsonqlKoa(opts): this is NOT for general use, since you have no idea what are the options, this is like a lego for us to build with other modules
- extendConfigCheck(extraAppProps, extraConstProps, config): this goes with the
composeJsonqlKoa
to create thejsonqlKoa
. Again this is not for general use, we use this to build with other modules.
Basically you only need this:
import { jsonqlKoa } from 'jsonql-koa'
In fact, we don't recommend you to use this package directly (unless you want create your own cool modules). Use @jsonql/koa instead.
Configuration options
| Name | Description | Expected Type | Default value |
| ----------- |:----------------------| :------------:| :--------------|
| projectRootDir | Where the root of your project is | String
| process.cwd()
|
| appDir | The name of your project directory, the above resolverDir
and contractDir
will under this directory | String
| '' |
| resolversDir | The name of the resolvers directory | String
| 'resolvers' |
| contractDir | The name of the contract directory | String
| 'contract' |
| enableAuth | if you need to use jwt authorisation | Boolean
| false
|
| keysDir | Where we store your RSA key to validate against the JWT token | String
| 'keys' |
More options to come later
Required Middlewares
Also you need to install middleware to parse the JSON content. Here we use koa-bodyparser.
const Koa = require('koa')
const { jsonqlKoa } = require('jsonql-koa')
const bodyparser = require('koa-bodyparser')
const { join } = require('path')
const app = new Koa()
app.use(bodyparser())
app.use(jsonqlKoa({
resolverDir: join(__dirname, 'resolvers') // this is the default value
}))
Resolver(s)
We expect the file in dasherized name style.
For example your resolver directory is <root>/resolvers
,
and your resolver name fetchList
, therefore the expected resolver file name will be:
<root>/resolvers/query/fetch-list.js
.
To properly organise your application. You should have something like this (otherwise it won't work)
/resolvers/query/name-of-your-call/index.js
/lib.js
/utils.js
/resolvers/mutation/name-of-your-update/index.js
...
This way you can separate the export main function to your helpers / utility what not. Also that makes the contract generator to understand your project and create the contract without your input.
And your resolver should look something like this:
// fetchList
/**
* @param {object} params bunch of stuff
* @return {object} modified bunch of stuff
*/
module.exports = function(params) {
params.modified = parseInt((new Date()).getTime()/1000, 10)
// some more things with your params
return params;
}
The middleware is using Async/Await internally, so you could return a promise.
ES6 support
As of V1.3.0 release. You can use ES6 module syntax for your resolver. If you using ES6 then you have to use all of them as ES6 modules, it doesn't support mix and match. The reason is we have to inject a esm export script to transpire your ES6 module to common js on the fly and import into your Koa middleware.
/**
* @param {number} id
* @return {any} your result
*/
export default function getSomethingById(id) {
// return your result
}
Return Data format
The result data will be wrap inside this signature
{
data: {
yourData: 'something'
}
}
We will be adapting more of the JSON API shortly
What you can do with this system
There are four types within this system you can call.
- query - Think of it as the REST API GET, where you get things
- mutation - Use this when you want to change something
- auth - Authorization methods
- socket - Coming soon
Query
When you using our js client or node client
It will generate function for you based on the contract
file it received.
From V.1 onward we will turn on the useDoc option, which means you are REQUIRED to write correct jsdoc for your resolvers
For example you have this resolver:
// ~/project/resolvers/query/ask-what-date.js
/**
* @return {string} UTC Date
*/
module.exports = function() {
return Date.UTC()
}
On your client will received what wrap inside the data
property.
If you throw an error inside your function call:
/**
* @return {object} just throw
*/
module.exports = function() {
throw new Error("I don't know")
}
This will get catch and wrap inside the result.error
field. And it will get throw on the client.
But you will always received a status 200. Because this is application level error. Nothing todo with the transport.
Query can have as many arguments as you want.
/**
* @param {number} a prop
* @param {number} b prop
* @param {number} c prop
* @param {number} [d=10] prop default 10
* @return {number} sum of them
*/
module.exports = function(a, b, c, d=10) {
return a + b + c + d;
}
TBC: How to get the userdata
We have one built in query call helloWorld
, it return Hello world!
.
It will always be available. And even if you use auth:true
option.
You do not need to login before you can access it. This is handy to ping the server and see if your set is correct.
Especially when you are writing test for your application.
Mutation
The different between query and mutation is, mutation has fixed number of arguments
/**
* For the payload it will be a bit more tricky to write jsdoc
* @param {object} payload - the submit payload
* @param {string} payload.username user name
* @param {object} conditions where cause
* @param {number} conditions.user_id to id the user
* @return {boolean} true on success
*/
module.exports = function(payload, conditions) {
// your code
}
Auth
There are only two authorisation methods
login
- basically what you do to loginvalidator
- every time the client request your API, this method will get call and validator against the token in the header.logout
- it does what it said on the tin.
How you implement the login and validation, it's entirely up to you as a developer. And issuer
is just a type of query
.
It has all the signature just like a normal query. The only different is, it will never received any additional parameter.
The naming is important, you must name your function file as /projects/resolvers/auth/issuer.js
and /projects/resolvers/auth/validator.js
. Any other location or file name will not be found, and middleware will throw error.
socket
This require out other module jsonql-ws-server
to setup. More coming soon.
Contract
BREAKING CHANAGE
We will start using the jsdoc to capture your parameter and returns type. Therefore from V.1 release onward it will be REQUIRED to write correct jsdoc
. This is important because
the client side will be expecting correct type information for
the validation to work. Also this solve a problem of
unable to pass default parameter pass to the resolver.
Contracts are generate automatically when you start up your application. We have a separate tool jsonql-contract to generate the contract file (it just a json file contain information about your applications). For internal user, the file is call contract.json
it will located in the contractDir
you specify when you config the middleware, default location will be ~/project/jsonql/contracts
.
There is another contract that is for your client to consume, which is name public-contract.json
. It has the same structure
but without some of the sensitive information, such as the file
field (where your code located) and the validator
field.
If you are not using auth:true
option, then even if you implement the auth/login.js
method, it will get remove as well.
Also whenever the client request the contract. It will look for additional json file in the same folder.
For instance, your NODE_ENV=development
therefore, it will search for ~/project/jsonql/contracts/development.json
and this will
get merge with the stock public-contract.json
. Therefore you can overwrite information (but not remove) or add additional
information, such as change the return field, and write a auto validator based on the result you received.
Also you can set a password to avoid unwanted access to your contract. Just pass the contractKey:[password]
to the config
object.
Main website: jsonql.org
MIT (c) 2019 to1source China in collaboration with NEWBRAN LTD UK