Skip to content

Upgrading to v1

v1 contains major and breaking changes.

But don't worry! You can upgrade your project easily by using this documentation.

Overview

The major changes are the following;

  • Express.js has been removed. Axe API uses the http module instead. It has improved its performance by around 40%.
  • A new logger library, pino, has been added.
  • Internal rate limiting has been added.
  • Form uploading helpers have been added.
  • Swagger support
  • Auto-caching (via Redis)
  • Full-text search (via Elasticsearch)

You can apply the following steps in your project.

Step 1. Update the new version

Update the axe-api version in your package.json file:

json
"dependencies": {
  "axe-api": "^1.0.0"
}
bash
$ npm install

Step 2. Add pino configurations

You should update your pino configuration in the app/config.ts file:

ts
import { IApplicationConfig } from "axe-api";

const config: IApplicationConfig = {
  // ...
  pino: {
    level: "debug",
    transport: {
      target: "pino-pretty",
    },
  },
  // ...
};

TIP

You can learn more from the pino documentation.

Step 3. Add caching configuration

Axe API has started to provide an auto-caching mechanism with the new version.

You can set up your caching configurations like the following example;

ts
import { RedisClientOptions } from "redis";
import { IApplicationConfig, CacheStrategies } from "axe-api";

const config: IApplicationConfig = {
  // ...
  redis: RedisClientOptions,
  cache: {
    enable: true,
    ttl: 300,
    invalidation: CacheStrategies.TimeBased,
  },
  // ...
};

Step 4. LogLevels is removed

The LogLevels configuration is removed because Axe API started to support pino. as the logger.

ts
import { IApplicationConfig, LogLevels } from "axe-api"; 
import { IApplicationConfig } from "axe-api"; 

const config: IApplicationConfig = {
  // ...
  logLevel: LogLevels.INFO, 
  // ...
};

Step 5. Set up the error handler

Axe API allows you to add a general error handler function for your API. You can set the error handler function via the app/config.ts file.

ts
import { IApplicationConfig } from "axe-api";
import errorHandler from "./errorHandler";

const config: IApplicationConfig = {
  // ...
  errorHandler,
  // ...
};
ts
import { IncomingMessage, ServerResponse } from "http";
import { NextFunction } from "axe-api/build/src/Types";

export default function (
  err: any,
  req: IncomingMessage,
  res: ServerResponse,
  next: NextFunction,
) {
  if (process.env.NODE_ENV === "production") {
    // Send errors to your error monitoring tool like Sentry
  }

  // Sett the HTTP status code
  res.statusCode = 500;

  // Set the default HTTP message
  res.write(
    JSON.stringify({
      error: "Internal server error",
      message: err.message,
    }),
  );

  res.end();
  next();
}

Step 6. Set up the rate-limiter

Since Axe API is a specified framework for building APIs, it now supports a rate-limiter internally. You can enable the rate-limiter via the app/config.ts file.

ts
import { IApplicationConfig } from "axe-api";

const config: IApplicationConfig = {
  // ...
  rateLimit: {
    enabled: false,
    maxRequests: 240,
    windowInSeconds: 60,
    trustProxyIP: true,
    adaptor: "redis",
  },
  // ...
};

Step 7. Set up documentations

You can enable or disable the auto-created documentation via the configuration file.

ts
import { IApplicationConfig } from "axe-api";

const config: IApplicationConfig = {
  // ...
  docs: true,
  // ...
};

Your API documentation will be automatically generated with Swagger. You can define the base structure of the documentation by editing the following file by yourself. The swagger.ts file would be the base of the documentation, and Axe API would override it.

ts
export default {
  info: {
    title: "Your API Title",
    description: "Description the API in here",
  },
  servers: [
    {
      url: "https://your-api.com",
    },
  ],
};

TIP

You can get more information via Swagger help files.

Step 8. IRequestPack change

The IRequestPack interface has been renamed as IContext.

ts
import { IRequestPack } from "axe-api"; 
import { IContext } from "axe-api"; 

// export default async (params: IRequestPack) => {
// [!code ++] export default async (params: IContext) => {
  // your hook/event function
};

Step 9. Express req and res

Axe API doesn't support Express.js any more. So you can not use req and res objects of Express.js. Instead, you can use AxeRequest and AxeResponse in your APIs.

ts
import { Request, Response } from "express"; 
import { AxeRequest, AxeResponse } from "axe-api"; 

// [!code --] export default async (req: Request, res: Response) => {
// [!code ++] export default async (req: AxeRequest, res: AxeResponse) => {
}

Step 10. IoCService changes

The IoCService.useByType() method has been removed. You can use the use<T>() method instead.

