Code Monkey home page Code Monkey logo

axe-api's Introduction

Axe API is a TypeScript-based Node.js framework designed to eliminate the need for repetitive tasks associated with common elements while allowing developers to focus on custom logic.

It offers a comprehensive structure for your API, including numerous features and best practices that will save you time.

Video Introduction

Documentation

Axe API has great documentation. Please check it out in here.

License

MIT License

axe-api's People

Contributors

arifkarakilic avatar bilalisler avatar burakack avatar dependabot[bot] avatar ozziest avatar samantatarun avatar saracalihan avatar snyk-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

axe-api's Issues

Adding more unit tests

We don't have enough unit test in the project and we should add more unit tests before the launch.

Changing some hook names

We should create a standard between handlers and hook/event names. We use INSERT as the handler name but the following hook names are not compatible.

  • onBeforeCreate
  • onAfterCreate

Has-one routes

For now, Axe API defines routes by looking at the hasMany relationship. But, developers should be able to define routes by looking at the hasOne relationship, too.

Model Definitions

import { Model } from "axe-api";

class User extends Model {
  option() {
    return this.hasOne("UserOption", "id", "user_id");
  }
}

export default User;

Auto-Generated Routes

In this case, Axe API should create the following routes;

  • GET /api/users/:id/option: The handler will not be the PAGINATION handler. It should be the SHOW handler.
  • POST /api/users/:id/option
  • PUT /api/users/:id/option
  • DELETE /api/users/:id/option

Discussions

We don't have to use the same handlers as the hasMany() method. We can create completely different handlers for this action because the handler will have special cases. For example; we don't need an id parameter update Option model because the relation is one-to-one between the User and UserOption model.

Making optional the default API response

For now, the default API response is something like that;

{
  "name": "AXE API",
  "description": "The best API creation tool in the world.",
  "aim": "To kill them all!"
}

But nobody has to use that schema. Developers should be able to select what kind of response should be given. To do that, I suggest that to add a new property to the app/Config/Application.js file like the following code;

import { LOG_LEVEL } from "axe-api";

export default async () => {
  return {
    env: process.env.NODE_ENV,
    port: process.env.APP_PORT,
    logLevel: LOG_LEVEL.INFO,
    defaultResponse: {
      "name": "AXE API",
      "description": "The best API creation tool in the world.",
      "aim": "To kill them all!"
    },
  };
};

With that, developers can decide what they want to show.

In this issue, we should update the axe-api-template project, too.

Transaction support

In some cases, we may need to use a transaction. We should provide that feature by defining it in models.

Knex.js allows us to use transaction like the following code;

// Using trx as a transaction object:
const trx = await knex.transaction();

const books = [
  {title: 'Canterbury Tales'},
  {title: 'Moby Dick'},
  {title: 'Hamlet'}
];

trx('catalogues')
  .insert({name: 'Old Books'}, 'id')
  .then(function(ids) {
    books.forEach((book) => book.catalogue_id = ids[0]);
    return trx('books').insert(books);
  })
  .then(trx.commit)
  .catch(trx.rollback);

Configurations

Axe API has many shared codes between handlers. We may let the developers set their transaction strategy in the application general. But also, developers should be able to set special transaction rules for some handlers.

app/Config/Application.js

import { LOG_LEVEL } from "axe-api";

export default async () => {
  return {
    env: process.env.NODE_ENV,
    port: process.env.APP_PORT,
    logLevel: LOG_LEVEL.INFO, 
    transaction: false
  };
};

Default transaction value: false. But developers should be able to open in general. On the other hand, if a developer doesn't want a general transaction rule, they should be able to define a transaction rule in model definition;

import { Model } from "axe-api";

class User extends Model {
  get transaction() {
    return true;
  }
}

export default User;

A transaction definition like this means that Axe API will use a transaction in all routes for the model. But, developers should be able to define handler-specific transactions too;

import { Model, HANDLERS } from "axe-api";

class User extends Model {
  get transaction() {
    return [
      {
        handler: HANDLERS.INSERT,
        transaction: false
      },
      {
        handler: HANDLERS.DELETE,
        transaction: true
      },
    ];
  }
}

