diff --git a/api/src/auth/auth.resolver.ts b/api/src/auth/auth.resolver.ts
index d01434af79f15ea82ac7168abc5f1b1b7c64e31d..4742be80f7a9237c6787596412a7618aa3bbb8b4 100644
--- a/api/src/auth/auth.resolver.ts
+++ b/api/src/auth/auth.resolver.ts
@@ -10,7 +10,6 @@ import { Response, Request } from 'express';
 import { CurrentUser } from '../common/decorators/user.decorator';
 import { GQLRequest } from '../common/decorators/gql-request.decoractor';
 import { GQLResponse } from '../common/decorators/gql-response.decoractor';
-import { parseToBoolean } from '../common/utilities';
 import {
   ENGINE_MODULE_OPTIONS,
   ENGINE_SERVICE,
@@ -23,6 +22,7 @@ import { GlobalAuthGuard } from './guards/global-auth.guard';
 import { LocalAuthGuard } from './guards/local-auth.guard';
 import { AuthenticationInput } from './inputs/authentication.input';
 import { AuthenticationOutput } from './outputs/authentication.output';
+import { parseToBoolean } from '../common/utils/shared.utils';
 
 //Custom defined type because Pick<CookieOptions, 'sameSite'> does not work
 type SameSiteType = boolean | 'lax' | 'strict' | 'none' | undefined;
diff --git a/api/src/common/utilities.ts b/api/src/common/utilities.ts
deleted file mode 100644
index 92d045784438e0181d5e80c85e85c20e82234fbf..0000000000000000000000000000000000000000
--- a/api/src/common/utilities.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import {
-  HttpException,
-  InternalServerErrorException,
-  NotFoundException,
-  RequestTimeoutException,
-  UnauthorizedException,
-} from '@nestjs/common';
-import axios from 'axios';
-
-export const errorAxiosHandler = (e: any) => {
-  if (!axios.isAxiosError(e)) throw new InternalServerErrorException(e);
-
-  if (e.response) {
-    if (e.response.status === 401) throw new UnauthorizedException();
-    if (e.response.status === 404) throw new NotFoundException();
-    if (e.response.status === 408) throw new RequestTimeoutException();
-    if (e.response.status === 500) throw new InternalServerErrorException();
-    if (e.response.status && e.response.status)
-      throw new HttpException(e.response.data, e.response.status);
-  }
-
-  throw new InternalServerErrorException('Unknown error');
-};
-
-/**
- * Parse a string to a boolean
- * @param {string} value - The value to parse.
- * @param [defaultValue=false] - The default value to return if the value is not a valid boolean.
- * @returns A boolean value.
- */
-export const parseToBoolean = (
-  value: string,
-  defaultValue = false,
-): boolean => {
-  try {
-    if (value.toLowerCase() == 'true') return true;
-    return value.toLowerCase() == 'false' ? false : defaultValue;
-  } catch {
-    return defaultValue;
-  }
-};
diff --git a/api/src/common/utilities.spec.ts b/api/src/common/utils/shared.utils.spec.ts
similarity index 96%
rename from api/src/common/utilities.spec.ts
rename to api/src/common/utils/shared.utils.spec.ts
index 88f5fb323c1fe3c5de33008cabf3060fa141b8fe..b521a2e27cd3dde32c0dea92ba4f6f97eb5f374f 100644
--- a/api/src/common/utilities.spec.ts
+++ b/api/src/common/utils/shared.utils.spec.ts
@@ -6,8 +6,7 @@ import {
   UnauthorizedException,
 } from '@nestjs/common';
 import axios from 'axios';
-import { response } from 'express';
-import { errorAxiosHandler, parseToBoolean } from './utilities';
+import { errorAxiosHandler, parseToBoolean } from './shared.utils';
 
 describe('Utility parseToBoolean testing', () => {
   it('Parse true string to boolean', () => {
diff --git a/api/src/common/utils/shared.utils.ts b/api/src/common/utils/shared.utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8f1ed083dd7caeee9ccbe56f82c94dd77d0c10b2
--- /dev/null
+++ b/api/src/common/utils/shared.utils.ts
@@ -0,0 +1,105 @@
+/* eslint-disable @typescript-eslint/ban-types */
+/* eslint-disable @typescript-eslint/no-use-before-define */
+
+import {
+  BadRequestException,
+  HttpException,
+  InternalServerErrorException,
+  NotFoundException,
+  RequestTimeoutException,
+  UnauthorizedException,
+} from '@nestjs/common';
+import axios from 'axios';
+
+export const errorAxiosHandler = (e: any) => {
+  if (!axios.isAxiosError(e)) throw new InternalServerErrorException(e);
+
+  if (e.response) {
+    if (e.response.status === 401) throw new UnauthorizedException();
+    if (e.response.status === 404) throw new NotFoundException();
+    if (e.response.status === 408) throw new RequestTimeoutException();
+    if (e.response.status === 500) throw new InternalServerErrorException();
+    if (e.response.status)
+      throw new HttpException(e.response.data, e.response.status);
+  }
+
+  throw new InternalServerErrorException('Unknown error');
+};
+
+/**
+ * It rounds a number to a given number of decimal places
+ * @param {number} val - the number to be rounded
+ * @param [decimal=2] - The number of decimal places to round to.
+ * @param [keepSmallNumber=true] - If true, it will keep the number in exponential form if it's smaller
+ * than 1/coef.
+ * @returns Formatted string number
+ */
+export const floatRound = (
+  val: number,
+  decimal = 2,
+  keepSmallNumber = true,
+) => {
+  const n = Math.trunc(decimal);
+
+  if (n < 0) throw new Error('decimal cannot be negative number');
+
+  const coef = Math.pow(10, n);
+
+  if (keepSmallNumber && val !== 0 && val < 1 / coef) {
+    return val.toExponential(n);
+  }
+
+  return (Math.round(val * coef) / coef).toString();
+};
+
+/**
+ * Parse a string to a boolean
+ * @param {string} value - The value to parse.
+ * @param [defaultValue=false] - The default value to return if the value is not a valid boolean.
+ * @returns A boolean value.
+ */
+export const parseToBoolean = (
+  value: string,
+  defaultValue = false,
+): boolean => {
+  try {
+    if (value.toLowerCase() == 'true') return true;
+    return value.toLowerCase() == 'false' ? false : defaultValue;
+  } catch {
+    return defaultValue;
+  }
+};
+
+export const isUndefined = (obj: any): obj is undefined =>
+  typeof obj === 'undefined';
+
+export const isObject = (fn: any): fn is object =>
+  !isNil(fn) && typeof fn === 'object';
+
+export const isPlainObject = (fn: any): fn is object => {
+  if (!isObject(fn)) {
+    return false;
+  }
+  const proto = Object.getPrototypeOf(fn);
+  if (proto === null) {
+    return true;
+  }
+  const ctor =
+    Object.prototype.hasOwnProperty.call(proto, 'constructor') &&
+    proto.constructor;
+  return (
+    typeof ctor === 'function' &&
+    ctor instanceof ctor &&
+    Function.prototype.toString.call(ctor) ===
+      Function.prototype.toString.call(Object)
+  );
+};
+
+export const isFunction = (val: any): boolean => typeof val === 'function';
+export const isString = (val: any): val is string => typeof val === 'string';
+export const isNumber = (val: any): val is number => typeof val === 'number';
+export const isConstructor = (val: any): boolean => val === 'constructor';
+export const isNil = (val: any): val is null | undefined =>
+  isUndefined(val) || val === null;
+export const isEmpty = (array: any): boolean => !(array && array.length > 0);
+export const isSymbol = (val: any): val is symbol => typeof val === 'symbol';
diff --git a/api/src/config/matomo.config.ts b/api/src/config/matomo.config.ts
index 5e7fa269a4913509846da9d367fc607954c7ee52..dacfd178f90073e950116aedf8e296dfaa557808 100644
--- a/api/src/config/matomo.config.ts
+++ b/api/src/config/matomo.config.ts
@@ -1,5 +1,5 @@
 import { registerAs } from '@nestjs/config';
-import { parseToBoolean } from 'src/common/utilities';
+import { parseToBoolean } from 'src/common/utils/shared.utils';
 
 export default registerAs('matomo', () => {
   return {
diff --git a/api/src/engine/connectors/datashield/main.connector.ts b/api/src/engine/connectors/datashield/main.connector.ts
index 153ceac93cb2968d1da019977b2b147345d3cd1b..487da9bbdd57964ce1824d5f69f20b484d861fe3 100644
--- a/api/src/engine/connectors/datashield/main.connector.ts
+++ b/api/src/engine/connectors/datashield/main.connector.ts
@@ -11,7 +11,7 @@ import {
   ExperimentResult,
   MIME_TYPES,
 } from 'src/common/interfaces/utilities.interface';
-import { errorAxiosHandler } from 'src/common/utilities';
+import { errorAxiosHandler } from 'src/common/utils/shared.utils';
 import { ENGINE_MODULE_OPTIONS } from 'src/engine/engine.constants';
 import {
   IConfiguration,
diff --git a/api/src/engine/connectors/exareme/handlers/algorithms/anova-one-way.handler.ts b/api/src/engine/connectors/exareme/handlers/algorithms/anova-one-way.handler.ts
index 5b25340ea67578d4367e003807f19a76f190fc59..42ff6da38781827fe8393159aac7c8e35840a8af 100644
--- a/api/src/engine/connectors/exareme/handlers/algorithms/anova-one-way.handler.ts
+++ b/api/src/engine/connectors/exareme/handlers/algorithms/anova-one-way.handler.ts
@@ -109,6 +109,6 @@ export default class AnovaOneWayHandler extends BaseHandler {
     const meanPlot = this.getMeanPlot(data);
     if (meanPlot && meanPlot.pointCIs) exp.results.push(meanPlot);
 
-    super.handle(exp, data); // continue request
+    return super.handle(exp, data); // continue request
   }
 }
diff --git a/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression.handler.spec.ts b/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression.handler.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3c29918a110de73ac3b02b7d3a0994ce607efe24
--- /dev/null
+++ b/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression.handler.spec.ts
@@ -0,0 +1,60 @@
+import { Experiment } from '../../../../models/experiment/experiment.model';
+import LinearRegressionHandler from './linear-regression.handler';
+
+const data = {
+  dependent_var: 'lefthippocampus',
+  n_obs: 15431,
+  df_resid: 1540.0,
+  df_model: 2.0,
+  rse: 0.1270107560405171,
+  r_squared: 0.8772983534917347,
+  r_squared_adjusted: 0.8771390007040616,
+  f_stat: 5505.38441342865,
+  f_pvalue: 0.0,
+  indep_vars: ['Intercept', 'righthippocampus', 'leftamygdala'],
+  coefficients: [0.2185676251985193, 0.611894589820809, 1.0305204881766319],
+  std_err: [0.029052606790014847, 0.016978263425746872, 0.05180007458246667],
+  t_stats: [7.523167431352125, 36.03988078621131, 19.894189274496593],
+  pvalues: [
+    9.04278019740564e-14, 8.833386164556705e-207, 1.4580450464941301e-78,
+  ],
+  lower_ci: [0.16158077395909892, 0.5785916308422961, 0.9289143512210847],
+  upper_ci: [0.2755544764379397, 0.6451975487993219, 1.132126625132179],
+};
+
+const createExperiment = (): Experiment => ({
+  id: 'dummy-id',
+  name: 'Testing purpose',
+  algorithm: {
+    name: 'LINEAR_REGRESSION',
+  },
+  datasets: ['desd-synthdata'],
+  domain: 'dementia',
+  variables: ['righthippocampus'],
+  coVariables: ['leftamygdala'],
+  results: [],
+});
+
+describe('Linear regression result handler', () => {
+  let linearHandler: LinearRegressionHandler;
+  let experiment: Experiment;
+
+  beforeEach(() => {
+    linearHandler = new LinearRegressionHandler();
+    experiment = createExperiment();
+  });
+
+  describe('Handle', () => {
+    it('with standard linear algo data', () => {
+      linearHandler.handle(experiment, data);
+
+      expect(experiment.results.length === 2);
+    });
+    it('Should be empty with another algo', () => {
+      experiment.algorithm.name = 'dummy_algo';
+      linearHandler.handle(experiment, data);
+
+      expect(experiment.results.length === 0);
+    });
+  });
+});
diff --git a/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression.handler.ts b/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression.handler.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dc9236ff40ee25e7522d629c4cea568e75fa00d5
--- /dev/null
+++ b/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression.handler.ts
@@ -0,0 +1,101 @@
+import { isNumber } from '../../../../../common/utils/shared.utils';
+import { Experiment } from '../../../../models/experiment/experiment.model';
+import {
+  TableResult,
+  TableStyle,
+} from '../../../../models/result/table-result.model';
+import BaseHandler from '../base.handler';
+
+const NUMBER_PRECISION = 4;
+const ALGO_NANE = 'linear_regression';
+const lookupDict = {
+  dependent_var: 'Dependent variable',
+  n_obs: 'Number of observations',
+  df_resid: 'Residual degrees of freedom',
+  df_model: 'Model degrees of freedom',
+  rse: 'Residual standard error',
+  r_squared: 'R-squared',
+  r_squared_adjusted: 'Adjusted R-squared',
+  f_stat: 'F statistic',
+  f_pvalue: 'P{>F}',
+  indep_vars: 'Independent variables',
+  coefficients: 'Coefficients',
+  std_err: 'Std.Err.',
+  t_stats: 't-statistics',
+  pvalues: 'P{>|t|}',
+  lower_ci: 'Lower 95% c.i.',
+  upper_ci: 'Upper 95% c.i.',
+};
+
+export default class LinearRegressionHandler extends BaseHandler {
+  private getModel(data: any): TableResult | undefined {
+    const excepts = ['n_obs'];
+    const tableModel: TableResult = {
+      name: 'Model',
+      tableStyle: TableStyle.NORMAL,
+      headers: ['name', 'value'].map((name) => ({ name, type: 'string' })),
+      data: [
+        'dependent_var',
+        'n_obs',
+        'df_resid',
+        'df_model',
+        'rse',
+        'r_squared',
+        'r_squared_adjusted',
+        'f_stat',
+        'f_pvalue',
+      ].map((name) => [
+        lookupDict[name],
+        isNumber(data[name]) && !excepts.includes(name)
+          ? data[name].toPrecision(NUMBER_PRECISION)
+          : data[name],
+      ]),
+    };
+
+    return tableModel;
+  }
+
+  private getCoefficients(data: any): TableResult | undefined {
+    const keys = [
+      'indep_vars',
+      'coefficients',
+      'std_err',
+      't_stats',
+      'pvalues',
+      'lower_ci',
+      'upper_ci',
+    ];
+    const tabKeys = keys.slice(1);
+
+    const tableCoef: TableResult = {
+      name: 'Coefficients',
+      tableStyle: TableStyle.NORMAL,
+      headers: keys.map((name) => ({
+        name: lookupDict[name],
+        type: 'string',
+      })),
+      data: data.indep_vars.map((variable, i) => {
+        const row = tabKeys
+          .map((key) => data[key][i])
+          .map((val) =>
+            isNumber(val) ? val.toPrecision(NUMBER_PRECISION) : val,
+          );
+        row.unshift(variable);
+        return row;
+      }),
+    };
+
+    return tableCoef;
+  }
+
+  handle(experiment: Experiment, data: any): void {
+    if (experiment.algorithm.name.toLowerCase() !== ALGO_NANE)
+      return super.handle(experiment, data);
+
+    const model = this.getModel(data);
+    if (model) experiment.results.push(model);
+
+    const coefs = this.getCoefficients(data);
+    if (coefs) experiment.results.push(coefs);
+  }
+}
diff --git a/api/src/engine/connectors/exareme/handlers/base.handler.ts b/api/src/engine/connectors/exareme/handlers/base.handler.ts
index 6f84123e0514cfe937c48366bccbacd7763d99cb..5934cf82008004b964a52601b2af21c08d9357d3 100644
--- a/api/src/engine/connectors/exareme/handlers/base.handler.ts
+++ b/api/src/engine/connectors/exareme/handlers/base.handler.ts
@@ -12,7 +12,7 @@ export default abstract class BaseHandler implements ResultHandler {
     return h;
   }
 
-  handle(partialExperiment: Experiment, data: unknown): void {
-    this.next?.handle(partialExperiment, data);
+  handle(experiment: Experiment, data: unknown): void {
+    this.next?.handle(experiment, data);
   }
 }
diff --git a/api/src/engine/connectors/exareme/handlers/index.ts b/api/src/engine/connectors/exareme/handlers/index.ts
index 2d7d607d5dac7ebc553536c30f1a3b4c2acd6514..1dca0fce5e88d570f1f7f754be57a30b766d1045 100644
--- a/api/src/engine/connectors/exareme/handlers/index.ts
+++ b/api/src/engine/connectors/exareme/handlers/index.ts
@@ -3,6 +3,7 @@ import AnovaOneWayHandler from './algorithms/anova-one-way.handler';
 import AreaHandler from './algorithms/area.handler';
 import DescriptiveHandler from './algorithms/descriptive.handler';
 import HeatMapHandler from './algorithms/heat-map.handler';
+import LinearRegressionHandler from './algorithms/linear-regression.handler';
 import PCAHandler from './algorithms/PCA.handler';
 import PearsonHandler from './algorithms/pearson.handler';
 import RawHandler from './algorithms/raw.handler';
@@ -15,6 +16,7 @@ start
   .setNext(new HeatMapHandler())
   .setNext(new AnovaOneWayHandler())
   .setNext(new PCAHandler())
+  .setNext(new LinearRegressionHandler())
   .setNext(new RawHandler()); // should be last handler as it works as a fallback (if other handlers could not process the results)
 
 export default (exp: Experiment, data: unknown): Experiment => {
diff --git a/api/src/engine/engine.resolver.ts b/api/src/engine/engine.resolver.ts
index 9a20cc5db5cfca459099aeabbc722e824cc55085..84ff2afbb7f04f97af6072d29fe25fd7d380079c 100644
--- a/api/src/engine/engine.resolver.ts
+++ b/api/src/engine/engine.resolver.ts
@@ -4,10 +4,10 @@ import { Args, Query, Resolver } from '@nestjs/graphql';
 import { Request } from 'express';
 import { Public } from 'src/auth/decorators/public.decorator';
 import { GlobalAuthGuard } from 'src/auth/guards/global-auth.guard';
+import { parseToBoolean } from 'src/common/utils/shared.utils';
 import { Md5 } from 'ts-md5';
 import { authConstants } from '../auth/auth-constants';
 import { GQLRequest } from '../common/decorators/gql-request.decoractor';
-import { parseToBoolean } from '../common/utilities';
 import {
   ENGINE_MODULE_OPTIONS,
   ENGINE_ONTOLOGY_URL,