Skip to content
This repository was archived by the owner on May 22, 2024. It is now read-only.

Middlewares

Marcel Kloubert edited this page Mar 14, 2023 · 1 revision

Table of contents

About []

Middlewares are functions, which are optional and executed during the lifecycle of a request, before a handler is executed.

import cors from "cors"; // npm i cors && npm i -D @types/cors
import createServer, { json } from "@egomobile/http-server";

async function main() {
  const app = createServer();

  app.get("/", [cors(), json()], async (request, response) => {
    response.write("Your input: " + JSON.stringify(request.body));
  });

  await app.listen();
}

main().catch(console.error);

You can define them globally and/or for one or more single routes.

The common structure of a middleware is:

export type HttpMiddleware = (
  request: IHttpRequest,
  response: IHttpResponse,
  next: NextFunction
) => any;

To continue the request, you always have to execute next() function without arguments.
If you submit a non-falsy value, the request is handled as "failed":

const createMyMiddleware =
  (/* you maybe want be able to parse options here */) => {
    return async (request, response, next) => {
      if (request.headers["foo"] === "bar")
        // continue, because we have data we need
        next();
      else if (!request.headers["foo"])
        // handle request as failed and execute
        // the global error handler
        next(new TypeError("We have no foo header here!"));
      else {
        // a more flexible way to
        // cancel the request
        response.writeHead(400);
        response.write("foo header has wrong data!");

        response.end(); // in that case, we have to end
        // the request by ourselves
      }
    };
  };

app.get("/", [createMyMiddleware()], async (request, response) => {
  response.writeHead(204);
});

Build-in middlewares []

params() - Extract parameters from URL paths []

import createServer, { params } from "@egomobile/http-server";

async function main() {
  const app = createServer();

  // this is not a classic middleware, more a path validator,
  // which additionally extracts the path variables from the
  // URL before the question mark and makes them available in 'params'
  // property of 'request'
  app.get(params("/foo/:bar"), async (request, response) => {
    response.write("bar: " + request.params!.bar);
  });

  await app.listen();
}

main().catch(console.error);

query() - Parse query / search parameters in URLs []

import createServer, { query } from "@egomobile/http-server";

async function main() {
  const app = createServer();

  // get and parse query / search parameters
  // and save them to 'query' property of 'request'
  app.post("/", [query()], async (request, response) => {
    response.write("Your query params: " + JSON.stringify(request.query));
  });

  await app.listen();
}

main().catch(console.error);

json() - Parse to JSON object []

import createServer, { json } from "@egomobile/http-server";

async function main() {
  const app = createServer();

  // handle the input as UTF-8 string and parse it as JSON,
  // then save the object to 'body' property of 'request' and
  // setup a limit of 64 MB (67108864)
  //                   👇
  app.post("/", [json(64)], async (request, response) => {
    response.write("Your input: " + JSON.stringify(request.body));
  });

  await app.listen();
}

main().catch(console.error);

yaml() - Handle input data as YAML string []

import createServer, { yaml } from "@egomobile/http-server";

async function main() {
  const app = createServer();

  // handle the input as UTF-8 YAML string and parse it to an object,
  // then save the object to 'body' property of 'request' and
  // setup a limit of 64 MB (67108864)
  //                   👇
  app.post("/", [yaml(64)], async (request, response) => {
    // input data is always wrapped
    // input an array
    const body: any[] = request.body;

    response.write("Your input: " + JSON.stringify(body));
  });

  await app.listen();
}

main().catch(console.error);

buffer() - Read whole input as buffer []

import createServer, { buffer } from "@egomobile/http-server";

async function main() {
  const app = createServer();

  // read the whole body as buffer and write it to
  // 'body' property of 'request' and setup a limit
  // of 512 MB (536870912)
  //                     👇
  app.post("/", [buffer(512)], async (request, response) => {
    const body: Buffer = request.body;

    response.write("Your input has a size of: " + String(body.length));
  });

  await app.listen();
}

main().catch(console.error);

text() - Read whole input as string []

import createServer, { text } from "@egomobile/http-server";

