koa-restql v15.0.0-beta
koa-restql
Build real RESTful APIs without writing one line of code. Cool, right?
Now it works perfectly with MySQL.
Installation
koa-restql requires node v6.0.0 or higher for (partial) ES2015 support.
npm install --save koa-restqlUsage
const koa = require('koa')
const RestQL = require('koa-restql')
let app = koa()
let restql = new RestQL(sequelize.models) // Build APIs from `sequelize.models`
app.use(restql.routes())How to request real RESTful APIs
Basic
GET /userIf you just have one database table and sequelize model both named user, just choose the right HTTP method to visit path as exactly same name as it.
Using querystring in your url can add condition or limit for the request. For more details, read about querystring.
List
Request
GET /userResponse
HTTP/1.1 206 Partial Content X-Range: items 0-2/10[ { "id": 1, "name": "Li Xin" }, { "id": 2, "name": "Zhang Chi" } ]Note:
- Request for a list will always respond an array.
- This response example include necessary HTTP headers to explain how
Partial Contentworks. If the response was just part of the list, the API would like to response HTTP status code 206.
Single
Request
GET /user/1Response
{ "id": 1, "name": "Li Xin" }Note: Request path with id will always respond an object.
Association
1:1
To define an 1:1 association with sequelize, use model.hasOne() or model.belongsTo().
Request
GET /user/1/profileResponse
{ "id": 1, "user_id": 1, "site": "https://github.com/crzidea" }Note: This example is for
hasOne(). If theprofilewas an association defined withbelongTo(), there should not beuser_idfield.
1:N
To define an 1:N association with sequelize, use model.belongsTo().
List
Request
GET /user/1/messagesResponse
[ { "id": 1, "content": "hello" }, { "id": 2, "content": "world" } ]
Single
Request
GET /user/1/messages/2Response
{ "id": 2, "content": "world" }
N:M
To define an N:M association with sequelize, use model.belongsToMany().
Basicly, you can use the same way to request n:n association as 1:N association. The difference is response.
Request
GET /user/1/friends/2Response
{ "id": 2, "name": "Zhang Chi", "friendship": { "id": 1, "user_id": 1, "friend_id": 2 } }Note: RestQL will respond the target model with another model referred
throughoption.
Another noticeable problem is, you can not do the following query with association path although it is supported by sequelize:
models.user.findAll(
{
include: models.user.association.friends
}
)But, fortunately, you can implement the query with querystring like this:
GET /user?_include%5B0%5D=friendsCRUD
RestQL could do all CRUD operations for you. Just choose the right HTTP method to access either the resource or the association path.
Supported HTTP verbs:
| HTTP verb | CRUD |
|---|---|
| GET | Read |
| POST | Create |
| PUT | Create/Update |
| DELETE | Delete |
Supported HTTP method with body:
| HTTP verb | List | Single |
|---|---|---|
| POST | Array/Object | × |
| PUT | Array/Object | Object |
Listpath examples:/resource/resource/:id/association, association is1:nrelationship/resource/:id/association, association isn:mrelationship
Singlepath examples:/resource/:id/resource/:id/association, association is1:1relationship/resource/:id/association/:id, association is1:nrelationship/resource/:id/association/:id, association isn:mrelationship
Note: PUT method must be used with unique key(s), which means you can not use PUT method with a request body without an unique key.
To use POST or PUT method, you should put data into request body. Example:
POST /user
{
"name": "Li Xin"
}querystring
It's strongly recommended that use qs to stringify nesting querystrings. And this document will assume you will use qs to stringify querystring from JavaScript object.
Example:
qs.stringify({a: 1, b:2}) // => a=1&b=2To understand RestQL querystring, there are only 3 rules:
Every keys in querystring not start with
_, will be directly used aswhereoption forsequelize#query(). Example:// query { name: "Li Xin" } // option for sequelize { where: { name: "Li Xin" } }Every keys in querystring start with
_, will be directly used assequelize#query().// query { _limit: 10 } // option for sequelize { limit: 10 }includeoption forsequelize#query()should be passed asStringof association name.// query { _include: ['friends'] } // option for sequelize { include: [ models.user.association.friends ] }
Sometimes, you want modify query in your own middleware. To do so, you should modify this.restql.query instead of this.request.query or this.query, because the query MUST be parsed with the package qs, not querystring (which is default package of koa).
Access Control
There are at least 2 ways to implement the Access Control:
- Add another middleware before request be handled by RestQL.
- Add options on
sequelize#model#associations, RestQL will handle the options.
This document will only talk about the 2nd way. And the option was only support with associations, not with models.
To specify which association should not be accessed by RestQL, add
ignoreoption. Example:models.user.hasOne( models.privacy, { restql: { ignore: true } } )To specify an association should not be accessed by specific HTTP method, add the method to
ignoreas an array element. Example:models.user.hasOne( models.privacy, { restql: { ignore: ['get'] } } )
Running tests
npm testLicense
MIT