export default User;

Configuration importance is like this;

  • If there is any handler-specific transaction configuration, use that option
  • Else if there is any model-specific transaction configuration, use that option.
  • Else use application-level transaction configuration.

Middlewares

Developers can't use transactions in middleware functions.

Events

In events, developers can't use transactions because events can have a different timeline.

Hooks

In hooks, developers should be able to use the transaction.

const onBeforeInsert = async ({ database, transaction }) => {
};

export { onBeforeInsert };

In general, we are passing the database instance to the hook methods. But, we should pass the transaction object to the hook methods if there is any transaction configuration for the related model. If the developers disabled the transaction, we should pass the transaction variable as NULL.

Error Handling

We should be able to handle errors and we shouldn't let locked tables be.

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for app/Models/.gitignore

An error is occurred if the app/Models folder contains .gitignore file.

(node:51706) UnhandledPromiseRejectionWarning: TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for /my-api/app/Models/.gitignore
    at Loader.defaultGetFormat [as _getFormat] (internal/modules/esm/get_format.js:71:15)
    at Loader.getFormat (internal/modules/esm/loader.js:105:42)
    at Loader.getModuleJob (internal/modules/esm/loader.js:243:31)
    at async Loader.import (internal/modules/esm/loader.js:177:17)
    at async default (file:///my-api/node_modules/axe-api/src/resolvers/getModelInstanceArray.js:15:32)
    at async Server._analyzeModels (file:///my-api/node_modules/axe-api/src/Server.js:69:19)
    at async Server.listen (file:///my-api/node_modules/axe-api/src/Server.js:31:5)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:51706) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:51706) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Setting custom primary key in the model

Developers should be able to select a custom primary key column in the model definition. The default value should be id.

Usage

import { Model } from "axe-api";

class User extends Model {
  get primaryKey () {
    return 'uid'
  }
}

export default User;

Limiting query features by configurations

Motivation

Axe API has strong query features. But it doesn't mean every developer will want that feature in their APIs. Especially, some query features on some models. So, developers should be able to which query features will be open to use in which models.

Configuration Levels

There should be a two-level configuration;

  • General configuration
  • Model-based configuration

Features

You can see the all following examples;

  • *
  • fields
  • sorting
  • limits
  • where.*
  • where.equal
  • where.notEqual
  • where.gt
  • where.gte
  • where.lt
  • where.lte
  • where.like
  • where.notLike
  • where.in
  • where.notIn
  • where.between
  • where.notBetween
  • where.null
  • where.notNull
  • trashed
  • with.*
  • with.hasOne
  • with.hasMany

These values should be in Enum fields.

Feature Levels

The * character should contain all sub-features. For example, where.* means that all where features.

Allow-Deny

Developers should be able to use two functions;

  • allow(feature: QueryFeature: fields: string[] = [])
  • deny(feature: QueryFeature: fields: string[] = [])

Version-based limits

Developers should define all features by manually by using allow and deny functions.

const config: IVersionConfig = {
  transaction: [],
  serializers: [],
  supportedLanguages: ["en"],
  defaultLanguage: "en",
  options: {
    defaults: {
      perPage: 10,
      minPerPage: 5,
      maxPerPage: 20,
    },
    queryLimits: [
      allow("fields.all"),
      allow("with.hasOne"),
      allow("sorting"),
      allow("where.*"),
      deny("where.like"),
      deny("where.notLike"),
    ],
  },
};

Model-based limits

Developers should be able to define model-based configurations like the following example;

class User extends Model {
  get limits() {
    return [allow("limits"), allow("where.like", ["name", "surname"])];
  }
}

In this definition, developers should be able to define a special rule for some columns.

Configuration Logics

All logic should be read from top to bottom. The bottom values override the above configuration. Column-based configuration is override all.

Initial process;

  • Load version-based configuration
  • Load model-based configuration

Checking the feature;

  • Get the last column-based configuration
  • Get the last configuration

Errors

If the client send unacceptable request, we should return a Bad Request response.

Implementation

  • Configuration reading
  • Checking permission
    • Fields
    • Limits
    • Sort
    • Where conditions
    • With
    • Trashed
  • Default pagination limits
  • Name checks
    • Fields
    • Relationships
  • Tests
    • Unit tests
    • Integration tests
  • Docs
  • dev-kit
  • api-template

Query bug on child models

Let's assume that we have a model like this;

class StudentLesson extends Model {
  lesson() {
    return this.belongsTo("Lesson", "lesson_id", "id");
  }

  teacher() {
    return this.belongsTo("Teacher", "teacher_id", "id");
  }
}

When we request the following example, we don't use parentheses to group query features;

/api/students/1/lessons?q=[{ "$or.lesson.name.$like":"*a*"},{ "$or.teacher.name.$like":"*a*"}]

When we execute this request, it executes the following query;

where `student_id` = 1 and `lessons`.`name` like "%a%" or `teachers`.`name` like "%a%"

But, it should be like the following one;

where `student_id` = 1 and (`lessons`.`name` like "%a%" or `teachers`.`name` like "%a%")

Soft deleting

What is a soft delete?

A soft delete marks a record as no longer active or valid without actually deleting it from the database. Soft deletes can improve performance, and can allow โ€œdeletedโ€ data to be recovered. [*]

Motivations

Developers should be able to add soft-delete features to the models.

Implementation

To add a soft-delete feature to a model, developers must add the following code to the model file;

class User extends Model {
  get deletedAtColumn(): string {
    return "deleted_at";
  }
}

deletedAtColumn should return NULL from the base Model. If developers add a database field name, it means that we have a soft-deleting feature for this model. Developers must create a timestamps data field on the database via migrations.

Soft-Deleting Record

The DELETE handler must work as the soft-deleting if there is soft-deleting support. Otherwise, it must work like a normal delete.

On the other hand, developers should be able to add FORCE_DELETE handler to models like the following example to support the force delete feature;

const { INSERT, PAGINATE, DELETE, FORCE_DELETE } = HandlerTypes;

class User extends Model {
  get handlers(): HandlerTypes[] {
    return [INSERT, PAGINATE, DELETE, FORCE_DELETE];
  }
}

Queries

Clients should not be able to see the soft-deleted records in queries (PAGINATE, SHOW). Also, clients should not be able to manipulate the soft-deleted records (UPDATE, PATCH).

In addition, the with keyword that fetches the related data should work with the soft-deleting feature perfectly.

Also, related data fetch must work perfectly with soft deleting. For example; the /api/users/1/posts request should check if the user 1 is soft-deleted or not. (#132)

Force Delete Handler

This would be a new handler for force deleting.

DELETE /api/users/:id/force

Listing deleted values

Clients should be able to fetch soft-deleted records from a database. To do that, they must use the following command;

/api/users?trashed=true

This should be configurable. By default, it should be false.

Hooks & Events

The following hooks and events should be supported for the force-delete handler;

  • onBeforeForceDeleteQuery
  • onAfterForceDeleteQuery
  • onBeforeForceDelete
  • onAfterForceDelete

Todo List

  • Add the deletedAtColumn column to the base Model
  • Change the DELETE handler works.
  • Add the FORCE_DELETE handler.
  • Change the basic queries.
  • Change the with operator works.
  • Add the trashed parameter to the queries.
  • Check the soft-deleting feature for PUT, PATCH
  • Add new hooks.
  • Add the related query checks for the soft-deleting feature (/api/users/1/posts)

Acceptance Criteria

  • Users should be able to soft delete (DELETE)
  • Users should not be able to see the soft-deleted records (PAGINATE, SHOW, ALL)
  • Users should not be able to update the soft-deleted records (PUT, PATCH)
  • Users should be able to force-delete a record (FORCE_DELETE)
  • Users should be able to see all records for non-soft-delete models (PAGINATE, SHOW, ALL)
  • Users should not be able to see soft deleted records (/api/users/1/customers => Customer)
  • Users should not be able to see soft deleted records (/api/users?with=customers => Customer)
  • Users should not be able to handle child element if the parent record has been soft-deleted (/api/customers/1/phones => Customer)
  • Users should not be able to handle records if related-parent elements (/api/phones?with=customer => Customer)
  • Users should be able to use force-delete hooks
  • Users should be able to see the trashed record by the query parameters (PAGINATE, SHOW, ALL)

Documentation Change

  • Models -> Soft Deleting
  • Handlers
  • Queries -> trashed
  • Queries -> related data
  • Hooks
  • DB Analyzer

Model serializations

Developers should be able to add a custom serialization method to the model. With that, they can create computed properties.

Usage

import { Model } from "axe-api";

class User extends Model {
  get serialize(item) {
    return {
      ...item,
      fullname: `${item.name} ${item.surname}`
    }
  }
}

export default User;

The serialize method should be optional. If there is any definition, we should call it before returning the model response.

Relationship

I am not %100 sure but we should be able to call serialize methods for the related data.

One way `belongsTo` relationship bug

In this example, auto-route creation doesn't work properly.

User.js

import { Model } from "axe-api";

class User extends Model {

}

export default User;

Contact.js

import { Model } from "axe-api";

class Contact extends Model {
  phones() {
    return this.hasMany("ContactPhone", "id", "contact_id");
  }

  user() {
    return this.belongsTo("User", "user_id", "id");
  }
}

export default Contact;

ContactPhone.js

import { Model } from "axe-api";

class ContactPhone extends Model {
  contact() {
    return this.belongsTo("Contact", "contact_id", "id");
  }
}

export default ContactPhone;

In this definition, I should be able to use the following query;

GET /api/contacts/1/phones/1?with=contact{name|surname|user{email|name|surname}}

But the routes are not created;

[
  "POST /api/users",
  "GET /api/users",
  "GET /api/users/:id",
  "PUT /api/users/:id"
]

General hooks for express application

For now, we call the init() function for once. But, developers should be able to use the express application in the beginning, and in the end.

Suggestion

const onBeforeInit = async ({ app }) => {};

const onAfterInit = async ({ app }) => {};

export { onBeforeInit, onAfterInit };

http module support

Motivation

Currently, we are using Express as the request handler. But we can use different frameworks such as Fastify.

Implementation

  • It should be configurable.
  • express packages are installed to the axe-api. We should remove them from the axe-api. In axe-api-template we should install express as the default framework. Developers must install fastify dependencies if they want to use it. (I am not sure we can do it.)
  • Almost in everywhere we use app: Express keywords. We should create an IFramework interface. Also, we should have FastifyFramework and ExpressFramework that implement IFramework. By configuration, we should create an instance of any of them. But the developers always should use IFramework.
  • We should create our IRequest, IResponse interfaces. They should be framework agnostic.
  • Many unit and integration tests should be changed.
  • We are extending the Express namespace for currentLanguage at some point. We should be able to do it dynamically by the configuration.

Auto update timestamps

Axe API should be able to update timestamps automatically. But also, developers should be able to change the column name in the model definition.

Usage

To define column name;

import { Model } from "axe-api";

class User extends Model {
  get createdAtColumn () {
    return 'created_at'
  }

  get updatedAtColumn () {
    return 'updated_at'
  }
}

export default User;
  • Default values; created_at, updated_at
  • To disable auto-update, developer should set NULL

One-to-one relationship queries

Problem

For now, we can use the following question;

/api/students?q=[ {"name": "John"} ]

In this example, we can add many query features like recursive, nested, logical operators, etc. But there is something that we can't do; query by the related table.

Suggestion

Let's assume that we have a model structure like this;

class Student extends Model {
  school() {
    return this.hasOne("School", "id", "school_id");
  }
}
class School extends Model {
}

In this scenario, the client should be able to query the student by the student's names;

/api/students?q=[ {"school.name.$like": "*Royal Institution*"} ]

Definitions

For now, in the property name section, we define the column name as default;

/api/students?q=[ {"name": "Royal"} ]

Also, clients can add prefix or suffix**.

/api/students?q=[ {"age.$gt": 18}, {"$or.surname": "Locke" } ]

But, we want to create a new feature for related data queries. To do that, we need to analyze the property name, by splitting the name with .

So there are four possible meanings of the property value;

  • Main table column name: like title, age, etc.
  • Prefixes: $or, $and
  • Suffixes: logical operators such as $gt, $lt, $like, etc.
  • Relationship name: like school

Example

A general example should be like this;

/api/students?q=[ {"age.$gt": 18}, {"$or.school.name.$like": "*Royal*" } ]

This example represents the following SQL;

FROM students
LEFT JOIN schools ON schools.id = students.school_id
WHERE (students.age > 18 OR schools.name LIKE "%ROYAL%")

Validations

We should validate the relation name if it has been defined on the Student model.

Missing hooks

We don't have the following hooks yet;

  • onBeforeDeleteQuery
  • onAfterDeleteQuery

Ignore feature on models for creating routes

As a developer, I should be able to hide a model in auto-created routes.

import { Model } from "axe-api"

class User extends Model {
  get ignore() {
    return true
  }
}

export default User

Obviously, we can use the handlers method to manage this. But the ignore() method would be easier.

Client should be able to use those models in related queries.

Pagination summary doesn't return the total page count

Pagination summary doesn't return the total and the lastPage values some pages.

"pagination": {
    "perPage": 2,
    "currentPage": 2,
    "from": 2,
    "to": 4
  }

It should be like this;

"pagination": {
    "total": 13,
    "lastPage": 7,
    "perPage": 2,
    "currentPage": 2,
    "from": 2,
    "to": 4
  }

Autosave handler

Developers should be able to add Autosave feature for record updates.

Example

import { Model, HANDLERS } from "axe-api";
const { INSERT, SHOW, UPDATE, PAGINATE, AUTOSAVE } = HANDLERS;

class Student extends Model {
  get handlers() {
    return [INSERT, PAGINATE, SHOW, UPDATE, AUTOSAVE];
  }

  get fillable() {
    return ["name", "phone"];
  }
}

export default Student;

Defaults

As default, this feature should be disabled. If developers want to enable it, they have to set handlers getter.

HTTP Request

PUT api/students/:id/autosave

Client doesn't have to send all record properties in autosave. For example; if the client wants to update only name field, they should be able to send only name field. Form validation should be executed after the item and new value has been merged.

Auto-detection of the database columns

This issue has been created by the discussion in #846385235.

Why?

#846385235

  • Auto-detection columns in the initialization process.
    • Not easy to implement. We should add auto-detection codes for all databases that Knex.js supports.
    • Easy for developers. They don't have to define anything at all.
    • Great feature for documentation. We can show which model has what kind of columns, even their types.

How

  • Inject auto-detection library by the database type (For example, MySQL, MariaDB, etc.)
  • Detect all database columns (name, type, is_indexed vs) in the database by tables.
  • Bind those columns to the model instances.

Database Column Detectors

Common Schema

{
  "name": "id",
  "tableName": "users",
  "isNullable": false,
  "dataType": "Number",
  "defaultValue": null,
  "maxLength": null,
  "numericPrecision": null,
  "numericScale": null,
  "isPrimary": true,
  "isAutoIncrement": true
}

Documentation

  • Axe API should be able to list all possible columns in the documentation routes.

Column Checks

Relationship queries

We need to add with feature to the Queries.

Examples

api/users?with=posts{id|title|comments{content}}

Dependencies

We should update the documentations.

knexfile.js problem

Now, we have two different configuration files for the database system. First, the main configuration file app/Config/Database.js . The second is ./knexfile.js.

./knexfile.js is used by knex cli to execute migrations. We should merge them in a way.

`$and` prefix should be able to use in queries

In queries, we should be able to use $and prefixes like the following one;

/api/contacts/1/phones?q=[{"type": "MOBILE" }, {"$and.type":"HOME"}]

But, we got an error;

Error: Unacceptable field name: $and.type

Hiding model fields

Developers should be able to hide some fields in the HTTP response automatically.

Usage

import { Model } from "axe-api";

class User extends Model {
  get hiddens () {
    return ["password_salt", "pasword_hash"]
  }
}

export default User;

Global serializer for HTTP results

Developers should be able to create a global serializer for HTTP responses. It will be greater general camelCase conventions.

Configuration

Config/Application.js

import { LOG_LEVEL } from "axe-api";

export default async () => {
  return {
    env: process.env.NODE_ENV,
    port: process.env.APP_PORT,
    logLevel: LOG_LEVEL.INFO,
    serializer: (result) => {
      // do something...
      return result;
    },
  };
};

Also, if developers want, they should be able to create handler-based serializers;

Config/Application.js

import { LOG_LEVEL } from "axe-api";

export default async () => {
  return {
    env: process.env.NODE_ENV,
    port: process.env.APP_PORT,
    logLevel: LOG_LEVEL.INFO,
    serializer: {
      PAGINATE: (result) => {
        // do something
        return result;
      },
    },
  };
};

Using joins in queries

Axe API has a powerful query structure but we make it better. For now, clients can ask for the related data for a model. But, if the client can send a query with joins, it would be great.

Join Query Example

GET /api/post?joins=user{id,create_user_id},user{id,update_user_id}

Discussions

This feature is completely under discussion. I am not sure we need something like this. Also, to manage this, we should be able to use aliases for fields and joins.

Multiple database support

Knex.js allows us to use multiple database configurations at the same time. Developers should be able to select a different connection for models.

Model Example

import { Model } from "axe-api"

class User extends Model {
  get connection() {
    return "database-1"
  }
}

export default User
import { Model } from "axe-api"

class Post extends Model {
  get connection() {
    return "database-2"
  }
}

export default Post

Configurations

Database.js

export default {
  "database-1": {
    client: process.env.DB_CLIENT,
    connection: {
      filename: "./db-1.sqlite",
      host: process.env.DB_HOST,
      user: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_DATABASE,
    },
  },
  "database-2": {
    client: process.env.DB_CLIENT,
    connection: {
      filename: "./db-2.sqlite",
      host: process.env.DB_HOST,
      user: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_DATABASE,
    },
  },
};

Application.js

import { LOG_LEVEL } from "axe-api";

export default async () => {
  return {
    env: process.env.NODE_ENV,
    port: process.env.APP_PORT,
    logLevel: LOG_LEVEL.INFO,
    defaultDatabase: "database-1"
  };
};

Discussions

I have no idea how we manage multiple databases for the migration files.

Multiple handler middleware at the same time

For now, developers have to define all handler definitions one by one.

  get middlewares() {
    return [
      {
        handler: SHOW,
        middleware: isAdmin,
      },
      {
        handler: UPDATE,
        middleware: isAdmin,
      },
    ];
  }

But, we can use the following method;

  get middlewares() {
    return [
      {
        handler: [SHOW, UPDATE],
        middleware: isAdmin,
      },
    ];
  }

Localizations

Developers should be able to select defaultLanguage in configurations.

Configurations

The following example shows how the general configuration should be;

app/Config/Application.ts

const config: IApplicationConfig = {
  prefix: "api",
  env: process.env.NODE_ENV || "production",
  port: process.env.APP_PORT ? parseInt(process.env.APP_PORT) : 3000,
  logLevel: LogLevels.INFO,
  transaction: [],
  serializer: [],
  supportedLanguages: ["en", "en-GB", "fr"],
  defaultLanguage: "en-GB",
};

System Messages

Axe API has several system messages such as warnings or bugs. We are not going to translate that messages for several reasons. We'll keep those messages in English.

Example;

throw new Error(`Dependency is not found ${name}`);

Custom Messages

Developers should decide what kind of i18n support they will provide. There are many solutions out there that can be used. We will NOT provide an internal solution.

Validation Messages

validatorjs has good localization support. We can use them. But we are going to use ISO Language Code Table that is not incompatible with validatorjs. We may need a language codes map.

Setting The Language

Clients should send language selection for every HTTP request because of API's stateless structure. To do that, we are going to use Accept-Language header. Also, we can use the accept-language-parser library to detect the client's selections

We should add an internal resolver to resolve Accept-Language header. There are many packages out there but they are outdated.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.