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 {
@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);
}
@Route.Post({
accesses: [Route.accesses.public],
bodyType: Types.object().keys({
email: Types.string().required(),
name: Types.string().uppercase(),
}),
})
async add(ctx) {
const body = this.body(ctx);
const user = await this.models.user.create(body);
this.sendCreated(ctx, user);
}
}
Summary
What is in this framework ?
**This framework gives you the tools to use a set of modules: **
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
Route
word 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');
}
}
-
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({
email: Types.string().required(),
name: Types.string().uppercase(),
}),
})
async userPost(ctx) {
const body = this.body(ctx);
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 disable
in the content of your decorator class
@Route.Route({
disable: true,
})
export default class RouteMyApi extends Route {
}
-
Disable a specific route
to disable a specific route you can add disable
in the content of your decorator
@Route.Get({
disable: true,
})
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 accessers
will be given the koa context (ctx)
as a parameter, and must return a boolean
to 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) {
return ctx.state.user;
}
async function isUserPremium(ctx) {
return ctx.state.user.isPremium;
}
async function isAdmin(ctx) {
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');
}
-
Middlewares
-
Of a Class
@Route.Route({
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: [
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');
}
-
bodyType
to check body params
-
quick example
@Route.Post({
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) {
const bodyParams = this.body(ctx);
const originBodyParams = this.body(ctx, true);
}
-
queryType
to 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) {
const queryParams = this.queryParam(ctx);
const originQueryParams = this.queryParam(ctx, true);
}
Automatic documention generation: See the manual for more information
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 { App } from 'koa-smart';
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 Route
will be added and mounted)
myApp.mountFolder(join(__dirname, 'routes'), '/');
-
Start your app
myApp.start();
Full example
-
Basic one
import { join } from 'path';
import { App } from 'koa-smart';
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 }),
]);
myApp.mountFolder(join(__dirname, 'routes'), '/');
myApp.start();
-
Other example who Extends class App
import { join } from 'path';
import { App } from 'koa-smart';
import {
i18n,
bodyParser,
compress,
cors,
helmet,
addDefaultBody,
handleError,
logger,
RateLimit,
} from 'koa-smart/middlewares';
export default class MyApp extends App {
constructor() {
super({ port: 3000 });
}
async start() {
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 }),
]);
super.mountFolder(join(__dirname, 'routes'));
return super.start();
}
}
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