Description
SugoiJS is a minimal modular framework.
SugoiJS gives you the ability to use only what you need and do it fast.
This is a standalone module that can be functional on its own (as all of the SugoiJS modules).
This module provides singleton services, request handling decorators and request policies decorators.
SugoiJS server uses inversify, inversify-express-utils and re-export those modules
@Sugoi/Server alternatives and similar modules
Based on the "Web Frameworks" category.
Alternatively, view @Sugoi/Server alternatives based on common mentions on social networks and blogs.
-
Nest
A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications with TypeScript/JavaScript ๐ -
Nuxt.js
DISCONTINUED. Nuxt is an intuitive and extendable way to create type-safe, performant and production-grade full-stack web apps and websites with Vue 3. [Moved to: https://github.com/nuxt/nuxt] -
AdonisJs Framework
AdonisJS is a TypeScript-first web framework for building web apps and API servers. It comes with support for testing, modern tooling, an ecosystem of official packages, and more. -
Quick Start
๐ A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. ๐ -
Encore
Open Source Development Platform for building robust type-safe distributed systems with declarative infrastructure -
Derby
MVC framework making it easy to write realtime, collaborative applications that run in both Node.js and browsers -
NestJS REST API boilerplate
NestJS boilerplate. Auth, TypeORM, Mongoose, Postgres, MongoDB, Mailing, I18N, Docker. -
ActionHero
Actionhero is a realtime multi-transport nodejs API Server with integrated cluster capabilities and delayed tasks -
Lad
Node.js framework made by a former @expressjs TC and @koajs team member. Built for @forwardemail, @spamscanner, @breejs, @cabinjs, and @lassjs. -
Marble.js
Marble.js - functional reactive Node.js framework for building server-side applications, based on TypeScript and RxJS. -
lychee.js
DISCONTINUED. :seedling: Next-Gen AI-Assisted Isomorphic Application Engine for Embedded, Console, Mobile, Server and Desktop -
Hemera
๐ฌ Writing reliable & fault-tolerant microservices in Node.js https://hemerajs.github.io/hemera/ -
Catberry
Catberry is an isomorphic framework for building universal front-end apps using components, Flux architecture and progressive rendering. -
dawson-cli
DISCONTINUED. A serverless web framework for Node.js on AWS (CloudFormation, CloudFront, API Gateway, Lambda) -
AdonisJs Application
DISCONTINUED. This repo is the pre-configured project structure to be used for creating ambitious web servers using AdonisJs. -
QueryQL
Easily add filtering, sorting, and pagination to your Node.js REST API through your old friend: the query string! -
express-version-route
A Node.js express middleware that implements API versioning for route controllers -
FortJs
A feature-rich Node.js web framework designed for building powerful, scalable, and maintainable web applications. -
Prim+RPC
Easy-to-understand, type-safe, transport-agnostic RPC/IPC for JavaScript, supporting callbacks, batching, file handling, custom serialization, and more.
SaaSHub - Software Alternatives and Reviews
* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of @Sugoi/Server or a related project?
README
@Sugoi/Server
Introduction
SugoiJS is a minimal modular framework.
SugoiJS gives you the ability to use only what you need and do it fast.
this is a standalone module that can be functional on its own (as all of the SugoiJS modules).
This module provides singleton services, request handling decorators and request policies decorators.
SugoiJS server uses inversify, inversify-express-utils and re-export those modules
Installation
npm install --save @sugoi/server
or use @sugoi/cli
tsconfig.json:
Under your tsconfig - compilerOptions set:
"target": "es5"
"emitDecoratorMetadata": true
"experimentalDecorators": true
"lib": ["es2015","dom"]
TSConfig Template
You are able to use the config template which was set for the @sugoi/demo application:
{
"compilerOptions": {
"baseUrl": "./src",
"allowJs": true,
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [
"es2015",
"dom"
],
"typeRoots": [
"./node_modules/@types"
],
"types": [
"body-parser",
"express",
"node"
]
}
}
Bootstrapping
To bootstrap you server use the 'init' method:
init(boostrapModule: any, rootPath?: string, authProvider?: AuthProvider)
when boostrapModule is the entry point module
rootPath - Server uri prefix
import {HttpServer} from "@sugoi/server";
const server:HttpServer = HttpServer.init(ServerModule,"/api");
Migrate existing project
SugoiJS support migrate existing project by providing hybrid mode.
For achieving this approach use the initializeFrom
method
HttpServer.initializeFrom(sourceApp: TServer, bootstrapModule: any, authProvider?: TNewable<AuthProvider>)
TServer - http.Server | https.Server | { listen: (...args) => any }
Example:
import {HttpServer} from "@sugoi/server";
const server:HttpServer = HttpServer.initializeFrom(myExpressApp,ServerModule);
Build & listen
After setting the middlewares and error handlers, build and listen to requests by:
server//HttpServer instance
.setStatic("assets")
.setMiddlewares((app) => {
app.use(bodyParser.json());
app.use(compression());
})
.setErrorHandlers((app) => {
app.use(function (req, res, next) {
return res.sendFile(path.resolve(paths.index))
});
app.use((req,res,next)=>{
return function(err){
if(err instanceof SugoiServerError){
console.log.error(err.stack);
console.log.error(`${err.message} ${JSON.stringify(err.data)});
res.status(500).send("Internal error");
}
}
});
})
.listen(PORT, (error: Error) => {
if (error) {
logger.error(error.message);
throw error;
}
logger.debug(`Server running @ ${HOST}:${PORT}`);
});
This call will return http.Server instance which can be use for setting app variables, socket server and more.
Set module
Creating a module requires you to should use the @ServerModule decorator
import {ServerModule} from "@sugoi/server"
@ServerModule({
controllers:[CoreController],
services: [CoreService],
modules:[LoginModule,DashboardModule]
})
export class ServerModule{
constructor(){}
}
Set controller
SugoiJS use inversify-express-utils decorators and re-export them (also with alias to capitalize camel-case decorator names)
import {Controller,Response,HttpGet,RequestParam} from "@sugoi/server";
@Controller('/dashboard')
export class CoreController{
constructor(){}
@HttpGet("/:role")
test(@response() response,@requestParam('role') role){
if(role === "user" )
return "authorized";
else{
throw new Error("unauthorized")
}
}
}
Further information can be found on Inversify-express-utils documentation
Set service
For setting class as service the class must be decorated with @Injectable
decorator, this will set the class as singleton.
@Injectable()
class MyService{
public listeners:number = 0;
public incListeners(){
this.listeners++;
}
public decListeners(){
this.listeners--;
}
}
later we will be able to inject the service instance by:
Variable binding
@Inject(MyService) private myService:MyService
@Inject("MyService") private myService:MyService
constructor(private myService:MyService)
#### Return the value from the "container"
The InversifyJS container is handling the singleton objects.
The container is stored on the server instance, each request and the ServerContainerService
by the instanceId
server.container
req.container
ServerContainerService.getContainerById(serverInstanceId)
After retriving the container we will able to get the service instance:
private myService:MyService = container.get(MyService)
private myService:MyService = container.get("MyService")
Setting middlewares and Error handlers
For setting static file serving use:
setStatic(pathToStatic:string,route?:string)
For setting middlewares use:
setMiddlewares(...(app)=>void)
For setting error handlers use:
setErrorHandlers((app) => void)
Full example:
(<HttpServer>server)
.setStatic("assets/admin","/admin")
.setStatic("assets/main")
.setMiddlewares((app) => {
app.use(bodyParser.json());
app.use(compression());
})
.setErrorHandlers((app) => {
app.use(function (req, res, next) {
return res.sendFile(path.resolve(paths.index))
});
app.use((req,res,next)=>{
return function(err){
if(err instanceof SugoiServerError){
console.log.error(err.stack);
console.log.error(`${err.message} ${JSON.stringify(err.data)});
res.status(500).send("Internal error");
}
}
});
});
Policies
@sugoi/server uses @sugoi/core policies and supply predefined policies.
And re-export SchemaTypes, TPolicy, TComparableSchema, Policy, UsePolicy, ComparableSchema from "@sugoi/core";
Further information on the @sugoi/core package documentation
RequestSchemaPolicy
/**
* paramSchema - req.params
* queryParamSchema - req.query
* bodySchema - req.body
* headersSchema - req.headers
**/
RequestSchemaPolicy(paramSchema?: TComparableSchema,queryParamSchema?: TComparableSchema,bodySchema?: TComparableSchema,headersSchema?: TComparableSchema)
The RequestSchemaPolicy
decorator use for validate the request is using a valid schema for params\queryParams\body\headers.
In case null will pass the value won't check.
Example:
@Controller('/dashboard')
export class DashboardController {
constructor() {
}
@HttpPost("/:id")
@RequestSchemaPolicy({"id": ComparableSchema.ofType(SchemaTypes.NUMBER)},
null,
{"role": ComparableSchema.ofType({text: ComparableSchema.ofType(SchemaTypes.STRING).setRegex("([A-Z])+","i")})})
//body schema is {role:{text:string//with regex /([A-Z])+/i}}
getUser(@RequestParam("id") id:number, @RequestBody("role") role:{text:string}) {
return User.findOne({id,role:role.text})
}
}
Authorization
Authorized
/**
* requiredRole: TStringOrNumber|TStringOrNumber[] - The required role(s)
* permissions: TStringOrNumber|TStringOrNumber[] - The required premission(s)
* failedCode: number - The response code in case the policy will fail
**/
Authorized(requiredRole: TStringOrNumber|TStringOrNumber[] = null, permissions: TStringOrNumber|TStringOrNumber[] = null, failedCode: number = 401)
The Authorized
decorator use for validate the user is Authorized and in the right role and permissions(optional).
In case null will pass the value won't check.
The Authorized policy will use the AuthProvider which pass while the server init:
init(boostrapModule: any, rootPath?: string, moduleMetaKey?: string, authProvider?: AuthProvider)
The AuthProvider will init for each request, the AuthProvider holding the request headers & cookies.
Example:
Authorization.class.ts:
export class Authorization extends AuthProvider<User> {
/**
* Verify if user is authorized
*
* Implemented dummy check for x-sug-demo header to be equal to "Wyn1RRR9PQJPaqYM"
*
* @returns {Promise<boolean>}
*/
isAuthenticated(): Promise<boolean> {
return Promise.resolve(this.headers["x-sug-demo"] === "Wyn1RRR9PQJPaqYM");
}
getUser(req?: e.Request, res?: e.Response, next?: e.NextFunction): Promise<any> {
return this.details
? Promise.resolve(this.details)
: UserService.getUser(UserService.getIdFromCookie(this.cookie))
.then((user:User)=>{
this.details = user;
return user;
})
}
isInRole(...roles: Array<string | number>): Promise<boolean> {
return this.getUser().then(user=>roles.includes(user.role));
}
/**
* Check if on of user has some of the permissions.
**/
isAllowedTo(...permissions: Array<string | number>): Promise<boolean> {
return this.getUser().then(user=>permissions.some(permission=>user.permissions.includes(permission)));
}
isResourceOwner(resourceId: any): Promise<boolean> {
return this.getUser().then(user=>Resources.checkIfOwner(resourceId,user.id));
}
}
app.ts:
`init(boostrapModule,"/",null, Authorization)`
dashboard.controller.ts:
@Controller('/dashboard')
export class DashboardController {
constructor() {
}
@HttpPost("/:id")
@Authorized(["User","Admin"],"User.READ")
@Authorized(null,"User.READ_BY_ID") // This case promise the user have both "User.READ" AND "User.READ_BY_ID" permissions
getUser(@RequestParam("id") id:number, @RequestBody("role") role:{text:string}) {
return User.findOne({id,role:role.text})
}
}
Documentation
You can find further information on Sugoi official website