Security News
38% of CISOs Fear They’re Not Moving Fast Enough on AI
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
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
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);
}
}
**This framework gives you the tools to use a set of modules: **
For routing
koajs 2
as the main, underlying frameworkkcors
is used to handle cross-domain requestskoa2-ratelimit
To limit bruteforce requestskoa-helmet
helps you secure your apikoa-body
to parse request bodieskoa-compress
to compress the responsekoa-i18n
for Internationalization (I18n)moment
Parse, validate, manipulate, and display dates in javascript.
jsonwebtoken
an implementation of JSON Web Tokens JWT
💪 @Decorators
to ensure a better project structure
npm install --save koa-smart
Or use the boilerplate (koa-smart-boilerplate)
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 ?
Route
word is removedexport 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({ // 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 disable
in 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 disable
in 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 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) {
// 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-ratelimit
module
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');
}
bodyType
to check body paramsquick 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);
}
queryType
to check query paramsquick 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);
}
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 Route
will be added and mounted)
myApp.mountFolder(join(__dirname, 'routes'), '/');
Start your app
myApp.start();
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();
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.
MIT © YSO Corp
FAQs
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
The npm package koa-smart receives a total of 124 weekly downloads. As such, koa-smart popularity was classified as not popular.
We found that koa-smart demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
Research
Security News
Socket researchers uncovered a backdoored typosquat of BoltDB in the Go ecosystem, exploiting Go Module Proxy caching to persist undetected for years.
Security News
Company News
Socket is joining TC54 to help develop standards for software supply chain security, contributing to the evolution of SBOMs, CycloneDX, and Package URL specifications.