FoalTS / foal

Full-featured Node.js framework 🚀
https://foalts.org/
MIT License
1.9k stars 142 forks source link

Improve performance using output catching #1051

Open lcnvdl opened 2 years ago

lcnvdl commented 2 years ago

Hi FoalTS team, I hope you are doing well!

I was looking for a server-side cache implementation in FoalTS but I couldn't find it. I bring you the following feature request:

You can dramatically improve the performance of your web applications by taking advantage of output caching. The output cache enables you to cache the content returned by a controller action. That way, the same content does not need to be generated each and every time the same controller action is invoked.

Imagine, for example, that your application displays a list of database records in a view named Index. Normally, each and every time that a user invokes the controller action that returns the Index view, the set of database records must be retrieved from the database by executing a database query.

If, on the other hand, you take advantage of the output cache then you can avoid executing a database query every time any user invokes the same controller action. The view can be retrieved from the cache instead of being regenerated from the controller action. Caching enables you to avoid performing redundant work on the server.

Source: Improving Performance with Output Catching - MSDN Article

Usage (example, in foal.ts):

import { Context, Delete, dependency, Get, HttpResponseOK, OutputCache } from '@foal/core';
import { Project } from '../services/project.service';
import { System } from '../services/system.service';

export class ApiController {
  @dependency
  private projects: Project;
  @dependency
  private system: System;

  @OutputCache({ duration: 15 }) // 15 minutes
  @Get('/status')
  async getSystemInformation(ctx: Context) {
    const result = await this.system.doSomeHeavyTask();

    return new HttpResponseOK(result);
  }

  @OutputCache({ varyByParam: 'projectId' }) // Default duration (5 minutes?) - Cache key: projectId (param)
  @Get('/project/:projectId')
  async getResources(ctx: Context) {
    const resources = await this.projects.getResources(ctx.request.params.projectId);

    return new HttpResponseOK(resources);
  }
}
lcnvdl commented 7 months ago

I have made a Cache hook a long time ago for my microservices gateway (Foal v2). If you want, you can close this thread (or use this code if is useful):

import { Hook, HookDecorator, HttpResponseOK } from '@foal/core';
import { MemoryCache } from '../services';

export function Cache(): HookDecorator {
  return Hook(async (ctx, services) => {
    if (ctx.request) {
      if (ctx.request.method.toLowerCase() === 'get') {
        const cache = services.get(MemoryCache);

        const key = `req.${ctx.request.url}${ctx.user ? ('+usr=' + ctx.user.id) : ''}`;

        ctx.state.cacheKey = key;

        if (cache.containsKey(key)) {
          const response = cache.getSync(key).value;
          return new HttpResponseOK(response);
        }
      }
    }
  });
}

Usage:

  @Get('/stats/projects')
  @Cache()
  async getAllProjectsStats(ctx: Context) {
    // ...
  }