Governing the Frontend API Layer

Oct 16, 2019 · 3 min read · 511 Words · -Views -Comments

The React project I’m currently working on is gradually showing chaos in API usage, so I started the governance process. Here’s a record of the causes and consequences.

Before Governance

Background: The number of API requests in the project has reached 100+.

Take an example of two API files, user-api.ts and setting-api.ts:

...

export const getUserList = (params: IUserQuery) => axios.get('/user/api/v1/users', { params });

...

...

export const getCountrySetting = (params:ICountrySettingParam) => axios.get('/setting/api/v1/country', { params });

...

Current APIs are all directly exported specific functions. This design has the following problems:

  1. Naming limitations: Because there’s no concept of namespaces, function names need to accurately express what the request does. Of course, they should also indicate that it’s an API request. As shown above, it’s hard to tell that it’s an API request when using it. Additionally, team members’ skills vary, and without constraints, API naming becomes chaotic.
  2. Redundant base paths: In a single API file, many requests have the same prefix, causing repetition.
  3. Tight coupling: API files are directly imported wherever they’re used. If any changes occur, the outer layers need to be modified accordingly.

After Governance

To improve the maintainability of the API layer, the following solution was finally adopted.

First, let’s show the code


export class BaseApi {
  basePath: string;

  constructor(basePath: string) {
    this.basePath = basePath;
  }
}

...
class UserApi extends BaseApi {
 export const getAll = (params: IUserQuery) => axios.get(`${this.basePath}/v1/users`, { params });

 }

const userApi = new UserApi('/user/api');
export { userApi };
export * from './user-api';

Benefits After Improvement

  • Using object-oriented approach makes each API module cohesive and easy to manage, e.g., shared basePath can be extracted.
  • With class objects (namespaces), using userApi.getUserList clearly indicates it’s an API request when reading code.
  • The index file directly exports API instances for easy use, avoiding importing multiple API files, and internal API changes are somewhat opaque to external layers.
  • As a class, theoretically we can instantiate different objects with different property values, which improves flexibility.

API Method Naming Guidelines

The above only addresses API management. To improve the readability of individual APIs, the team discussed and formed a set of naming guidelines

Need continuous improvement.

Recommendations

  • getOne*
  • getAll*
  • update*
  • create*
  • delete*
  • export*
  • download*
  • is optional and depends on whether a noun is needed to more clearly reflect the method’s purpose.

API Layer Design in Angular

Because I did Angular development for 1-2 years before, I’ll extend this to discuss how it’s done in Angular.

In Angular, we create ApiService classes where each request is a function. When used in specific components, we get an instance of this ApiService and then operate on a specific request. This scheme seems familiar?

Yes, the governance scheme above is basically consistent with the official Angular scheme. So, behind technology are principles and design, which are framework-agnostic.

Final Thoughts

After quickly adjusting the API layer, the overall look is more comfortable now — happy. But the path of governance and refactoring never stops; let’s continue to improve.

References

Authors
Developer, digital product enthusiast, tinkerer, sharer, open source lover