code: @Override
  	public Action install(Action action, Tree config) {
  
  		// Is caching enabled?
  		Tree cacheNode = config.get("cache");
  		if (cacheNode == null || cacheNode.isNull()) {
  			return null;
  		}
  		if (cacheNode.getType() == Boolean.class && !cacheNode.asBoolean()) {
  			return null;
  		}
  		
  		// Get cache keys
  		Tree keyNode = cacheNode.get("keys");
  		final String[] keys;
  		if (keyNode == null) {
  			keys = null;
  		} else {
  			List<String> list = keyNode.asList(String.class);
  			if (list.isEmpty()) {
  				keys = null;
  			} else {
  				keys = new String[list.size()];
  				list.toArray(keys);
  			}
  		}
  
  		// Get TTL (0 = use default TTL)
  		final int ttl = cacheNode.get("ttl", 0);
  
  		return new Action() {
  
  			@Override
  			public Object handler(Context ctx) throws Exception {
  				String key = getCacheKey(ctx.name, ctx.params, keys);
  				return new Promise(resolver -> {
  					get(key).then(in -> {
  						if (in == null || in.isNull()) {
  							new Promise(action.handler(ctx)).then(tree -> {
  								set(key, tree, ttl);
  								resolver.resolve(tree);
  							}).catchError(err -> {
  								resolver.reject(err);
  							});
  						} else {
  							resolver.resolve(in);
  						}
  					}).catchError(err -> {
  						resolver.reject(err);
  					});
  				});
  			}
  		};
  	}
docs: --- ADD MIDDLEWARE TO ACTION ---