async function main() {
  const app = createServer();

  // read the whole body as UTF-8 string and write it to
  // 'body' property of 'request' and setup a limit
  // of 1 MB (1048576)
  //               👇
  app.post("/", [text(1)], async (request, response) => {
    const body: string = request.body;

    response.write("Your string input has a size of: " + body.length);
  });

  await app.listen();
}

main().catch(console.error);

validate() - Validate input with the help of schemas []

import createServer, { json, schema, validate } from "@egomobile/http-server";

interface IMySchema {
  email: string;
  name?: string;
}

const mySchema = schema
  .object({
    email: schema.string().strict().trim().email().required(),
    name: schema.string().strict().trim().min(1).optional(),
  })
  .required();

async function main() {
  const app = createServer();

  // read and parse the input as JSON object with a limit of
  // 1 MB (1048576) and validate it with 'mySchema'
  //               👇        👇
  app.put("/", [json(1), validate(mySchema)], async (request, response) => {
    const body: IMySchema = request.body;

    response.write("Your input: " + JSON.stringify(body));
  });

  await app.listen();
}

main().catch(console.error);

lang() - Setup current request language []

import createServer, { lang } from "@egomobile/http-server";

async function main() {
  const app = createServer();

  // setup languages 'de' and 'en', by using the first one
  // as default / fallback language
  //
  // the middleware will extract the value from 'Accept-Language'
  // HTTP header and pick up the best matching language from argument list
  // by quality value and write it to 'lang' property of 'request'
  //               👇
  app.post("/", [lang("de", "en")], async (request, response) => {
    const lang = request.lang as string; // 'de' or 'en'

    response.write("Current language: " + lang);
  });

  await app.listen();
}

main().catch(console.error);

apiKey() - Check for an API key []

import createServer, { apiKey } from "@egomobile/http-server";

async function main() {
  const app = createServer();

  // checks if x-api-key HTTP header
  // contains "mySecret@piKey1234"
  app.get(
    "/secret-content",
    [apiKey("mySecret@piKey1234")],
    async (request, response) => {
      // your code
    }
  );

  await app.listen();
}

main().catch(console.error);

auth() - Check Authorization header []

import createServer, {
  auth,
  AuthValidatorWithoutScheme,
  IHttpRequest,
  IHttpResponse,
} from "@egomobile/http-server";

const checkBearer: AuthValidatorWithoutScheme = async (
  value: string,
  request: IHttpRequest,
  response: IHttpResponse
) => {
  // client must submit something like
  // 'Authorization: Bearer myBearerValue'
  return value === "myBearerValue";
};

async function main() {
  const app = createServer();

  // check if authorization header uses 'Bearer' scheme
  // and its value matches all criteria of 'checkBearer()'
  app.get("/", auth("Bearer", checkBearer), async (response, response) => {
    // your code, if bearer value is valid
  });

  await app.listen();
}

main().catch(console.error);

basicAuth() - Check username and password []

import createServer, {
  basicAuth,
  BasicAuthCredentials,
  IHttpRequest,
  IHttpResponse,
} from "@egomobile/http-server";

const usersAndPasswords: BasicAuthCredentials = {
  bill: "B@zPasswd$",
  marcel: "fooPassword",
  tanja: "barPasswd1234",
};

async function main() {
  const app = createServer();

  app.get(
    "/",
    basicAuth(usersAndPasswords),
    async (response: IHttpRequest, response: IHttpResponse) => {
      // your code, if credentials are valid
    }
  );

  await app.listen();
}

main().catch(console.error);

validateWithSwagger() - Validate request with Swagger []

import createServer, { query, validateWithSwagger } from '@egomobile/http-server'
import type { OpenAPIV3.OperationObject } from 'openapi-types'

const swaggerDocumentOfGetRequest: OpenAPIV3.OperationObject = {
  "parameters": [
    {
      in: 'query',
      name: 'foo',
      required: true
    },
    "responses": {}
  ]
}

const app = createServer()

app.get(
  '/',
  [
    query(),
    validateWithSwagger(swaggerDocumentOfGetRequest)
  ],
  async (request, response) => {
    assert.strictEqual(typeof request.query!.get('foo'), 'string')
  }
)

// ...