Skip to content

bubao/aex

Repository files navigation

Build Status Coverage Status MIT license

AEX

A simple, easy to use, scoped web server, with async linear middlewares and no callbacks.

It is an example:

  1. To show that callbacks are not needed with promise/async/await.
  2. To use middlewares in a linear way instead of stacked way which is insecure.

    For the stacked middleware model will carry response back to the top most so called middleware pushed, where every middleware can access to the body returned.

  3. To pass some vairiables through middlewares and to the final handler.

A simple example

import { Aex, Router } from "@aex/core";

const aex = new Aex();
const router = new Router(aex);

// Simple string route
router.get("/", async (req, res, scope) => {
  // request processing time started
  console.log(scope.time.stated);
  // processing time passed
  console.log(scope.time.passed);
  res.end("Hello Aex!");
});

// Route Array
router.get(["/user/home", "/user/profile"], async (req, res, scope) => {
  // request processing time started
  console.log(scope.time.stated);
  // processing time passed
  console.log(scope.time.passed);
  res.end("Hello Aex!");
});

aex.use(router.toMiddleware());

const port = 3000;
const host = "localhost";
aex.start(port, host).then();

Install

npm i @aex/core

or

yarn add @aex/core

Usage

1. Create an Aex instance

const aex = new Aex();

2. Create a Router

const router = new Router();

2. Setup the option for handler

router.get("/", async (req, res, scope) => {
  // request processing time started
  console.log(scope.time.stated);
  // processing time passed
  console.log(scope.time.passed);
  res.end("Hello Aex!");
});

3. Use router as an middleware

aex.use(router.toMiddleware());

4. Start the server

const port = 3000;
const host = "localhost";
const server = await aex.start(port, host);
// server === aex.server

Websocket support

Simple example

  1. Create a WebSocketServer instance
const aex = new Aex();
const server = await aex.start();
const ws = new WebSocketServer(server);
  1. Get handler for one websocket connection
ws.on(WebSocketServer.ENTER, handler => {
  // process/here
});
  1. Listen on user-customized events
ws.on(WebSocketServer.ENTER, handler => {
  handler.on("event-name", data => {
    // data.message = "Hello world!"
  });
});
  1. Send message to browser / client
ws.on(WebSocketServer.ENTER, handler => {
  handler.send("event-name", { key: "value" });
});
  1. New browser/client WebSocket object
const wsc: WebSocket = new WebSocket("ws://localhost:3000/path");
wsc.on("open", function open() {
  wsc.send("");
});
  1. Listen on user-customized events
ws.on("new-message", () => {
  // process/here
});
  1. Sending ws message in browser/client
const wsc: WebSocket = new WebSocket("ws://localhost:3000/path");
wsc.on("open", function open() {
  wsc.send(
    JSON.stringify({
      event: "event-name",
      data: {
        message: "Hello world!"
      }
    })
  );
});
  1. Use websocket middlewares
ws.use(async (req, ws, scope) => {
  // return false
});

Middlewares

Global middlewares

Global middlewares are effective all over the http request process.

They can be added by aex.use function.

aex.use(async (req, res, scope) => {
  // process 1
  // return false
});

aex.use(async (req, res, scope) => {
  // process 2
  // return false
});

// ...

aex.use(async (req, res, scope) => {
  // process N
  // return false
});

Return false in middlewares will cancel the whole http request processing
It normally happens after a res.end

Handler specific middlewares

Handler specific middlewares are effective only to the specific handler.

They can be optionally added to the handler option via the optional attribute middlewares.

the middlewares attribute is an array of async functions of IAsyncMiddleware.

so we can simply define handler specific middlewares as follows:

router.get(
  "/",
  async (req, res, scope) => {
    res.end("Hello world!");
  },
  [
    async (req, res, scope) => {
      // process 1
      // return false
    },
    async (req, res, scope) => {
      // process 2
      // return false
    },
    // ...,
    async (req, res, scope) => {
      // process N
      // return false
    }
  ]
);

Websocket middlewares

Websocket middlewares are of the same to the above middlewares except that the parameters are of different.

type IWebSocketAsyncMiddleware = (
  req: Request,
  socket: WebSocket,
  scope?: Scope
) => Promise<boolean | undefined | null | void>;

The Websocket Middlewares are defined as IWebSocketAsyncMiddleware, they pass three parameters:

  1. the http request
  2. the websocket object
  3. the scope object

THe middlewares can stop websocket from further execution by return false

Accessable members

server

The node system http.Server.

Accessable through aex.server.

const aex = new Aex();
const server = await aex.start();
expect(server === aex.server).toBeTruthy();
server.close();

Scope

Aex provides scoped data for global and local usage.

A scope object is passed by middlewares and handlers right after req, res as the third parameter.

It is defined in IAsyncMiddleware as the following:

async (req, res, scope) => {
  // process N
  // return false
};

the scope variable has three native attributes: time, outer, inner.

The time attribute contains the started time and passed time of requests. The outer attribute is to store general or global data. The inner attribute is to store specific or local data.

time

  1. Get the requesting time
scope.time.started;
// 2019-12-12T09:01:49.543Z
  1. Get the passed time
scope.time.passed;
// 2019-12-12T09:01:49.543Z

outer and inner

The outer and innervariables are objects used to store data for different purposes.

You can simply assign them a new attribute with data;

scope.inner.a = 100;
scope.outer.a = 120;

all these build-in attribute are readonly

// scope.outer = {};  // Wrong operation!
// scope.inner = {};   // Wrong operation!
// scope.time = {};    // Wrong operation!
// scope.time.started = {};  // Wrong operation!
// scope.time.passed = {};   // Wrong operation!

Use middlewares from expressjs

Aex provide a way for express middlewares to be translated into Aex middlewares.

You need just a simple call to toAsyncMiddleware to generate Aex's async middleware.

const oldMiddleware = (_req: any, _res: any, next: any) => {
  // ...
  next();
};

const pOld = toAsyncMiddleware(oldMiddleware);
aex.use(pOld);

You should be cautious to use express middlewares. Full testing is appreciated.

About

A simple, async, scoped web server, with async middlewares and no more callbacks.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors