koa-smart
koa-smart copied to clipboard
A framework base on Koajs2 with Decorator, Params checker and a base of modules (cors, bodyparser, compress, I18n, etc…) to let you develop smart api easily
KoaSmart is a framework based on Koajs2, which allows you to develop RESTful APIs with : Class, Decorator, Params checker
A framework based on Koajs2 with Decorator, Params checker and a base of modules (cors, bodyparser, compress, I18n, etc... ) to allow you to develop a smart api easily
export default class RouteUsers extends Route {
// get route: http://localhost:3000/users/get/:id
@Route.Get({
path: 'get/:id'
})
async get(ctx) {
const user = await this.models.users.findById(ctx.params.id);
this.assert(user, 404, 'User not found');
this.sendOk(ctx, user);
}
// post route: http://localhost:3000/users/add
@Route.Post({
accesses: [Route.accesses.public],
bodyType: Types.object().keys({
email: Types.string().required(), // return an 400 if the body doesn't contain email key
name: Types.string().uppercase(), // optional parameter
}),
})
async add(ctx) {
const body = this.body(ctx); // or ctx.request.body
// body can contain only an object with email and name field
const user = await this.models.user.create(body);
this.sendCreated(ctx, user);
}
}
Api documentation
Summary
- What's in this framework ?
- Install
- Router with decorator
- Params checker of POST body
- Automatic documention generation
- Get Started
- Full example
- Add treatment on route
What is in this framework ?
**This framework gives you the tools to use a set of modules: **
-
For routing
koajs 2as the main, underlying frameworkkcorsis used to handle cross-domain requestskoa2-ratelimitTo limit bruteforce requestskoa-helmethelps you secure your apikoa-bodyto parse request bodieskoa-compressto compress the responsekoa-i18nfor Internationalization (I18n)
-
momentParse, validate, manipulate, and display dates in javascript. -
jsonwebtokenan implementation of JSON Web Tokens JWT -
💪
@Decoratorsto ensure a better project structure
Install
npm install --save koa-smart
Or use the boilerplate (koa-smart-boilerplate)
Router with decorator
All routes have to extend the Route class in order to be mount
-
Prefix of routes
If you have a route class with the name
RouteMyApi, all the routes inside said class will be preceded by/my-api/-
How does it work ?
- the
Routeword is removed - uppercase letters are replaced with '-'. (essentially converting camelCase into camel-case) e.g.: this will add a get route => http://localhost:3000/my-api/hello
export default class RouteMyApi extends Route { @Route.Get({}) async hello(ctx) { this.sendOk(ctx, 'hello'); } } - the
-
Change prefix of all routes in the class: http://localhost:3000/my-prefix/hello
@Route.Route({ routeBase: 'my-prefix', }) export default class RouteMyApi extends Route { @Route.Get({}) async hello(ctx) { this.sendOk(ctx, 'hello'); } }
-
-
Get route http://localhost:3000/my-api/hello
@Route.Get({}) async hello(ctx) { this.sendOk(ctx, null, 'hello'); } -
Change path http://localhost:3000/my-api/myroute/15
@Route.Get({ path: '/myroute/:id' }) async hello(ctx) { this.sendOk(ctx, 'hello' + ctx.params.id); } -
Post route http://localhost:3000/my-api/user-post
@Route.Post({ bodyType: Types.object().keys({ // body to allow: all other params will be rejected email: Types.string().required(), // return an 400 if the body doesn't contain email key name: Types.string().uppercase(), // optional parameter }), }) async userPost(ctx) { const body = this.body(ctx); // body can contain only an object with email and name field const user = await this.models.user.create(body); this.sendCreated(ctx, user); } -
Disable route
-
Disable all routes in a class
to disable all routes in a class you should add
disablein the content of your decorator class@Route.Route({ disable: true, }) export default class RouteMyApi extends Route { // All routes in this class will not be mounted } -
Disable a specific route
to disable a specific route you can add
disablein the content of your decorator@Route.Get({ disable: true, // this route will not be mounted }) async hello(ctx) { this.sendOk(ctx, null, 'hello'); }
-
-
Grant accesses
Koa smart allows grant permission to be handled in a simple and efficient manner.
Each function passed to
accesserswill be given thekoa context (ctx)as a parameter, and must return abooleanto express whether is grants access to the route or not.If at least one of the function given returns
true, access to the route will be granted.async function isConnected(ctx) { // TODO test if the user is connected return ctx.state.user; } async function isUserPremium(ctx) { // TODO test if the user is premium return ctx.state.user.isPremium; } async function isAdmin(ctx) { // TODO test if the user is a admin return ctx.state.user.isAdmin; }-
Of a Class
@Route.Route({ accesses: [isConnected] }) class RouteMiddlewares extends Route { @Route.Get({}) async view(ctx, next) { console.log('I can be call if the current client is connected'); this.sendOk(ctx, null, 'OK'); } } -
Of a specific route
@Route.Get({}) async myPublicRoute(ctx, next) { console.log('I am a public route, I can be call by any one'); this.sendOk(ctx, null, 'OK'); } @Route.Get({ accesses: [isConnected] }) async myConnectedRoute(ctx, next) { console.log('I can be call if the current client is connected'); this.sendOk(ctx, null, 'OK'); } @Route.Get({ accesses: [isUserPremium, isAdmin] }) async myPremiumRoute(ctx, next) { console.log('I can be call if the current client is connected and premium or admin'); this.sendOk(ctx, null, 'OK'); }
-
-
RateLimit : For more infos, see the
koa2-ratelimitmodule-
Configure
import { App } from 'koa-smart'; import { RateLimit, RateLimitStores } from 'koa-smart/middlewares'; const app = new App({ port: 3000 }); // Set Default Option const store = new RateLimitStores.Memory() OR new RateLimitStores.Sequelize(sequelizeInstance) RateLimit.defaultOptions({ message: 'Too many requests, get out!', store: store, // By default it will create MemoryStore }); // limit 100 accesses per min on your API app.addMiddlewares([ // ... RateLimit.middleware({ interval: { min: 1 }, max: 100 }), // ... ]);
-
RateLimit On Decorator
Single RateLimit
@Route.Get({ // allow only 100 requests per day to /view rateLimit: { interval: { day: 1 }, max: 100 }, }) async view(ctx) { this.sendOk(ctx, null, 'hello'); }Multiple RateLimit
// Multiple RateLimit @Route.Get({ rateLimit: [ { interval: { day: 1 }, max: 100 }, // allow only 100 requests per day { interval: { min: 2 }, max: 40 }, // allow only 40 requests in 2 minutes ], }) async hello(ctx) { this.sendOk(ctx, null, 'hello'); }
-
-
Middlewares
-
Of a Class
@Route.Route({ middlewares: [ // Array of middlewares async (ctx, next) => { console.log('I will be call before all route in this class'); await next(); }, ], }) class RouteMiddlewares extends Route { @Route.Get({}) async view(ctx, next) { console.log('I will be call after middlewares of class'); this.sendOk(ctx, null, 'hello'); } } -
Of a specific route
@Route.Get({ middlewares: [ // Array of middlewares async (ctx, next) => { console.log('I will be call before the route but after middlewares of class'); await next(); }, ], }) async view(ctx, next) { console.log('I will be call after middlewares of the class and route'); this.sendOk(ctx, null, 'hello'); }
-
Params checker: See the doc of Types for more information
-
bodyTypeto check body params-
quick example
@Route.Post({ // or Put, Patch bodyType: Types.object().keys({ email: Types.string().regex(/\S+@\S+\.\S+/).required(), password: Types.string().min(8).required(), address: Types.object().keys({ country: Types.string().required(), street: Types.string().required(), }).required(), }), }) async user(ctx) { // this is the body manage by bodyType const bodyParams = this.body(ctx); // this is the origin body pass const originBodyParams = this.body(ctx, true); }
-
-
queryTypeto check query params-
quick example
@Route.Get({ queryType: Types.object().keys({ limit: Types.number().integer().required().default(10), offset: Types.number().integer().required().default(10), }), }) async users(ctx) { // this can contain only limit and offset const queryParams = this.queryParam(ctx); // this is the origin queryParams pass const originQueryParams = this.queryParam(ctx, true); }
-
Automatic documention generation: See the manual for more information
Get Started (quick-start boilerplate)
in order to get started quickly, look at this boilerplate, or follow the instructions below:
-
import the app and your middlewares
import { join } from 'path'; // import the app import { App } from 'koa-smart'; // import middlewares koa-smart give you OR others import { bodyParser, compress, cors, handleError, RateLimit, ... } from 'koa-smart/middlewares';
-
create an app listening on port 3000
const myApp = new App({ port: 3000, }); -
add your middlewares
myApp.addMiddlewares([ cors({ credentials: true }), helmet(), bodyParser({ multipart: true }), handleError(), RateLimit.middleware({ interval: { min: 1 }, max: 100 }), ... ]); -
add your routes mount a folder with a prefix (all file who extends from
Routewill be added and mounted)myApp.mountFolder(join(__dirname, 'routes'), '/'); -
Start your app
myApp.start();
Full example
-
Basic one
import { join } from 'path'; // import the app import { App } from 'koa-smart'; // import middlewares koa-smart give you OR others import { i18n, bodyParser, compress, cors, helmet, addDefaultBody, handleError, logger, RateLimit, } from 'koa-smart/middlewares'; const myApp = new App({ port: 3000, }); myApp.addMiddlewares([ cors({ credentials: true }), helmet(), bodyParser({ multipart: true }), i18n(myApp.app, { directory: join(__dirname, 'locales'), locales: ['en', 'fr'], modes: ['query', 'subdomain', 'cookie', 'header', 'tld'], }), handleError(), logger(), addDefaultBody(), compress({}), RateLimit.middleware({ interval: { min: 1 }, max: 100 }), ]); // mount a folder with an prefix (all file who extends from `Route` will be add and mount) myApp.mountFolder(join(__dirname, 'routes'), '/'); // start the app myApp.start(); -
Other example who Extends class App
import { join } from 'path'; // import the app import { App } from 'koa-smart'; // import middlewares koa-smart give you OR others import { i18n, bodyParser, compress, cors, helmet, addDefaultBody, handleError, logger, RateLimit, } from 'koa-smart/middlewares'; // create an class who extends from App class export default class MyApp extends App { constructor() { super({ port: 3000 }); } async start() { // add your Middlewares super.addMiddlewares([ cors({ credentials: true }), helmet(), bodyParser({ multipart: true }), i18n(this.app, { directory: join(__dirname, 'locales'), locales: ['en', 'fr'], modes: ['query', 'subdomain', 'cookie', 'header', 'tld'], }), handleError(), logger(), addDefaultBody(), compress({}), RateLimit.middleware({ interval: { min: 1 }, max: 100 }), ]); // mount a folder with an prefix (all file who extends from `Route` will be add and mount) super.mountFolder(join(__dirname, 'routes')); return super.start(); } } // start the app const myApp = new MyApp(); myApp.start();
Upgrade to 4.0.0
4.0.0 upgrades koa2-ratelimit to version 1.0.0
This means koa2-ratelimit does not install sequelize, mongoose or redis anymore. If you use these packages, make sure you install them in your project.
License
MIT © YSO Corp

