@scorpijs/platform-lungo
v2.3.1
Published
💎 A modern web framework that is built on top of Express and Lungo. It provides a decorator-based approach for building scalable and well-structured APIs.
Downloads
28
Maintainers
Readme
Table of Contents
- Installation
- Example usage
- More examples
- Returning promises
- Using Request and Response objects
- Loading all controllers by file suffix
- Loading all global middlewares by file suffix
- Prefix all controller routes
- Prefix controller with base route
- Inject routing parameters
- Inject query parameters
- Inject request body
- Inject request header parameters
- Inject cookie parameters
- Inject session object
- Inject uploaded files
- Inject host
- Inject ip
- Convert parameters to objects
- Set custom HTTP status code
- Set custom ContentType
- Set Redirect
- Set Location
- Set custom headers
- Render templates
- Throw HTTP exceptions
- Enable CORS
- Using middlewares
- Creating instances of classes from action params
- Auto validating action params
- Using dependency injection
- Contributing
- License
Installation
Install the core package of scorpi:
npm install @scorpijs/core
Install the platform package:
If you want to use Scorpi with Express:
npm install @scorpijs/platform-express
npm install --save-dev @types/express
Or if you want to use Scorpi with Lungo:
npm install @scorpijs/platform-lungo
Install class validation and class transformation packages:
npm install class-validator class-transformer
Set this options in
tsconfig.json
file of your project:{ "emitDecoratorMetadata": true, "experimentalDecorators": true }
Example usage
- Create a file
user.controller.ts
import { Body, Controller, Delete, Get, Params, Post, Put } from '@scorpijs/core'
@Controller('/users')
export class UserController {
@Get('/')
public getAll(): string {
return 'Return all users'
}
@Get('/:id')
public getById(@Params('id') id: number): string {
return `Return the user with id ${id}`
}
@Post('/')
public create(@Body() user: any): string {
return 'Create the user'
}
@Put('/:id')
public update(@Params('id') id: number, @Body() user: any): string {
return 'Update the user'
}
@Delete('/:id')
public delete(@Params('id') id: number): string {
return 'Delete the user'
}
}
- Create a file
index.ts
import { ScorpiFactory } from '@scorpijs/core'
import { ExpressAdapter } from '@scorpijs/platform-express'
import { UserController } from './user.controller'
async function bootstrap(): Promise<void> {
const app = await ScorpiFactory.create(ExpressAdapter, {
controllers: [UserController]
})
await app.listen(3000)
}
bootstrap()
Note: You can also use LungoAdapter instead of ExpressAdapter to work with Lungo.
- Open in browser
http://localhost:3000/users
. You will seeReturn all users
in your browser. If you openhttp://localhost:3000/users/10
you will seeReturn the user with id 10
in your browser.
More examples
Returning promises
You can return promises in your controller. Scorpi will wait until promise resolved and return promise result in the response body.
import { Body, Controller, Get, Params, Post } from '@scorpijs/core'
@Controller('/users')
export class UserController {
@Get('/')
public async getAll(): Promise<User[]> {
return userRepository.findAll()
}
@Get('/:id')
public async getById(@Params('id') id: number): Promise<User> {
return userRepository.findById(id)
}
@Post('/')
public async create(@Body() user: User): Promise<User> {
return userRepository.create(user)
}
}
Using Request and Response objects
You can use framework's request and response objects directly.
import { Request, Response } from 'express'
import { Controller, Get, Req, Res } from '@scorpijs/core'
@Controller('/users')
export class UserController {
@Get('/')
public getAll(@Req() req: Request, @Res() res: Response): void {
res.send('Send all of the users...')
}
}
Note: Instead of using the
@Req()
decorator, you can use the@Request()
decorator, and similarly, you can use the@Response()
decorator instead of@Res()
.
Loading all controllers by file suffix
By specifying the suffix of your controller files, you can load all controllers from anywhere in your project.
import { ScorpiFactory } from '@scorpijs/core'
import { ExpressAdapter } from '@scorpijs/platform-express'
async function bootstrap(): Promise<void> {
const app = await ScorpiFactory.create(ExpressAdapter, {
controllers: '*.controller.ts'
})
await app.listen(3000)
}
bootstrap()
Loading all global middlewares by file suffix
By specifying the suffix of your global middleware files, you can load all global middlewares from anywhere in your project.
import { ScorpiFactory } from '@scorpijs/core'
import { ExpressAdapter } from '@scorpijs/platform-express'
async function bootstrap(): Promise<void> {
const app = await ScorpiFactory.create(ExpressAdapter, {
middlewares: '*.middleware.ts'
})
await app.listen(3000)
}
bootstrap()
Prefix all controller routes
You can use the globalPrefix
option to specify a prefix for all controller routes in your application.
import { ScorpiFactory } from '@scorpijs/core'
import { ExpressAdapter } from '@scorpijs/platform-express'
async function bootstrap(): Promise<void> {
const app = await ScorpiFactory.create(ExpressAdapter, {
globalPrefix: '/api'
})
await app.listen(3000)
}
bootstrap()
Prefix controller with base route
You can prefix all actions of a specific controller with the base route.
@Controller('/users')
export class UserController {
// ...
}
Inject routing parameters
To inject parameters into your controller actions, use the @Params()
decorator.
@Get('/:id')
public getById(@Params('id') id: number): string {
return `Return the user with id ${id}`
}
If you want to inject all parameters, use the @Params()
decorator without passing any parameters.
Inject query parameters
To inject query parameters, use the @Query()
decorator.
@Get('/')
public getById(@Query('id') id: number): void {
return `Return the user with id ${id}`
}
If you want to inject all queries, use the @Query()
decorator without passing any parameters.
Inject request body
To inject request body, use the @Body()
decorator.
@Post('/')
public createUser(@Body() user: User): string {
return 'Create the user'
}
Inject request header parameters
To inject request header parameters, use the @HeaderParams()
decorator.
@Get('/')
public index(@HeaderParams() headers: any): void {
// ...
}
Inject cookie parameters
To inject cookie parameters, use the @Cookies()
decorator.
@Get('/')
public index(@Cookies('token') token: string): void {
// ...
}
If you want to inject all cookies, use the @Cookies()
decorator without passing any parameters.
Inject session object
To inject session object, use the @Session()
decorator.
@Post('/')
public index(@Session() session: any): void {
// ...
}
Inject uploaded files
To inject uploaded files, use the @UploadedFiles()
decorator.
@Post('/')
public upload(@UploadedFiles('fileName') files: any): void {
// ...
}
Inject host
To inject host from request, use the @Host()
decorator.
@Get('/')
public index(@Host() host: string): void {
// ...
}
Inject ip
To inject ip from request, use the @Ip()
decorator.
@Get('/')
public index(@Ip() ip: string): void {
// ...
}
Convert parameters to objects
If you specify a class type as a parameter and decorate it with the parameter decorator, Scorpi will use class-transformer to create an instance of that class type.
Set custom HTTP status code
You can use the @StatusCode()
decorator to set the HTTP status code for any action.
import { HttpStatus } from '@scorpijs/core'
@Get('/')
@StatusCode(HttpStatus.I_AM_A_TEAPOT)
public index(@Res() res: Response): void {
res.end()
}
Note: Most HTTP status codes can be accessed directly through the HttpStatus enum.
Set custom ContentType
You can use the @ContentType()
decorator to set the ContentType of any action.
@Get('/')
@ContentType('text/html')
public index(): string {
return '<h1>Hello from Scorpi!</h1>'
}
Set Redirect
You can use the @Redirect()
decorator to set a Redirect header for any action.
@Get('/')
@Redirect('https://github.com')
public index(@Res() res: Response): void {
res.end()
}
Set Location
You can use the @Location()
decorator to set a Location header for any action.
@Get('/')
@Location('https://github.com')
public index(@Res() res: Response): void {
res.end()
}
Set custom headers
You can use the @Headers()
decorator to set any custom header in a response.
@Get('/')
@Headers({ key: 'x-package-name', 'scorpi' })
public index(@Res() res: Response): void {
res.end()
}
Render templates
First, you need to install the view engine package that is compatible with your web framework. For instance:
npm i pug
Next, you need to specify the view engine options within the app options:
const app = await ScorpiFactory.create(ExpressAdapter, {
controllers: [UserController],
viewEngine: {
// assuming that you have installed 'pug'
name: 'pug',
// the folder containing our template files
views: path.join(__dirname + '/views')
}
})
Next, we will create a template file in the views
folder.
Create a new file called greetings.pug
in our views
folder:
h1 Hello from Pug template!
Finally, we need to render the template from our action:
@Get('/')
@Render('greetings.pug')
public index(): void {}
Throw HTTP exceptions
If you need to return errors with specific error codes, there is a simple solution:
@Get('/:id')
public async getById(@Params('id') id: number): Promise<User> {
const user = await userRepository.findById(id)
if (!user) {
throw new NotFoundException('User cannot found.')
}
return user
}
If a user is not found with the requested ID, the response will have an HTTP status code of 404 and include the following content:
{
"statusCode": 404,
"message": "User cannot found.",
"error": "Not Found"
}
There are set of prepared exceptions you can use:
- HttpException
- BadRequestException
- BadGatewayException
- ConflictException
- ForbiddenException
- HttpVersionNotSupportedException
- ImATeapotException
- InternalServerErrorException
- MethodNotAllowedException
- MisdirectedException
- NotAcceptableException
- NotFoundException
- NotImplementedException
- RequestTimeoutException
- ServiceUnavailableException
- UnauthorizedException
- UnsupportedMediaTypeException
Enable CORS
As CORS is a feature utilized in nearly all web API applications, you can enable it within the Scorpi options.
import { ScorpiFactory } from '@scorpijs/core'
import { ExpressAdapter } from '@scorpijs/platform-express'
async function bootstrap(): Promise<void> {
const app = await ScorpiFactory.create(ExpressAdapter, {
cors: true
})
await app.listen(3000)
}
bootstrap()
To use CORS, you must first install the corresponding package. You can install the cors package for both Express and Lungo frameworks. Furthermore, you can also pass CORS options:
import { ScorpiFactory } from '@scorpijs/core'
import { ExpressAdapter } from '@scorpijs/platform-express'
async function bootstrap(): Promise<void> {
const app = await ScorpiFactory.create(ExpressAdapter, {
cors: {
// options from cors documentation
}
})
await app.listen(3000)
}
bootstrap()
Using middlewares
Scorpi enables you to use any existing Express or Lungo middleware, or create your own custom middleware. Both frameworks offer a middleware interface for creating your own middlewares, and you can use the @Use()
decorator to make use of existing middlewares.
Use existing middleware
There are several ways to implement middleware. For instance, let's attempt to use the helmet middleware as an example.
Install helmet middleware:
npm install helmet
To use middleware per-action:
import helmet from 'helmet' import { Controller, Get, Use } from '@scorpijs/core' @Controller('/users') export class UserController { @Get('/') @Use(helmet()) public index(): void { // ... } }
By doing so, the
helmet
middleware will only be applied to theindex
controller action and executed before the action itself.To use middleware per-controller:
import helmet from 'helmet' import { Controller, Use } from '@scorpijs/core' @Controller('/users') @Use(helmet()) export class UserController { // ... }
By doing so, the
helmet
middleware will be applied to all actions of the UserController and executed before each action.If you want to use the
helmet
module globally for all controllers, you can easily register it during the bootstrap process.import helmet from 'helmet' import { ScorpiFactory } from '@scorpijs/core' import { ExpressAdapter } from '@scorpijs/platform-express' import { UserController } from './user.controller' async function bootstrap(): Promise<void> { const app = await ScorpiFactory.create(ExpressAdapter, { controllers: [UserController], middlewares: [helmet()] }) await app.listen(3000) } bootstrap()
Alternatively, you can create a custom global middleware and simply delegate its execution to the
helmet
module.
Creating your own middlewares
Here's an example of how to create middleware:
import { ExpressMiddleware } from '@scorpijs/platform-express'
class CustomMiddleware implements ExpressMiddleware {
public use(req: Request, res: Response, next: NextFunction): void {
console.log('Hello from our custom middleware!')
next()
}
}
Note: If you are working with Lungo you can use the
LungoMiddleware
interface instead of theExpressMiddleware
interface.
Then you can use them this way:
@Controller('/users')
@Use(CustomMiddleware)
export class UserController {
// ...
}
or per-action:
@Controller('/users')
export class UserController {
@Get('/')
@Use(CustomMiddleware)
public index(): void {
// ...
}
}
Global middlewares
Global middlewares always run before each request. To make your middleware global, you must register it during the bootstrap process.
import { ExpressMiddleware } from '@scorpijs/platform-express'
class CustomMiddleware implements ExpressMiddleware {
public use(req: Request, res: Response, next: NextFunction): void {
console.log('Hello from our global middleware!')
next()
}
}
You can enable your custom middleware by registering it during the bootstrap process.
async function bootstrap(): Promise<void> {
const app = await ScorpiFactory.create(ExpressAdapter, {
middlewares: [CustomMiddleware]
})
await app.listen(3000)
}
bootstrap()
Exception handlers
By default, Scorpi uses the default exception handler of its underlying framework. However, it is also easy to create your own custom exception handler middleware like that:
import { ExpressExceptionHandler } from '@scorpijs/platform-express'
class CustomExceptionHandler implements ExpressExceptionHandler {
public catch(exception: HttpException, req: Request, res: Response): void {
if (exception instanceof Error) {
throw exception
} else {
throw new InternalServerErrorException()
}
}
}
Note: If you are working with Lungo you can use the
LungoExceptionHandler
interface instead of theExpressExceptionHandler
interface.
You should then register your custom error handler with Scorpi during the bootstrapping process.
async function bootstrap(): Promise<void> {
const app = await ScorpiFactory.create(ExpressAdapter, {
exceptionHandler: CustomExceptionHandler
})
await app.listen(3000)
}
bootstrap()
Creating instances of classes from action params
Scorpi provides a built-in feature using class-transformer that allows you to parse a JSON object into an object of a specific class, instead of a simple literal object. Note that this feature is enabled by default, but can be disabled by setting the useClassTransformer
option to false
during application bootstrap.
If you specify a class when parsing your action parameters, Scorpi will create an instance of that class using the data sent by the user.
class User {
private firstName!: string
private lastName!: string
public getName(): string {
return this.lastName + ' ' + this.firstName
}
}
@Controller('/users')
export class UserController {
@Post('/')
public create(@Body() user: User): void {
console.log('Creating user: ' + user.getName())
}
}
Auto validating action params
Converting a JSON object into an instance of a class may not always be sufficient. For example, if you rely on TypeScript's type safety, you may encounter runtime errors because class-transformer doesn't verify the property types. Additionally, you may want to validate the object to ensure that the password is long enough or that the entered email is valid.
Fortunately, this can be easily accomplished through integration with class-validator, which is enabled by default. If you want to turn off this feature, you need to explicitly disable it during application bootstrap by passing the useValidation: false
option:
async function bootstrap(): Promise<void> {
const app = await ScorpiFactory.create(ExpressAdapter, {
useValidation: false
})
await app.listen(3000)
}
bootstrap()
Next, you'll need to define a class that will be used as the type for the controller's method parameter. Make sure to decorate the class's properties with the appropriate validation decorators.
import { IsEmail, MinLength } from 'class-validator'
export class User {
@IsEmail()
public email!: string
@MinLength(6)
public password!: string
}
If you're not familiar with class-validator and its decorators, you can learn more about using them for handling complex object validation here.
Now, if you have specified a class type for your action parameters, the received data from the user will not only be converted into an instance of that class but also validated. This means that you won't have to manually check each property in the controller method body for issues such as incorrect email or too short a password.
@Controller('/users')
export class UserController {
@Post('/')
public create(@Body() user: User): void {
console.log(`We can now be sure that the password is 6 characters or longer: ${user.password}`)
console.log(`We can now be sure that the email is in a valid email format: ${user.email}`)
}
}
If the received parameter does not meet the requirements specified by the class-validator decorators, an error will be thrown and handled by Scorpi. As a result, the client will receive a 400 Bad Request response along with a detailed JSON array of validation errors.
This technique works not only with @Body()
but also with @Params()
, @Query()
, @Cookies()
decorators.
Using dependency injection
Scorpi uses MagnoDI as its built-in DI container and exposes it to users. You can use this container to inject your services into your controllers, middlewares, and error handlers.
Let's take a look at how to use it by creating a service:
import { Injectable } from '@scorpijs/core'
@Injectable()
export class LoggerService {
public log(message: string): void {
console.log(message)
}
}
Note: To register our class with the container, we need to mark it with the
@Injectable()
decorator.
Afterwards, let's inject this service into our controller:
@Controller('/users')
export class UserController {
constructor(private readonly loggerService: LoggerService) {}
// controller actions...
}
Contributing
- Fork this repository.
- Create a new branch with feature name.
- Create your feature.
- Commit and set commit message with feature name.
- Push your code to your fork repository.
- Create pull request.
License
Scorpi is MIT licensed.