/micro.ts

Primary LanguageTypeScript

micro.ts

Quick start

Project setup
mkdir first_micro_project
cd first_micro_project
npm init -y
npm i --save @micro.ts/core@0.1.7-rc.9
npm i --save @hapi/hapi
npm i --save-dev typescript
npm i --save-dev @types/node

# Create  folders
mkdir config
mkdir src
mkdir src/controllers
mkdir src/services

# Create initial files
touch tsconfig.json
touch src/main.ts
touch src/Startup.ts
Folder Structure

You should end up with this folder structure

.
├── config
├── package.json
├── package-lock.json
├── src
│   ├── controllers
│   ├── main.ts
│   ├── services
│   └── Startup.ts
└── tsconfig.json
File contents
tsconfig.json
{
  "compilerOptions": {
    "alwaysStrict": true,
    "emitDecoratorMetadata": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "module": "CommonJS",
    "noImplicitAny": true,
    "outDir": "./build",
    "removeComments": false,
    "rootDir": "./src",
    "skipLibCheck": true,
    "sourceMap": true,
    "strict": true,
    "strictPropertyInitialization": true,
    "target": "ES2018"
  }
}
src/main.ts
import { Container, AppBuilder, BaseConfiguration } from '@micro.ts/core';
import { Startup } from './Startup';

async function main(): Promise<void> {
  const appBuilder: AppBuilder = new AppBuilder(
    Container.get<BaseConfiguration>(BaseConfiguration)
  ).useStartup(Startup);
  await appBuilder.start();
}

main().catch((error) => {
  process.exit(1);
});
src/Startup.ts
import { OptionsBuilder, StartupBase } from '@micro.ts/core';
import '@micro.ts/core/brokers/http/hapi';

export class Startup extends StartupBase {
  /*
   * Use this methods for inializiations before the brokers are initialized
   */
  public async beforeStart(): Promise<void> {
    console.log('Before start called');
  }

  /*
   * This method gets called after the brokers are initialized
   */
  public async afterStart(): Promise<void> {
    console.log('After start called!');
  }

  /*
   * Application setup builder
   **/
  public configureServer(builder: OptionsBuilder): void {
    builder.setBasePath('api'); // all routes are prefixed with /api
    builder.setLogRequests(true); // log all requests
    builder.useHapiBroker((b) => b.withConfig({ port: 8080 })); // Serve endpoints on port 8080 using Hapi
  }
}
Creating First Controller
Create a file on src/controllers named “HelloController.ts”
src/controllers/HelloController.ts
import { Get, JsonController } from '@micro.ts/core';

@JsonController('first') // Controller path
export class HelloController {
  @Get('') // Request verb (Get, Post, Put, Patch, Delete)
  public getHello(): string { // Handler
    return 'Hello, World!';
  }
}
Register the controller on Startup
src/Startup.ts
//--- imports
import { HelloController } from './controllers/FirstController';
//---

//---
// Add this line at the end of configureServer() method
    builder.addControllers(HelloController);
//---
Build and run the project
Execute the command below
tsc && node build/main.js
Open browser on http://localhost:8080/api/first And now you should see the “Hello, World!” message!

Request Data Decorators

Headers

Injects all request headers as an object

@Get('')
public get(@Headers() headers: Record<string, string | string[]>): void {
  console.log(headers);
}
Header

Injects a single header

@Get('')
public get(
  @Header('Authorization', { required: true }) token: string
): void {
  console.log(token);
}
Body

Injects request body from post/put/patch requests

@Post('')
public get(
  @Body() body: Record<string, string>
): void {
  console.log(body);
}
BodyParam

Injects a single key from request body from post/put/patch requests

@Post('')
public get(
  @BodyParam(id) id: string
): void {
  console.log(id);
}
Query

Injects the querystring parsed as an object

@Get('')
public get(
  @Query() query: {id: string, search: string}
): void {
  console.log(query);
}
QueryParam

Injects a single query parameter

@Get('')
public get(
  @QueryParam(id) id: string
): void {
  console.log(id);
}
Param

Injects a route parameter

@Get(':id/:name')
public get(
  @Param('id') id: string,
  @Param('name') name: string,
): void {
  console.log(id, name);
}
Params

Injects all route parameters as an object

@Get(':id/:name')
public get(
  @Params() params: {id: string, name: string},
): void {
  console.log(params);
}
Method

Injects request verb

@Get('')
public get(
  @Method() method: string
): void {
  console.log(string);
}
RawRequest

Injects the raw broker request object

@Get('')
public get<T>(
  @RawRequest() request: T
): void {
  console.log(request);
}
Broker

Injects the broker handling the request

@Get('')
public get(
  @Broker() broker: IBroker
): void {
  console.log(broker);
}
Connection

Injects the broker connection (in case of http brokers injects the server instance)

@Get('')
public get<T>(
  @Connection() connection: T
): void {
  console.log(connection);
}

Middlewares

Middlewares modify the Action object which is the request/response container that gets passed around in a request cycle Middlewares are classified into BeforeMiddlewares and AfterMiddlewars They can be places around the handler, around the controller or in application level

Handler Level Middlewares
Before Middlewares
@BeforeMiddlewares([(action: Action) => {
  action.request.headers['x-test'] = 'test';
  return action;
}]);
@Get('')
public getHello(@Header('x-test') test_header: string): void {
  console.log(test_header);
}
After Middlewares
@AfterMiddlewares([(action: Action) => {
  action.reponse.headers['Content-Type'] = 'text/html';
  return action;
}]);
@Get('')
public getHello(): void {
  return '<div>Hello</div>'
}
Controller Level Middlewares
Before Middlewares
@BeforeMiddlewares([(action: Action) => {
  action.request.headers['x-test'] = 'test';
  return action;
}]);
@JsonController()
export class TestController{
  @Get('')
  public getHello(@Header('x-test') test_header: string): void {
      console.log(test_header);
  }
}
After Middlewares
@AfterMiddlewares([(action: Action) => {
  action.reponse.headers['Content-Type'] = 'text/html';
  return action;
}]);
@JsonController("test")
export class TestController{
  @Get('')
  public getHello(): void {
      return '<div>Hello</div>'
  }
}
Application Level Middlewares
// src/Startup.ts
public configureServer(builder: OptionsBuilder): void {
  builder.setBasePath('api'); // all routes are prefixed with /api
  builder.setLogRequests(true); // log all requests
  builder.addBeforeMiddlewares((action: Action) => {
          action.request.headers['x-test'] = 'test';
          return action;
      });
  builder.addAfterMiddlewares((action: Action) => {
          action.reponse.headers['Content-Type'] = 'text/html';
          return action;
      });
  builder.useHapiBroker((b) => b.withConfig({ port: 8080 })); // Serve endpoints on port 8080 using Hapi
}
Middleware construction

Middlewares can be a function or a class constructor implementing the IMiddleware interface

type AppMiddleware = Class<IMiddleware> | MiddlewareFunction;

interface IMiddleware {
    do(action: Action, def?: BaseRouteDefinition, controller?: any, broker?: IBroker, send?: (data: any) => Action): Action | Promise<Action>;
}

type MiddlewareFunction = (action: Action, def?: BaseRouteDefinition, controller?: any, broker?: IBroker, send?: (data: any) => Action) => Action | Promise<Action>;

type Class<T = any> = {
    new (...args: any[]): T;
};