ts
IoCService.useByType<Knex>("Database"); 
IoCService.use<Knex>("Database"); 

Step 11. Middlewares

You should use connect middleware functions instead of Express.js middleware.

ts
import { Request, Response, NextFunction } from "express"; 
import { IncomingMessage, ServerResponse, NextFunction } from "http"; 

export const myMiddleware = (
  req: Request, 
  req: IncomingMessage, 
  res: Response, 
  res: ServerResponse, 
  next: NextFunction,
): void => {
  next();
};

WARNING

You should use connect-based middleware functions instead of Express.js middleware.

We had to make this decision to get rid of Express.js. Please let us know if there is an Express.js middleware that doesn't have a connect version.

We can figure it out together! 💪

Step 12. Init functions

Axe API started to use its own App instance instead of Express.js' app instance.

You should use the Axe API App instance in your app/{version}/init.ts files like the following example;

ts
import { Express } from "express"; 
import { App } from "axe-api"; 

// [!code --] const onBeforeInit = async (app: Express) => {
// [!code ++] const onBeforeInit = async (app: App) => {
  // your logic
};

// [!code --] const onAfterInit = async (app: Express) => {
// [!code ++] const onAfterInit = async (app: App) => {
  // your logic
};

export { onBeforeInit, onAfterInit };

Step 13. IMethodBaseConfig

The IMethodBaseValidations interface has been renamed with IMethodBaseConfig.

ts
import { Model, IMethodBaseValidations } from "axe-api"
import { Model, IMethodBaseConfig } from "axe-api"

class User extends Model {
  get fillable() IMethodBaseValidations { 
  get fillable() IMethodBaseConfig<string[]> { 
    return {
      "POST": ["name", "surname"]
    }
  }
}

Step 14. IHandlerBasedTransactionConfig

The definition of the IHandlerBasedTransactionConfig interface has been changed. You should change your model transaction configurations to the new type.

ts
export interface IHandlerBasedTransactionConfig {
  handler: HandlerTypes | HandlerTypes[]; 
  handlers: HandlerTypes[]; 
  transaction: boolean;
}

Step 15. LogService method changes

Axe API started to use the pino library as the logger. It doesn't support success() or log() methods anymore.

You can use debug() method instead.

ts
LogService.success("My success message"); 
LogService.log("My log message"); 
LogService.debug("My success or log message"); 

Step 16. Model types

The Axe API model has new return types.

You should review the following changes and apply them to your models;

validations()

ts
class Model {
  // [!code --]  get validations(): ModelValidation | IMethodBaseValidations {}
  // [!code ++]  get validations(): ModelValidation | IMethodBaseConfig<ModelValidation> {}
}
ts
export interface IMethodBaseConfig<T> {
  [HttpMethods.POST]?: T;
  [HttpMethods.PUT]?: T;
  [HttpMethods.PATCH]?: T;
}

middlewares()

ts
class Model {
  // [!code --]  get middlewares():
  // [!code --]    | MiddlewareFunction[]
  // [!code --]    | IHandlerBaseMiddleware[]
  // [!code --]    | IHandlerBaseMiddleware {}
  // [!code ++]  get middlewares(): ModelMiddleware {}
}
ts
type ModelMiddleware = Array<AxeFunction | IHandlerBaseMiddleware>;

type AxeFunction = GeneralFunction | PhaseFunction;

type GeneralFunction = MiddlewareFunction | HandlerFunction;

type PhaseFunction = (context: IContext) => DefaultResponse;

export type MiddlewareFunction = (
  req: IncomingMessage,
  res: ServerResponse,
  next: NextFunction,
) => DefaultResponse;

export type HandlerFunction = (
  request: AxeRequest,
  response: AxeResponse,
) => DefaultResponse;
ts
export interface IHandlerBaseMiddleware {
  handler: HandlerTypes[];
  middleware: AxeFunction;
}

transaction()

ts
class Model {
  // [!code --]  get transaction():
  // [!code --]    | boolean
  // [!code --]    | IHandlerBasedTransactionConfig
  // [!code --]    | IHandlerBasedTransactionConfig[]
  // [!code --]    | null {}
  // [!code ++]  get transaction(): boolean | IHandlerBasedTransactionConfig[] {}
}
ts
export interface IHandlerBasedTransactionConfig {
  handlers: HandlerTypes[];
  transaction: boolean;
}

Next steps

We tried to cover all major changes in this documentation. On the other hand, there are many new features. You can review the documentation and implement new features in your APIs.

Please let us know if you notice anything broken so we can fix it quickly.

Let's develop better APIs together with the power of Axe API! 💪

Released under the MIT License.