diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a99622b60a0fc5de6105d2845a71ffa5a0a2de8b
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,24 @@
+image: docker:20
+services:
+  - docker:20-dind
+
+stages:
+  - build
+  - release
+  - deploy
+
+variables:
+  CONTAINER_BASE_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
+
+before_script:
+  - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
+
+build:
+  stage: build
+  script:
+    - docker build -t $CONTAINER_BASE_IMAGE ./api
+    - docker push $CONTAINER_BASE_IMAGE
+  only:
+    - main
+    - develop
+    - /^release-.*$/
diff --git a/api/.env.defaults b/api/.env.defaults
new file mode 100644
index 0000000000000000000000000000000000000000..ad9987672442b84904a71d8526b7e050267b3924
--- /dev/null
+++ b/api/.env.defaults
@@ -0,0 +1,3 @@
+ENGINE_TYPE=exareme
+ENGINE_BASE_URL=http://127.0.0.1:8080/services/
+GATEWAY_PORT=8081
diff --git a/api/.eslintrc.js b/api/.eslintrc.js
index f6c62bee279534a2a2ce7ee4424670655fbd2a4c..8e34f1ea92b73633f2be969865474d6ca4e5cb9c 100644
--- a/api/.eslintrc.js
+++ b/api/.eslintrc.js
@@ -20,5 +20,6 @@ module.exports = {
     '@typescript-eslint/explicit-function-return-type': 'off',
     '@typescript-eslint/explicit-module-boundary-types': 'off',
     '@typescript-eslint/no-explicit-any': 'off',
+    'prettier/prettier': ['error', { "endOfLine": "auto"}, { usePrettierrc: true }],
   },
 };
diff --git a/api/.gitignore b/api/.gitignore
index 22f55adc5647206db11558139164c3deb77f5c01..5ecdd35f3ffc85cf6a6c620d543d0cb4d31c7354 100644
--- a/api/.gitignore
+++ b/api/.gitignore
@@ -32,4 +32,7 @@ lerna-debug.log*
 !.vscode/settings.json
 !.vscode/tasks.json
 !.vscode/launch.json
-!.vscode/extensions.json
\ No newline at end of file
+!.vscode/extensions.json
+
+# env
+.env
\ No newline at end of file
diff --git a/api/.husky/pre-commit b/api/.husky/pre-commit
new file mode 100755
index 0000000000000000000000000000000000000000..6f13889f0984140d0b831a129b3818e7f3760425
--- /dev/null
+++ b/api/.husky/pre-commit
@@ -0,0 +1,5 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+cd api
+npm run lint
diff --git a/api/Dockerfile b/api/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..d843415a4cad9d1908b9afa723589ed6b75579cb
--- /dev/null
+++ b/api/Dockerfile
@@ -0,0 +1,33 @@
+FROM node:12.13-alpine As development
+
+WORKDIR /usr/src/app
+
+COPY package*.json ./
+
+RUN npm ci --development
+
+COPY . .
+
+RUN npm run build
+
+RUN npm run test
+
+FROM node:12.13-alpine as production
+
+ARG NODE_ENV=production
+ENV NODE_ENV=${NODE_ENV}
+
+RUN apk update && apk add bash
+RUN apk add --no-cache bash
+
+WORKDIR /usr/src/app
+
+COPY package*.json ./
+
+RUN npm ci --production
+
+COPY . .
+
+COPY --from=development /usr/src/app/dist ./dist
+
+CMD ["node", "dist/main"]
diff --git a/api/package-lock.json b/api/package-lock.json
index c564f292ce87de51fe2f29be8fab6f19a2c06ceb..88182859b9a42510792332bff648111830b823b3 100644
--- a/api/package-lock.json
+++ b/api/package-lock.json
@@ -11,6 +11,7 @@
       "dependencies": {
         "@nestjs/axios": "^0.0.1",
         "@nestjs/common": "^8.0.0",
+        "@nestjs/config": "^1.0.1",
         "@nestjs/core": "^8.0.0",
         "@nestjs/graphql": "^9.0.4",
         "@nestjs/platform-express": "^8.0.0",
@@ -18,6 +19,8 @@
         "apollo-server-express": "^3.3.0",
         "axios": "^0.21.1",
         "graphql": "^15.5.3",
+        "graphql-type-json": "^0.3.2",
+        "jsonata": "^1.8.5",
         "reflect-metadata": "^0.1.13",
         "rimraf": "^3.0.2",
         "rxjs": "^7.2.0"
@@ -35,6 +38,7 @@
         "eslint": "^7.30.0",
         "eslint-config-prettier": "^8.3.0",
         "eslint-plugin-prettier": "^3.4.0",
+        "husky": "^7.0.2",
         "jest": "^27.0.6",
         "prettier": "^2.3.2",
         "supertest": "^6.1.3",
@@ -1765,6 +1769,32 @@
         }
       }
     },
+    "node_modules/@nestjs/config": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-1.0.1.tgz",
+      "integrity": "sha512-azMl4uYlFIhYsywFxPJT81RxF3Pnn0TZW3EEmr0Wa0Wex8R2xpvBNrCcrOgW3TB1xGMP7eqBrlfsVh5ZP82szg==",
+      "dependencies": {
+        "dotenv": "10.0.0",
+        "dotenv-expand": "5.1.0",
+        "lodash.get": "4.4.2",
+        "lodash.has": "4.5.2",
+        "lodash.set": "4.3.2",
+        "uuid": "8.3.2"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^7.0.0 || ^8.0.0",
+        "reflect-metadata": "^0.1.13",
+        "rxjs": "^6.0.0 || ^7.2.0"
+      }
+    },
+    "node_modules/@nestjs/config/node_modules/dotenv": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
+      "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/@nestjs/core": {
       "version": "8.0.6",
       "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-8.0.6.tgz",
@@ -4392,6 +4422,11 @@
         "node": ">=10"
       }
     },
+    "node_modules/dotenv-expand": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
+      "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA=="
+    },
     "node_modules/ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -5548,6 +5583,14 @@
         "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0"
       }
     },
+    "node_modules/graphql-type-json": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/graphql-type-json/-/graphql-type-json-0.3.2.tgz",
+      "integrity": "sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg==",
+      "peerDependencies": {
+        "graphql": ">=0.8.0"
+      }
+    },
     "node_modules/graphql-ws": {
       "version": "5.4.0",
       "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.4.0.tgz",
@@ -5695,6 +5738,21 @@
         "node": ">=10.17.0"
       }
     },
+    "node_modules/husky": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.2.tgz",
+      "integrity": "sha512-8yKEWNX4z2YsofXAMT7KvA1g8p+GxtB1ffV8XtpAEGuXNAbCV5wdNKH+qTpw8SM9fh4aMPDR+yQuKfgnreyZlg==",
+      "dev": true,
+      "bin": {
+        "husky": "lib/bin.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/typicode"
+      }
+    },
     "node_modules/iconv-lite": {
       "version": "0.4.24",
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -7124,6 +7182,14 @@
         "node": ">=6"
       }
     },
+    "node_modules/jsonata": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/jsonata/-/jsonata-1.8.5.tgz",
+      "integrity": "sha512-ilDyTBkg6qhNoNVr8PUPzz5GYvRK+REKOM5MdOGzH2y6V4yvPRMegSvbZLpbTtI0QAgz09QM7drDhSHUlwp9pA==",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
     "node_modules/jsonc-parser": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
@@ -7217,12 +7283,27 @@
       "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
       "dev": true
     },
+    "node_modules/lodash.get": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+      "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
+    },
+    "node_modules/lodash.has": {
+      "version": "4.5.2",
+      "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz",
+      "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI="
+    },
     "node_modules/lodash.merge": {
       "version": "4.6.2",
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
       "dev": true
     },
+    "node_modules/lodash.set": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
+      "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM="
+    },
     "node_modules/lodash.sortby": {
       "version": "4.7.0",
       "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
@@ -11891,6 +11972,26 @@
         "uuid": "8.3.2"
       }
     },
+    "@nestjs/config": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-1.0.1.tgz",
+      "integrity": "sha512-azMl4uYlFIhYsywFxPJT81RxF3Pnn0TZW3EEmr0Wa0Wex8R2xpvBNrCcrOgW3TB1xGMP7eqBrlfsVh5ZP82szg==",
+      "requires": {
+        "dotenv": "10.0.0",
+        "dotenv-expand": "5.1.0",
+        "lodash.get": "4.4.2",
+        "lodash.has": "4.5.2",
+        "lodash.set": "4.3.2",
+        "uuid": "8.3.2"
+      },
+      "dependencies": {
+        "dotenv": {
+          "version": "10.0.0",
+          "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
+          "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="
+        }
+      }
+    },
     "@nestjs/core": {
       "version": "8.0.6",
       "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-8.0.6.tgz",
@@ -13914,6 +14015,11 @@
       "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
       "peer": true
     },
+    "dotenv-expand": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
+      "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA=="
+    },
     "ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -14791,6 +14897,12 @@
         "tslib": "^2.1.0"
       }
     },
+    "graphql-type-json": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/graphql-type-json/-/graphql-type-json-0.3.2.tgz",
+      "integrity": "sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg==",
+      "requires": {}
+    },
     "graphql-ws": {
       "version": "5.4.0",
       "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.4.0.tgz",
@@ -14901,6 +15013,12 @@
       "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
       "dev": true
     },
+    "husky": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.2.tgz",
+      "integrity": "sha512-8yKEWNX4z2YsofXAMT7KvA1g8p+GxtB1ffV8XtpAEGuXNAbCV5wdNKH+qTpw8SM9fh4aMPDR+yQuKfgnreyZlg==",
+      "dev": true
+    },
     "iconv-lite": {
       "version": "0.4.24",
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -15985,6 +16103,11 @@
         "minimist": "^1.2.5"
       }
     },
+    "jsonata": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/jsonata/-/jsonata-1.8.5.tgz",
+      "integrity": "sha512-ilDyTBkg6qhNoNVr8PUPzz5GYvRK+REKOM5MdOGzH2y6V4yvPRMegSvbZLpbTtI0QAgz09QM7drDhSHUlwp9pA=="
+    },
     "jsonc-parser": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
@@ -16061,12 +16184,27 @@
       "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
       "dev": true
     },
+    "lodash.get": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+      "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
+    },
+    "lodash.has": {
+      "version": "4.5.2",
+      "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz",
+      "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI="
+    },
     "lodash.merge": {
       "version": "4.6.2",
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
       "dev": true
     },
+    "lodash.set": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
+      "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM="
+    },
     "lodash.sortby": {
       "version": "4.7.0",
       "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
diff --git a/api/package.json b/api/package.json
index c467c3162a253b554f8c90d90e10cff550646752..1885442d70f4ac545ac7fc662ec9bef27d1d21de 100644
--- a/api/package.json
+++ b/api/package.json
@@ -18,11 +18,13 @@
     "test:watch": "jest --watch",
     "test:cov": "jest --coverage",
     "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
-    "test:e2e": "jest --config ./test/jest-e2e.json"
+    "test:e2e": "jest --config ./test/jest-e2e.json",
+    "prepare": "cd .. && husky install api/.husky"
   },
   "dependencies": {
     "@nestjs/axios": "^0.0.1",
     "@nestjs/common": "^8.0.0",
+    "@nestjs/config": "^1.0.1",
     "@nestjs/core": "^8.0.0",
     "@nestjs/graphql": "^9.0.4",
     "@nestjs/platform-express": "^8.0.0",
@@ -30,6 +32,8 @@
     "apollo-server-express": "^3.3.0",
     "axios": "^0.21.1",
     "graphql": "^15.5.3",
+    "graphql-type-json": "^0.3.2",
+    "jsonata": "^1.8.5",
     "reflect-metadata": "^0.1.13",
     "rimraf": "^3.0.2",
     "rxjs": "^7.2.0"
@@ -47,6 +51,7 @@
     "eslint": "^7.30.0",
     "eslint-config-prettier": "^8.3.0",
     "eslint-plugin-prettier": "^3.4.0",
+    "husky": "^7.0.2",
     "jest": "^27.0.6",
     "prettier": "^2.3.2",
     "supertest": "^6.1.3",
diff --git a/api/src/common/interfaces/utilities.interface.ts b/api/src/common/interfaces/utilities.interface.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ddf5dedfa9585bdffa46ef5a90ca643d49eacf7a
--- /dev/null
+++ b/api/src/common/interfaces/utilities.interface.ts
@@ -0,0 +1 @@
+export type Dictionary<T> = { [key: string]: T };
diff --git a/api/src/engine/connectors/csv/main.connector.ts b/api/src/engine/connectors/csv/main.connector.ts
new file mode 100644
index 0000000000000000000000000000000000000000..05864adb55c7141de3ec49bd6854b5fdcdb793ee
--- /dev/null
+++ b/api/src/engine/connectors/csv/main.connector.ts
@@ -0,0 +1,177 @@
+import { firstValueFrom, Observable } from 'rxjs';
+import { IEngineOptions, IEngineService } from 'src/engine/engine.interfaces';
+import { Domain } from 'src/engine/models/domain.model';
+import { ExperimentCreateInput } from 'src/engine/models/experiment/input/experiment-create.input';
+import {
+  Experiment,
+  PartialExperiment,
+} from 'src/engine/models/experiment/experiment.model';
+import { ListExperiments } from 'src/engine/models/experiment/list-experiments.model';
+import { ExperimentEditInput } from 'src/engine/models/experiment/input/experiment-edit.input';
+import { Algorithm } from 'src/engine/models/experiment/algorithm.model';
+import { HttpService } from '@nestjs/axios';
+import { Group } from 'src/engine/models/group.model';
+import { Dictionary } from 'src/common/interfaces/utilities.interface';
+
+export default class CSVService implements IEngineService {
+  constructor(
+    private readonly options: IEngineOptions,
+    private readonly httpService: HttpService,
+  ) {}
+
+  logout() {
+    throw new Error('Method not implemented.');
+  }
+
+  getAlgorithms(): Algorithm[] | Promise<Algorithm[]> {
+    throw new Error('Method not implemented.');
+  }
+
+  createExperiment(
+    data: ExperimentCreateInput,
+    isTransient: boolean,
+  ): Experiment | Promise<Experiment> {
+    throw new Error('Method not implemented.');
+  }
+
+  listExperiments(
+    page: number,
+    name: string,
+  ): ListExperiments | Promise<ListExperiments> {
+    throw new Error('Method not implemented.');
+  }
+
+  getExperiment(uuid: string): Experiment | Promise<Experiment> {
+    throw new Error('Method not implemented.');
+  }
+
+  removeExperiment(
+    uuid: string,
+  ): PartialExperiment | Promise<PartialExperiment> {
+    throw new Error('Method not implemented.');
+  }
+
+  editExperient(
+    uuid: string,
+    expriment: ExperimentEditInput,
+  ): Experiment | Promise<Experiment> {
+    throw new Error('Method not implemented.');
+  }
+
+  async getDomains(): Promise<Domain[]> {
+    const path = this.options.baseurl;
+
+    const { data } = await firstValueFrom(this.httpService.get<string>(path));
+
+    const rows = data
+      .split('\r\n')
+      .map((row) => row.split('\t').filter((i) => i))
+      .filter((row) => row.length >= 2);
+
+    rows.shift(); // remove headers
+
+    const vars = [];
+    const groups: Dictionary<Group> = {};
+    const rootGroup: Group = {
+      id: 'Global group',
+      groups: [],
+    };
+
+    rows.forEach((row) => {
+      const variable = {
+        id: row[0].toLowerCase(),
+        label: row[0],
+      };
+
+      row.shift(); // get ride of the variable name, keep only groups
+
+      vars.push(variable);
+
+      row
+        .filter((group) => !groups[group.toLowerCase()])
+        .forEach((group, i) => {
+          const groupId = group.toLowerCase();
+          if (i === 0) rootGroup.groups.push(groupId);
+          groups[groupId] = {
+            id: groupId,
+            label: group,
+            variables: [],
+            groups: [],
+          };
+        });
+
+      const groupId = row[row.length - 1].toLowerCase(); // group's variable container
+
+      groups[groupId].variables.push(variable.id); // add variable
+
+      row
+        .reverse()
+        .map((group) => group.toLowerCase())
+        .forEach((group, i) => {
+          const groupId = group.toLowerCase();
+
+          if (i !== row.length - 1) {
+            const parentId = row[i + 1].toLowerCase();
+            if (groups[parentId].groups.indexOf(groupId) === -1)
+              groups[parentId].groups.push(groupId);
+          }
+        });
+    });
+
+    rootGroup.groups = [...new Set(rootGroup.groups)]; // get distinct values
+
+    return [
+      {
+        id: 'Dummy',
+        label: 'Dummy',
+        datasets: [{ id: 'DummyDataset', label: 'Dummy Dataset' }],
+        groups: Object.values(groups),
+        rootGroup: rootGroup,
+        variables: vars,
+      },
+    ];
+  }
+
+  getActiveUser(): string {
+    const dummyUser = {
+      username: 'anonymous',
+      subjectId: 'anonymousId',
+      fullname: 'anonymous',
+      email: 'anonymous@anonymous.com',
+      agreeNDA: true,
+    };
+    return JSON.stringify(dummyUser);
+  }
+
+  editActiveUser(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  getExperimentREST(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  deleteExperiment(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  editExperimentREST(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  startExperimentTransient(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  startExperiment(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  getExperiments(): string {
+    return '[]';
+  }
+
+  getAlgorithmsREST(): string {
+    return '[]';
+  }
+}
diff --git a/api/src/engine/connectors/datashield/main.connector.ts b/api/src/engine/connectors/datashield/main.connector.ts
index f3a15d7680b7bb4f0d167b565160f8e010f25d13..4df8aa14bf1fda0bdec3a4d21c98cc79277cdd83 100644
--- a/api/src/engine/connectors/datashield/main.connector.ts
+++ b/api/src/engine/connectors/datashield/main.connector.ts
@@ -1,10 +1,92 @@
-import { HttpService } from "@nestjs/axios";
-import { IEngineOptions, IEngineService } from "src/engine/engine.interface";
+import { Observable } from 'rxjs';
+import { IEngineService } from 'src/engine/engine.interfaces';
+import { Domain } from 'src/engine/models/domain.model';
+import { ExperimentCreateInput } from 'src/engine/models/experiment/input/experiment-create.input';
+import {
+  Experiment,
+  PartialExperiment,
+} from 'src/engine/models/experiment/experiment.model';
+import { ListExperiments } from 'src/engine/models/experiment/list-experiments.model';
+import { ExperimentEditInput } from 'src/engine/models/experiment/input/experiment-edit.input';
+import { Algorithm } from 'src/engine/models/experiment/algorithm.model';
 
 export default class DataShieldService implements IEngineService {
-    constructor(private readonly options: IEngineOptions, private readonly httpService: HttpService) { }
+  logout(): void {
+    throw new Error('Method not implemented.');
+  }
 
-    demo(): string {
-        return "datashield";
-    }
-}
\ No newline at end of file
+  getAlgorithms(): Algorithm[] | Promise<Algorithm[]> {
+    throw new Error('Method not implemented.');
+  }
+
+  createExperiment(
+    data: ExperimentCreateInput,
+    isTransient: boolean,
+  ): Experiment | Promise<Experiment> {
+    throw new Error('Method not implemented.');
+  }
+
+  listExperiments(
+    page: number,
+    name: string,
+  ): ListExperiments | Promise<ListExperiments> {
+    throw new Error('Method not implemented.');
+  }
+
+  getExperiment(uuid: string): Experiment | Promise<Experiment> {
+    throw new Error('Method not implemented.');
+  }
+
+  removeExperiment(
+    uuid: string,
+  ): PartialExperiment | Promise<PartialExperiment> {
+    throw new Error('Method not implemented.');
+  }
+
+  editExperient(
+    uuid: string,
+    expriment: ExperimentEditInput,
+  ): Experiment | Promise<Experiment> {
+    throw new Error('Method not implemented.');
+  }
+
+  getDomains(): Domain[] {
+    throw new Error('Method not implemented.');
+  }
+
+  getActiveUser(): string {
+    throw new Error('Method not implemented.');
+  }
+
+  editActiveUser(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  getExperimentREST(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  deleteExperiment(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  editExperimentREST(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  startExperimentTransient(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  startExperiment(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  getExperiments(): string {
+    throw new Error('Method not implemented.');
+  }
+
+  getAlgorithmsREST(): string {
+    throw new Error('Method not implemented.');
+  }
+}
diff --git a/api/src/engine/connectors/exareme/converters.ts b/api/src/engine/connectors/exareme/converters.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e9bc1d7c51908d9b833500d70e573774b908a221
--- /dev/null
+++ b/api/src/engine/connectors/exareme/converters.ts
@@ -0,0 +1,197 @@
+import { Category } from 'src/engine/models/category.model';
+import { AlgorithmParameter } from 'src/engine/models/experiment/algorithm-parameter.model';
+import { Algorithm } from 'src/engine/models/experiment/algorithm.model';
+import { Experiment } from 'src/engine/models/experiment/experiment.model';
+import { ExperimentCreateInput } from 'src/engine/models/experiment/input/experiment-create.input';
+import { Group } from 'src/engine/models/group.model';
+import { ResultUnion } from 'src/engine/models/result/common/result-union.model';
+import {
+  GroupResult,
+  GroupsResult,
+} from 'src/engine/models/result/groups-result.model';
+import { RawResult } from 'src/engine/models/result/raw-result.model';
+import { Variable } from 'src/engine/models/variable.model';
+import { Entity } from './interfaces/entity.interface';
+import { ExperimentData } from './interfaces/experiment/experiment.interface';
+import { ResultExperiment } from './interfaces/experiment/result-experiment.interface';
+import { Hierarchy } from './interfaces/hierarchy.interface';
+import { VariableEntity } from './interfaces/variable-entity.interface';
+import {
+  descriptiveModelToTables,
+  descriptiveSingleToTables,
+  transformToAlgorithms,
+  transformToExperiment,
+} from './transformations';
+
+export const dataToGroup = (data: Hierarchy): Group => {
+  return {
+    id: data.code,
+    label: data.label,
+    groups: data.groups
+      ? data.groups.map(dataToGroup).map((group) => group.id)
+      : [],
+    variables: data.variables
+      ? data.variables.map((data: VariableEntity) => data.code)
+      : [],
+  };
+};
+
+export const dataToCategory = (data: Entity): Category => {
+  return {
+    id: data.code,
+    label: data.label,
+  };
+};
+
+export const dataToVariable = (data: VariableEntity): Variable => {
+  return {
+    id: data.code,
+    label: data.label,
+    type: data.type,
+    description: data.description,
+    enumerations: data.enumerations
+      ? data.enumerations.map(dataToCategory)
+      : [],
+    groups: [],
+  };
+};
+
+const algoParamInputToData = (param: AlgorithmParameter) => {
+  return {
+    name: param.name,
+    label: param.name,
+    value: param.value.join(','),
+  };
+};
+
+export const experimentInputToData = (data: ExperimentCreateInput) => {
+  const formula =
+    ((data.transformations?.length > 0 || data.interactions?.length > 0) && {
+      single:
+        data.transformations?.map((t) => ({
+          var_name: t.name,
+          unary_operation: t.operation,
+        })) || [],
+      interactions:
+        data.interactions?.map((v) =>
+          v.reduce((a, e, i) => ({ ...a, [`var${i + 1}`]: e }), {}),
+        ) || [],
+    }) ||
+    null;
+
+  return {
+    algorithm: {
+      parameters: [
+        {
+          name: 'dataset',
+          label: 'dataset',
+          value: data.datasets.join(','),
+        },
+        {
+          name: 'filter',
+          label: 'filter',
+          value: data.filter,
+        },
+        {
+          name: 'pathology',
+          label: 'pathology',
+          value: data.domain,
+        },
+        {
+          name: 'y',
+          label: 'y',
+          value: data.variables.join(','),
+        },
+        ...(formula
+          ? [
+              {
+                name: 'formula',
+                value: JSON.stringify(formula),
+              },
+            ]
+          : []),
+      ].concat(data.algorithm.parameters.map(algoParamInputToData)),
+      type: data.algorithm.type ?? 'string',
+      name: data.algorithm.name,
+    },
+    name: data.name,
+  };
+};
+
+export const descriptiveDataToTableResult = (
+  data: ResultExperiment,
+): GroupsResult[] => {
+  const result = new GroupsResult();
+
+  result.groups = [
+    new GroupResult({
+      name: 'Variables',
+      description: 'Descriptive statistics for the variables of interest.',
+      results: descriptiveSingleToTables.evaluate(data),
+    }),
+  ];
+
+  result.groups.push(
+    new GroupResult({
+      name: 'Model',
+      description:
+        'Intersection table for the variables of interest as it appears in the experiment.',
+      results: descriptiveModelToTables.evaluate(data),
+    }),
+  );
+
+  return [result];
+};
+
+export const dataToExperiment = (data: ExperimentData): Experiment => {
+  const expTransform = transformToExperiment.evaluate(data);
+
+  const exp: Experiment = {
+    ...expTransform,
+    results: [],
+  };
+
+  exp.results = data.result
+    ? data.result
+        .map((result) => dataToResult(result, exp.algorithm.name))
+        .flat()
+    : [];
+
+  return exp;
+};
+
+export const dataToAlgorithms = (data: string): Algorithm[] => {
+  return transformToAlgorithms.evaluate(data);
+};
+
+export const dataToRaw = (result: ResultExperiment): RawResult[] => {
+  return [
+    {
+      rawdata: result.data,
+    },
+  ];
+};
+
+export const dataToResult = (
+  result: ResultExperiment,
+  algo: string,
+): Array<typeof ResultUnion> => {
+  switch (result.type.toLowerCase()) {
+    case 'application/json':
+      return dataJSONtoResult(result, algo);
+    default:
+      return dataToRaw(result);
+  }
+};
+
+export const dataJSONtoResult = (
+  result: ResultExperiment,
+  algo: string,
+): Array<typeof ResultUnion> => {
+  switch (algo.toLowerCase()) {
+    case 'descriptive_stats':
+      return descriptiveDataToTableResult(result);
+    default:
+      return [];
+  }
+};
diff --git a/api/src/engine/connectors/exareme/interfaces/entity.interface.ts b/api/src/engine/connectors/exareme/interfaces/entity.interface.ts
new file mode 100644
index 0000000000000000000000000000000000000000..38e3ab4125544a5b3eedf6f824533042f3c9d7d3
--- /dev/null
+++ b/api/src/engine/connectors/exareme/interfaces/entity.interface.ts
@@ -0,0 +1,4 @@
+export interface Entity {
+  code: string;
+  label?: string;
+}
diff --git a/api/src/engine/connectors/exareme/interfaces/experiment/experiment.interface.ts b/api/src/engine/connectors/exareme/interfaces/experiment/experiment.interface.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7412cb0560c9eda88fe61dd4ebbf8128c94e2c62
--- /dev/null
+++ b/api/src/engine/connectors/exareme/interfaces/experiment/experiment.interface.ts
@@ -0,0 +1,11 @@
+import { ResultExperiment } from './result-experiment.interface';
+
+export interface ExperimentData {
+  name: string;
+  uuid?: string;
+  status?: string;
+  createdBy?: string;
+  shared?: boolean;
+  viewed?: boolean;
+  result?: ResultExperiment[];
+}
diff --git a/api/src/engine/connectors/exareme/interfaces/experiment/experiments.interface.ts b/api/src/engine/connectors/exareme/interfaces/experiment/experiments.interface.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5ef0c0f84568d7a7e87dd1f759c1db3b17e8066a
--- /dev/null
+++ b/api/src/engine/connectors/exareme/interfaces/experiment/experiments.interface.ts
@@ -0,0 +1,8 @@
+import { ExperimentData } from './experiment.interface';
+
+export interface ExperimentsData {
+  experiments: ExperimentData[];
+  currentpage?: number;
+  totalExperiments?: number;
+  totalPages?: number;
+}
diff --git a/api/src/engine/connectors/exareme/interfaces/experiment/result-experiment.interface.ts b/api/src/engine/connectors/exareme/interfaces/experiment/result-experiment.interface.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e655151c9e7b113a2dbff0402d8e99c61534adee
--- /dev/null
+++ b/api/src/engine/connectors/exareme/interfaces/experiment/result-experiment.interface.ts
@@ -0,0 +1,4 @@
+export interface ResultExperiment {
+  data: unknown;
+  type: string;
+}
diff --git a/api/src/engine/connectors/exareme/interfaces/hierarchy.interface.ts b/api/src/engine/connectors/exareme/interfaces/hierarchy.interface.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d5017f13c8d24134de164eb2a4da9064c69c4fe0
--- /dev/null
+++ b/api/src/engine/connectors/exareme/interfaces/hierarchy.interface.ts
@@ -0,0 +1,8 @@
+import { VariableEntity } from './variable-entity.interface';
+
+export interface Hierarchy {
+  code: string;
+  label: string;
+  groups: Hierarchy[];
+  variables: VariableEntity[];
+}
diff --git a/api/src/engine/connectors/exareme/interfaces/pathology.interface.ts b/api/src/engine/connectors/exareme/interfaces/pathology.interface.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e71e3a0251af2e03aa7e7995e6129609a9140cd0
--- /dev/null
+++ b/api/src/engine/connectors/exareme/interfaces/pathology.interface.ts
@@ -0,0 +1,9 @@
+import { Hierarchy } from './hierarchy.interface';
+import { VariableEntity } from './variable-entity.interface';
+
+export interface Pathology {
+  code: string;
+  label: string;
+  datasets: VariableEntity[];
+  metadataHierarchy: Hierarchy;
+}
diff --git a/api/src/engine/connectors/exareme/interfaces/variable-entity.interface.ts b/api/src/engine/connectors/exareme/interfaces/variable-entity.interface.ts
new file mode 100644
index 0000000000000000000000000000000000000000..06c880dcb97fb9e203a51067fe265c75492fef58
--- /dev/null
+++ b/api/src/engine/connectors/exareme/interfaces/variable-entity.interface.ts
@@ -0,0 +1,9 @@
+import { Entity } from './entity.interface';
+
+export interface VariableEntity extends Entity {
+  type?: 'nominal' | 'ordinal' | 'real' | 'integer' | 'text' | 'date';
+  description?: string;
+  enumerations?: Entity[];
+  group?: Entity[];
+  info?: string;
+}
diff --git a/api/src/engine/connectors/exareme/main.connector.ts b/api/src/engine/connectors/exareme/main.connector.ts
index e80647b1c6fa3cfd4c5e03d3315a1b8e1abb2ebd..f096ddde5642606b8f3196e2e00f182d326268b2 100644
--- a/api/src/engine/connectors/exareme/main.connector.ts
+++ b/api/src/engine/connectors/exareme/main.connector.ts
@@ -1,10 +1,256 @@
-import { HttpService } from "@nestjs/axios";
-import { IEngineOptions, IEngineService } from "src/engine/engine.interface";
+import { HttpService } from '@nestjs/axios';
+import {
+  BadRequestException,
+  HttpException,
+  HttpStatus,
+  Inject,
+  Injectable,
+} from '@nestjs/common';
+import { REQUEST } from '@nestjs/core';
+import { Request } from 'express';
+import { firstValueFrom, map, Observable } from 'rxjs';
+import { ENGINE_MODULE_OPTIONS } from 'src/engine/engine.constants';
+import { IEngineOptions, IEngineService } from 'src/engine/engine.interfaces';
+import { Domain } from 'src/engine/models/domain.model';
+import { Algorithm } from 'src/engine/models/experiment/algorithm.model';
+import {
+  Experiment,
+  PartialExperiment,
+} from 'src/engine/models/experiment/experiment.model';
+import { ExperimentCreateInput } from 'src/engine/models/experiment/input/experiment-create.input';
+import { ExperimentEditInput } from 'src/engine/models/experiment/input/experiment-edit.input';
+import { ListExperiments } from 'src/engine/models/experiment/list-experiments.model';
+import { Group } from 'src/engine/models/group.model';
+import { Variable } from 'src/engine/models/variable.model';
+import {
+  dataToAlgorithms,
+  dataToCategory,
+  dataToExperiment,
+  dataToGroup,
+  dataToVariable,
+  experimentInputToData,
+} from './converters';
+import { ExperimentData } from './interfaces/experiment/experiment.interface';
+import { ExperimentsData } from './interfaces/experiment/experiments.interface';
+import { Hierarchy } from './interfaces/hierarchy.interface';
+import { Pathology } from './interfaces/pathology.interface';
 
+@Injectable()
 export default class ExaremeService implements IEngineService {
-    constructor(private readonly options: IEngineOptions, private readonly httpService: HttpService) { }
+  constructor(
+    @Inject(ENGINE_MODULE_OPTIONS) private readonly options: IEngineOptions,
+    private readonly httpService: HttpService,
+    @Inject(REQUEST) private readonly req: Request,
+  ) {}
 
-    demo(): string {
-        return "exareme"
+  async logout() {
+    const path = `${this.options.baseurl}logout`;
+
+    await firstValueFrom(this.httpService.get(path));
+  }
+
+  async createExperiment(
+    data: ExperimentCreateInput,
+    isTransient = false,
+  ): Promise<Experiment> {
+    const form = experimentInputToData(data);
+
+    const path =
+      this.options.baseurl + `experiments${isTransient ? '/transient' : ''}`;
+
+    const resultAPI = await firstValueFrom(
+      this.httpService.post<ExperimentData>(path, form),
+    );
+
+    return dataToExperiment(resultAPI.data);
+  }
+
+  async listExperiments(page: number, name: string): Promise<ListExperiments> {
+    const path = this.options.baseurl + 'experiments';
+
+    const resultAPI = await firstValueFrom(
+      this.httpService.get<ExperimentsData>(path, { params: { page, name } }),
+    );
+
+    return {
+      ...resultAPI.data,
+      experiments: resultAPI.data.experiments.map(dataToExperiment),
+    };
+  }
+
+  async getAlgorithms(): Promise<Algorithm[]> {
+    const path = this.options.baseurl + 'algorithms';
+
+    const resultAPI = await firstValueFrom(this.httpService.get<string>(path));
+
+    return dataToAlgorithms(resultAPI.data);
+  }
+
+  async getExperiment(uuid: string): Promise<Experiment> {
+    const path = this.options.baseurl + `experiments/${uuid}`;
+
+    const resultAPI = await firstValueFrom(
+      this.httpService.get<ExperimentData>(path),
+    );
+
+    return dataToExperiment(resultAPI.data);
+  }
+
+  async editExperient(
+    uuid: string,
+    expriment: ExperimentEditInput,
+  ): Promise<Experiment> {
+    const path = this.options.baseurl + `experiments/${uuid}`;
+
+    const resultAPI = await firstValueFrom(
+      this.httpService.patch<ExperimentData>(path, expriment),
+    );
+
+    return dataToExperiment(resultAPI.data);
+  }
+
+  async removeExperiment(uuid: string): Promise<PartialExperiment> {
+    const path = this.options.baseurl + `experiments/${uuid}`;
+
+    try {
+      await firstValueFrom(this.httpService.delete(path));
+      return {
+        uuid: uuid,
+      };
+    } catch (error) {
+      throw new BadRequestException(`${uuid} does not exists`);
     }
-}
\ No newline at end of file
+  }
+
+  async getDomains(ids: string[]): Promise<Domain[]> {
+    const path = this.options.baseurl + 'pathologies';
+
+    try {
+      const data = await firstValueFrom(
+        this.httpService.get<Pathology[]>(path),
+      );
+
+      return (
+        data?.data
+          .filter((data) => !ids || ids.length == 0 || ids.includes(data.code))
+          .map((data): Domain => {
+            const groups = this.flattenGroups(data.metadataHierarchy);
+
+            return {
+              id: data.code,
+              label: data.label,
+              groups: groups,
+              rootGroup: dataToGroup(data.metadataHierarchy),
+              datasets: data.datasets ? data.datasets.map(dataToCategory) : [],
+              variables: data.metadataHierarchy
+                ? this.flattenVariables(data.metadataHierarchy, groups)
+                : [],
+            };
+          }) ?? []
+      );
+    } catch (error) {
+      throw new HttpException(
+        `Error in exareme engine : '${error.response.data['message']}'`,
+        error.response.data['statusCode'] ?? HttpStatus.INTERNAL_SERVER_ERROR,
+      );
+    }
+  }
+
+  getActiveUser(): Observable<string> {
+    const path = this.options.baseurl + 'activeUser';
+
+    return this.httpService
+      .get<string>(path)
+      .pipe(map((response) => response.data));
+  }
+
+  editActiveUser(): Observable<string> {
+    const path = this.options.baseurl + 'activeUser/agreeNDA';
+
+    return this.httpService
+      .post<string>(path, this.req.body)
+      .pipe(map((response) => response.data));
+  }
+
+  getExperimentREST(uuid: string): Observable<string> {
+    const path = this.options.baseurl + `experiments/${uuid}`;
+
+    return this.httpService
+      .get<string>(path)
+      .pipe(map((response) => response.data));
+  }
+
+  deleteExperiment(uuid: string): Observable<string> {
+    const path = this.options.baseurl + `experiments/${uuid}`;
+
+    return this.httpService.delete(path).pipe(map((response) => response.data));
+  }
+
+  editExperimentREST(uuid: string): Observable<string> {
+    const path = this.options.baseurl + `experiments/${uuid}`;
+
+    return this.httpService
+      .patch(path, this.req.body)
+      .pipe(map((response) => response.data));
+  }
+
+  startExperimentTransient(): Observable<string> {
+    const path = this.options.baseurl + 'experiments/transient';
+
+    return this.httpService
+      .post(path, this.req.body)
+      .pipe(map((response) => response.data));
+  }
+
+  startExperiment(): Observable<string> {
+    const path = this.options.baseurl + 'experiments';
+
+    return this.httpService
+      .post(path, this.req.body)
+      .pipe(map((response) => response.data));
+  }
+
+  getExperiments(): Observable<string> {
+    const path = this.options.baseurl + 'experiments';
+
+    return this.httpService
+      .get<string>(path, { params: this.req.query })
+      .pipe(map((response) => response.data));
+  }
+
+  getAlgorithmsREST(): Observable<string> {
+    const path = this.options.baseurl + 'algorithms';
+
+    return this.httpService
+      .get<string>(path, { params: this.req.query })
+      .pipe(map((response) => response.data));
+  }
+
+  // UTILITIES
+  private flattenGroups = (data: Hierarchy): Group[] => {
+    let groups: Group[] = [dataToGroup(data)];
+
+    if (data.groups) {
+      groups = groups.concat(data.groups.flatMap(this.flattenGroups));
+    }
+
+    return groups;
+  };
+
+  private flattenVariables = (data: Hierarchy, groups: Group[]): Variable[] => {
+    const group = groups.find((group) => group.id == data.code);
+    let variables = data.variables ? data.variables.map(dataToVariable) : [];
+
+    variables.forEach((variable) => (variable.groups = group ? [group] : []));
+
+    if (data.groups) {
+      variables = variables.concat(
+        data.groups.flatMap((hierarchy) =>
+          this.flattenVariables(hierarchy, groups),
+        ),
+      );
+    }
+
+    return variables;
+  };
+}
diff --git a/api/src/engine/connectors/exareme/transformations.ts b/api/src/engine/connectors/exareme/transformations.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a57e677bf1e341a527f2c98c105376135c6a4339
--- /dev/null
+++ b/api/src/engine/connectors/exareme/transformations.ts
@@ -0,0 +1,139 @@
+// This file contains all transformation queries for JSONata
+// see : https://docs.jsonata.org/
+
+import * as jsonata from 'jsonata'; // old import style needed due to 'export = jsonata'
+
+export const transformToAlgorithms = jsonata(`
+(
+    $params := ["y", "pathology", "dataset", "filter"];
+
+    $toArray := function($x) { $type($x) = 'array' ? $x : [$x]};
+
+    *.{
+    'name': name,
+    'label': label,
+    'description': desc,
+    'parameters': $toArray(parameters[$not(name in $params)].{
+        'name': name,
+        'description': desc,
+        'label': label,
+        'type': valueType,
+        'defaultValue': defaultValue,
+        'isMultiple': $boolean(valueMultiple),
+        'isRequired': $boolean(valueNotBlank),
+        'min': valueMin,
+        'max': valueMax
+    })
+}
+)
+`);
+
+export const transformToExperiment = jsonata(`
+( 
+    $params := ["y", "pathology", "dataset", "filter"];
+
+    {
+        "name": name,
+        "uuid": uuid,
+        "author": createdBy,
+        "viewed": viewed,
+        "status": status,
+        "createdAt": created,
+        "finishedAt": finished,
+        "shared": shared,
+        "updateAt": updated,
+        "domains": algorithm.parameters[name = "pathology"].value,
+        "variables": $split(algorithm.parameters[name = "y"].value, ','),
+        "filter": algorithm.parameters[name = "filter"].value,
+        "datasets": $split(algorithm.parameters[name = "dataset"].value, ','),
+        "algorithm": {
+            "name": algorithm.name,
+            "parameters" : 
+                algorithm.parameters[$not(name in $params)].({
+                    "name": name,
+                    "label": label,
+                    "value": value
+                })
+        }
+    }
+)
+`);
+
+const headerDescriptivie = `
+$fnum := function($x) { $type($x) = 'number' ? $round($number($x),3) : $x };
+
+$e := function($x, $r) {($x != null) ? $fnum($x) : ($r ? $r : '')};
+
+$fn := function($o, $prefix) {
+    $type($o) = 'object' ? 
+    $each($o, function($v, $k) {(
+        $type($v) = 'object' ? { $k: $v.count & ' (' & $v.percentage & '%)' } : {
+            $k: $v
+        }
+    )}) ~> $merge()
+    : {}
+};`;
+
+export const descriptiveModelToTables = jsonata(`
+(   
+    ${headerDescriptivie}
+    
+    $vars := $count($keys(data.model.*.data))-1;
+    $varNames := $keys(data.model.*.data);
+    $model := data.model;
+
+    [[0..$vars].(
+        $i := $;
+        $varName := $varNames[$i];
+        $ks := $keys($model.*.data.*[$i][$type($) = 'object']);
+        {
+            'name': $varName,
+            'headers': $append("", $keys($$.data.model)).{
+                'name': $,
+                'type': 'string'
+            },
+            'data': [
+                [$varName, $model.*.($e(num_total))],
+                ['Datapoints', $model.*.($e(num_datapoints))],
+                ['Nulls', $model.*.($e(num_nulls))],
+                ($lookup($model.*.data, $varName).($fn($)) ~> $reduce(function($a, $b) {
+                    $map($ks, function($k) {(
+                        {
+                            $k : [$e($lookup($a,$k), "No data"), $e($lookup($b,$k), "No data")]
+                        }
+                    )}) ~> $merge()
+                })).$each(function($v, $k) {$append($k,$v)})[]
+            ]
+        }
+    )]  
+)`);
+
+export const descriptiveSingleToTables = jsonata(`
+( 
+    ${headerDescriptivie}
+
+    data.[
+        $.single.*@$p#$i.(
+            $ks := $keys($p.*.data[$type($) = 'object']);
+            {
+            'name': $keys(%)[$i],
+            'headers': $append("", $keys(*)).{
+                'name': $,
+                'type': 'string'
+            },
+            'data' : [
+                [$keys(%)[$i], $p.*.($e(num_total))],
+                ['Datapoints', $p.*.($e(num_datapoints))],
+                ['Nulls', $p.*.($e(num_nulls))],
+                ($p.*.data.($fn($)) ~> $reduce(function($a, $b) {
+                    $map($ks, function($k) {(
+                        {
+                            $k : [$e($lookup($a,$k), "No data"), $e($lookup($b,$k), "No data")]
+                        }
+                    )}) ~> $merge()
+                })).$each(function($v, $k) {$append($k,$v)})[]
+            ]
+        })
+    ]
+)
+`);
diff --git a/api/src/engine/connectors/local/main.connector.ts b/api/src/engine/connectors/local/main.connector.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cb472d927f571c9553c0115624a9981f2be653c6
--- /dev/null
+++ b/api/src/engine/connectors/local/main.connector.ts
@@ -0,0 +1,114 @@
+import { Observable } from 'rxjs';
+import { IEngineService } from 'src/engine/engine.interfaces';
+import { Domain } from 'src/engine/models/domain.model';
+import { ExperimentCreateInput } from 'src/engine/models/experiment/input/experiment-create.input';
+import {
+  Experiment,
+  PartialExperiment,
+} from 'src/engine/models/experiment/experiment.model';
+import { ListExperiments } from 'src/engine/models/experiment/list-experiments.model';
+import { ExperimentEditInput } from 'src/engine/models/experiment/input/experiment-edit.input';
+import { Algorithm } from 'src/engine/models/experiment/algorithm.model';
+
+export default class LocalService implements IEngineService {
+  logout(): void {
+    throw new Error('Method not implemented.');
+  }
+
+  getAlgorithms(): Algorithm[] | Promise<Algorithm[]> {
+    throw new Error('Method not implemented.');
+  }
+
+  createExperiment(
+    data: ExperimentCreateInput,
+    isTransient: boolean,
+  ): Experiment | Promise<Experiment> {
+    throw new Error('Method not implemented.');
+  }
+
+  listExperiments(
+    page: number,
+    name: string,
+  ): ListExperiments | Promise<ListExperiments> {
+    throw new Error('Method not implemented.');
+  }
+
+  getExperiment(uuid: string): Experiment | Promise<Experiment> {
+    throw new Error('Method not implemented.');
+  }
+
+  removeExperiment(
+    uuid: string,
+  ): PartialExperiment | Promise<PartialExperiment> {
+    throw new Error('Method not implemented.');
+  }
+
+  editExperient(
+    uuid: string,
+    expriment: ExperimentEditInput,
+  ): Experiment | Promise<Experiment> {
+    throw new Error('Method not implemented.');
+  }
+
+  getDomains(): Domain[] {
+    return [
+      {
+        id: 'Dummy',
+        label: 'Dummy',
+        datasets: [{ id: 'DummyDataset', label: 'DummyDataset' }],
+        groups: [
+          {
+            id: 'DummyGroup',
+            variables: ['DummyVar'],
+            groups: [],
+          },
+        ],
+        rootGroup: { id: 'DummyGroup' },
+        variables: [{ id: 'DummyVar', type: 'string' }],
+      },
+    ];
+  }
+
+  getActiveUser(): string {
+    const dummyUser = {
+      username: 'anonymous',
+      subjectId: 'anonymousId',
+      fullname: 'anonymous',
+      email: 'anonymous@anonymous.com',
+      agreeNDA: true,
+    };
+    return JSON.stringify(dummyUser);
+  }
+
+  editActiveUser(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  getExperimentREST(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  deleteExperiment(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  editExperimentREST(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  startExperimentTransient(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  startExperiment(): Observable<string> {
+    throw new Error('Method not implemented.');
+  }
+
+  getExperiments(): string {
+    return '[]';
+  }
+
+  getAlgorithmsREST(): string {
+    return '[]';
+  }
+}
diff --git a/api/src/engine/engine.constants.ts b/api/src/engine/engine.constants.ts
index 8cd04e3e70f4401d1b73841cf79c4e57ba138dd4..e8fe57799577d257d79d8eea188da5f1bed75417 100644
--- a/api/src/engine/engine.constants.ts
+++ b/api/src/engine/engine.constants.ts
@@ -1,2 +1,2 @@
-export const ENGINE_MODULE_OPTIONS = "EngineModuleOption";
-export const ENGINE_SERVICE = "EngineService"
\ No newline at end of file
+export const ENGINE_MODULE_OPTIONS = 'EngineModuleOption';
+export const ENGINE_SERVICE = 'EngineService';
diff --git a/api/src/engine/engine.controller.ts b/api/src/engine/engine.controller.ts
index 503edea819501aa28b12574822a5182ae0b86718..d7f34d493e03ae0f08401baf9aab1b888851b9f9 100644
--- a/api/src/engine/engine.controller.ts
+++ b/api/src/engine/engine.controller.ts
@@ -1,14 +1,72 @@
-import { HttpService } from '@nestjs/axios';
-import { Controller, Get, Inject } from '@nestjs/common';
+import {
+  Controller,
+  Delete,
+  Get,
+  Inject,
+  Param,
+  Patch,
+  Post,
+  UseInterceptors,
+} from '@nestjs/common';
+import { Observable } from 'rxjs';
 import { ENGINE_SERVICE } from './engine.constants';
-import { IEngineService } from './engine.interface';
+import { IEngineService } from './engine.interfaces';
+import { HeadersInterceptor } from './interceptors/headers.interceptor';
 
+@UseInterceptors(HeadersInterceptor)
 @Controller()
 export class EngineController {
-  constructor(@Inject(ENGINE_SERVICE) private readonly engineService: IEngineService, private readonly httpService: HttpService) { }
+  constructor(
+    @Inject(ENGINE_SERVICE) private readonly engineService: IEngineService,
+  ) {}
 
-  @Get("/test")
-  getTest(): string {
-    return this.engineService.demo();
+  @Get('/algorithms')
+  getAlgorithms(): Observable<string> | string {
+    return this.engineService.getAlgorithmsREST();
+  }
+
+  @Get('/experiments')
+  getExperiments(): Observable<string> | string {
+    return this.engineService.getExperiments();
+  }
+
+  @Get('/experiments/:uuid')
+  getExperiment(@Param('uuid') uuid: string): Observable<string> | string {
+    return this.engineService.getExperimentREST(uuid);
+  }
+
+  @Delete('/experiments/:uuid')
+  deleteExperiment(@Param('uuid') uuid: string): Observable<string> | string {
+    return this.engineService.deleteExperiment(uuid);
+  }
+
+  @Patch('/experiments/:uuid')
+  editExperiment(@Param('uuid') uuid: string): Observable<string> | string {
+    return this.engineService.editExperimentREST(uuid);
+  }
+
+  @Post('experiments/transient')
+  startExperimentTransient(): Observable<string> | string {
+    return this.engineService.startExperimentTransient();
+  }
+
+  @Post('experiments')
+  startExperiment(): Observable<string> | string {
+    return this.engineService.startExperiment();
+  }
+
+  @Get('activeUser')
+  getActiveUser(): Observable<string> | string {
+    return this.engineService.getActiveUser();
+  }
+
+  @Post('activeUser/agreeNDA')
+  agreeNDA(): Observable<string> | string {
+    return this.engineService.editActiveUser();
+  }
+
+  @Get('logout')
+  logout(): void {
+    this.engineService.logout();
   }
 }
diff --git a/api/src/engine/engine.interface.ts b/api/src/engine/engine.interface.ts
deleted file mode 100644
index 073b05a3f933ea9d4b9ab1d26ea840b7f31e5018..0000000000000000000000000000000000000000
--- a/api/src/engine/engine.interface.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { AxiosResponse } from "axios";
-import { Observable } from "rxjs";
-
-export interface IEngineOptions {
-    type: string;
-}
-
-export interface IEngineService {
-    demo(): string;
-}
\ No newline at end of file
diff --git a/api/src/engine/engine.interfaces.ts b/api/src/engine/engine.interfaces.ts
new file mode 100644
index 0000000000000000000000000000000000000000..29e57f161a8dc2a69c747d415fa529b4c986002a
--- /dev/null
+++ b/api/src/engine/engine.interfaces.ts
@@ -0,0 +1,64 @@
+import { Observable } from 'rxjs';
+import { Domain } from './models/domain.model';
+import { Algorithm } from './models/experiment/algorithm.model';
+import {
+  Experiment,
+  PartialExperiment,
+} from './models/experiment/experiment.model';
+import { ExperimentCreateInput } from './models/experiment/input/experiment-create.input';
+import { ExperimentEditInput } from './models/experiment/input/experiment-edit.input';
+import { ListExperiments } from './models/experiment/list-experiments.model';
+
+export interface IEngineOptions {
+  type: string;
+  baseurl: string;
+}
+
+export interface IEngineService {
+  //GraphQL
+  getDomains(ids: string[]): Domain[] | Promise<Domain[]>;
+
+  createExperiment(
+    data: ExperimentCreateInput,
+    isTransient: boolean,
+  ): Promise<Experiment> | Experiment;
+
+  listExperiments(
+    page: number,
+    name: string,
+  ): Promise<ListExperiments> | ListExperiments;
+
+  getExperiment(uuid: string): Promise<Experiment> | Experiment;
+
+  removeExperiment(
+    uuid: string,
+  ): Promise<PartialExperiment> | PartialExperiment;
+
+  editExperient(
+    uuid: string,
+    expriment: ExperimentEditInput,
+  ): Promise<Experiment> | Experiment;
+
+  getAlgorithms(): Promise<Algorithm[]> | Algorithm[];
+
+  // Standard REST API call
+  getAlgorithmsREST(): Observable<string> | string;
+
+  getExperiments(): Observable<string> | string;
+
+  getExperimentREST(uuid: string): Observable<string> | string;
+
+  deleteExperiment(uuid: string): Observable<string> | string;
+
+  editExperimentREST(uuid: string): Observable<string> | string;
+
+  startExperimentTransient(): Observable<string> | string;
+
+  startExperiment(): Observable<string> | string;
+
+  getActiveUser(): Observable<string> | string;
+
+  editActiveUser(): Observable<string> | string;
+
+  logout(): void;
+}
diff --git a/api/src/engine/engine.module.ts b/api/src/engine/engine.module.ts
index 2490e51b9327b2241cbd246206173a953d7a91a8..48442588a8d1a2c5120e99fa15d60e7da3c29739 100644
--- a/api/src/engine/engine.module.ts
+++ b/api/src/engine/engine.module.ts
@@ -1,15 +1,18 @@
 import { HttpModule, HttpService } from '@nestjs/axios';
-import { DynamicModule, Global, Module } from '@nestjs/common';
+import { DynamicModule, Global, Logger, Module } from '@nestjs/common';
+import { REQUEST } from '@nestjs/core';
 import { GraphQLModule } from '@nestjs/graphql';
 import { join } from 'path';
 import { ENGINE_MODULE_OPTIONS, ENGINE_SERVICE } from './engine.constants';
 import { EngineController } from './engine.controller';
-import { IEngineOptions, IEngineService } from './engine.interface';
+import { IEngineOptions, IEngineService } from './engine.interfaces';
 import { EngineResolver } from './engine.resolver';
 
 @Global()
 @Module({})
 export class EngineModule {
+  private static readonly logger = new Logger(EngineModule.name);
+
   static async forRootAsync(options: IEngineOptions): Promise<DynamicModule> {
     const optionsProvider = {
       provide: ENGINE_MODULE_OPTIONS,
@@ -18,10 +21,10 @@ export class EngineModule {
 
     const engineProvider = {
       provide: ENGINE_SERVICE,
-      useFactory: async (httpService: HttpService) => {
-        return await this.createEngineConnection(options, httpService)
+      useFactory: async (httpService: HttpService, req: Request) => {
+        return await this.createEngineConnection(options, httpService, req);
       },
-      inject: [HttpService]
+      inject: [HttpService, REQUEST],
     };
 
     return {
@@ -32,22 +35,29 @@ export class EngineModule {
           autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
         }),
       ],
-      providers: [
-        optionsProvider,
-        engineProvider,
-        EngineResolver
-      ],
-      controllers: [
-        EngineController
-      ],
+      providers: [optionsProvider, engineProvider, EngineResolver],
+      controllers: [EngineController],
       exports: [optionsProvider, engineProvider],
-    }
+    };
   }
 
-  private static async createEngineConnection(options: IEngineOptions, httpService: HttpService): Promise<IEngineService> {
-    let service = await import(`./connectors/${options.type}/main.connector`);
+  private static async createEngineConnection(
+    options: IEngineOptions,
+    httpService: HttpService,
+    req: Request,
+  ): Promise<IEngineService> {
+    try {
+      const service = await import(
+        `./connectors/${options.type}/main.connector`
+      );
+      const engine = new service.default(options, httpService, req);
 
-    return new service.default(options, httpService);
+      return engine;
+    } catch {
+      this.logger.error(
+        `There is a problem with the connector '${options.type}'`,
+      );
+      process.exit(); // We can't continue without an engine, shutdown the process...
+    }
   }
-
 }
diff --git a/api/src/engine/engine.resolver.ts b/api/src/engine/engine.resolver.ts
index 94f911825d5548eabea84382f7f5727e77e68092..6e23ba38ca26d1129e51abb7cbba8d311cad67b9 100644
--- a/api/src/engine/engine.resolver.ts
+++ b/api/src/engine/engine.resolver.ts
@@ -1,14 +1,75 @@
-import { Inject } from '@nestjs/common';
-import { Query, Resolver } from '@nestjs/graphql';
+import { Inject, UseInterceptors } from '@nestjs/common';
+import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
 import { ENGINE_SERVICE } from './engine.constants';
-import { IEngineService } from './engine.interface';
+import { IEngineService } from './engine.interfaces';
+import { HeadersInterceptor } from './interceptors/headers.interceptor';
+import { Domain } from './models/domain.model';
+import { Algorithm } from './models/experiment/algorithm.model';
+import {
+  Experiment,
+  PartialExperiment,
+} from './models/experiment/experiment.model';
+import { ExperimentCreateInput } from './models/experiment/input/experiment-create.input';
+import { ExperimentEditInput } from './models/experiment/input/experiment-edit.input';
+import { ListExperiments } from './models/experiment/list-experiments.model';
 
+@UseInterceptors(HeadersInterceptor)
 @Resolver()
 export class EngineResolver {
-    constructor(@Inject(ENGINE_SERVICE) private readonly engineService: IEngineService) { }
+  constructor(
+    @Inject(ENGINE_SERVICE) private readonly engineService: IEngineService,
+  ) {}
 
-    @Query(() => String)
-    async hello() {
-        return this.engineService.demo();
-    }
-}
\ No newline at end of file
+  @Query(() => [Domain])
+  async domains(
+    @Args('ids', { nullable: true, type: () => [String], defaultValue: [] })
+    ids: string[],
+  ) {
+    return this.engineService.getDomains(ids);
+  }
+
+  @Query(() => ListExperiments)
+  async experiments(
+    @Args('page', { nullable: true, defaultValue: 0 }) page: number,
+    @Args('name', { nullable: true, defaultValue: '' }) name: string,
+  ) {
+    return this.engineService.listExperiments(page, name);
+  }
+
+  @Query(() => Experiment)
+  async expriment(@Args('uuid') uuid: string) {
+    return this.engineService.getExperiment(uuid);
+  }
+
+  @Query(() => [Algorithm])
+  async algorithms() {
+    return this.engineService.getAlgorithms();
+  }
+
+  @Mutation(() => Experiment)
+  async createExperiment(
+    @Args('data') experimentCreateInput: ExperimentCreateInput,
+    @Args('isTransient', { nullable: true, defaultValue: false })
+    isTransient: boolean,
+  ) {
+    return this.engineService.createExperiment(
+      experimentCreateInput,
+      isTransient,
+    );
+  }
+
+  @Mutation(() => Experiment)
+  async editExperiment(
+    @Args('uuid') uuid: string,
+    @Args('data') experiment: ExperimentEditInput,
+  ) {
+    return this.engineService.editExperient(uuid, experiment);
+  }
+
+  @Mutation(() => PartialExperiment)
+  async removeExperiment(
+    @Args('uuid') uuid: string,
+  ): Promise<PartialExperiment> {
+    return this.engineService.removeExperiment(uuid);
+  }
+}
diff --git a/api/src/engine/interceptors/headers.interceptor.ts b/api/src/engine/interceptors/headers.interceptor.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9b28e4953456de5d9bc66a390c9cf452cf45306f
--- /dev/null
+++ b/api/src/engine/interceptors/headers.interceptor.ts
@@ -0,0 +1,45 @@
+import { HttpService } from '@nestjs/axios';
+import { Injectable, NestInterceptor, CallHandler } from '@nestjs/common';
+import { GqlExecutionContext } from '@nestjs/graphql';
+import { IncomingMessage } from 'http';
+import { Observable, tap } from 'rxjs';
+
+@Injectable()
+export class HeadersInterceptor implements NestInterceptor {
+  constructor(private httpService: HttpService) {}
+
+  intercept(context: GqlExecutionContext, next: CallHandler): Observable<any> {
+    // cleaner : add only the auth header (should find the name)
+
+    const keys = ['cookie', 'x-xsrf-token'];
+    let headers = {};
+
+    switch (context.getType()) {
+      case 'http': {
+        const ctx = context.switchToHttp();
+        const request = ctx.getRequest<Request>();
+        headers = request.headers;
+        break;
+      }
+      case 'graphql': {
+        const ctx = GqlExecutionContext.create(context);
+        const req: IncomingMessage = ctx.getContext().req;
+        headers = req.headers;
+        break;
+      }
+    }
+
+    Object.keys(headers)
+      .filter((key) => keys.includes(key))
+      .map((key) => key.toLowerCase())
+      .forEach((key) => {
+        this.httpService.axiosRef.defaults.headers.common[key] = headers[key];
+      });
+
+    return next.handle().pipe(
+      tap(() => {
+        this.httpService.axiosRef.defaults.headers.common = {}; //cleaning request
+      }),
+    );
+  }
+}
diff --git a/api/src/engine/models/category.model.ts b/api/src/engine/models/category.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..921ebfce8d518a66a484bf44d04cf330b8091915
--- /dev/null
+++ b/api/src/engine/models/category.model.ts
@@ -0,0 +1,5 @@
+import { ObjectType } from '@nestjs/graphql';
+import { Entity } from './entity.model';
+
+@ObjectType()
+export class Category extends Entity {}
diff --git a/api/src/engine/models/domain.model.ts b/api/src/engine/models/domain.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f5b8def42a318613b348040b2eccb3ea67a7aa29
--- /dev/null
+++ b/api/src/engine/models/domain.model.ts
@@ -0,0 +1,23 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+import { Category } from './category.model';
+import { Entity } from './entity.model';
+import { Group } from './group.model';
+import { Variable } from './variable.model';
+
+@ObjectType()
+export class Domain extends Entity {
+  @Field({ nullable: true })
+  description?: string;
+
+  @Field(() => [Group])
+  groups: Group[];
+
+  @Field(() => [Variable])
+  variables: Variable[];
+
+  @Field(() => [Category])
+  datasets: Category[];
+
+  @Field(() => Group)
+  rootGroup: Group;
+}
diff --git a/api/src/engine/models/entity.model.ts b/api/src/engine/models/entity.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..32183fd7e970410a1ee3f479cef4d691d56fb7a7
--- /dev/null
+++ b/api/src/engine/models/entity.model.ts
@@ -0,0 +1,11 @@
+import { Field, InputType, ObjectType } from '@nestjs/graphql';
+
+@InputType()
+@ObjectType()
+export class Entity {
+  @Field()
+  id: string;
+
+  @Field({ nullable: true })
+  label?: string;
+}
diff --git a/api/src/engine/models/experiment/algorithm-parameter.model.ts b/api/src/engine/models/experiment/algorithm-parameter.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d5db6bfc11634ae3ec054c4c6eef9a5441f74931
--- /dev/null
+++ b/api/src/engine/models/experiment/algorithm-parameter.model.ts
@@ -0,0 +1,34 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+
+@ObjectType()
+export class AlgorithmParameter {
+  @Field()
+  name: string;
+
+  @Field(() => [String], { nullable: true })
+  value?: string[];
+
+  @Field({ nullable: true })
+  label?: string;
+
+  @Field({ nullable: true })
+  description?: string;
+
+  @Field({ nullable: true })
+  defaultValue?: string;
+
+  @Field({ defaultValue: false, nullable: true })
+  isMultiple?: boolean;
+
+  @Field({ defaultValue: false, nullable: true })
+  isRequired?: boolean;
+
+  @Field({ nullable: true })
+  min?: string;
+
+  @Field({ nullable: true })
+  max?: string;
+
+  @Field({ nullable: true })
+  type?: string;
+}
diff --git a/api/src/engine/models/experiment/algorithm.model.ts b/api/src/engine/models/experiment/algorithm.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ec26d747fc8019a29822bf673aa82b29162f5757
--- /dev/null
+++ b/api/src/engine/models/experiment/algorithm.model.ts
@@ -0,0 +1,20 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+import { AlgorithmParameter } from './algorithm-parameter.model';
+
+@ObjectType()
+export class Algorithm {
+  @Field()
+  name: string;
+
+  @Field(() => [AlgorithmParameter], { nullable: true, defaultValue: [] })
+  parameters?: AlgorithmParameter[];
+
+  @Field({ nullable: true })
+  label?: string;
+
+  @Field({ nullable: true })
+  type?: string;
+
+  @Field({ nullable: true })
+  description?: string;
+}
diff --git a/api/src/engine/models/experiment/experiment.model.ts b/api/src/engine/models/experiment/experiment.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..06e28b4b2e1c93e8dac7c6e7dc828c706f370c8a
--- /dev/null
+++ b/api/src/engine/models/experiment/experiment.model.ts
@@ -0,0 +1,54 @@
+import { Field, ObjectType, PartialType } from '@nestjs/graphql';
+import { ResultUnion } from '../result/common/result-union.model';
+import { Algorithm } from './algorithm.model';
+
+@ObjectType()
+export class Experiment {
+  @Field({ nullable: true })
+  uuid?: string;
+
+  @Field({ nullable: true, defaultValue: '' })
+  author?: string;
+
+  @Field({ nullable: true })
+  createdAt?: number;
+
+  @Field({ nullable: true })
+  updateAt?: number;
+
+  @Field({ nullable: true })
+  finishedAt?: number;
+
+  @Field({ nullable: true, defaultValue: false })
+  viewed?: boolean;
+
+  @Field({ nullable: true })
+  status?: string;
+
+  @Field({ defaultValue: false })
+  shared?: boolean;
+
+  @Field(() => [ResultUnion], { nullable: true, defaultValue: [] })
+  results?: Array<typeof ResultUnion>;
+
+  @Field(() => [String])
+  datasets: string[];
+
+  @Field(() => String, { nullable: true })
+  filter?: string;
+
+  @Field()
+  domain: string;
+
+  @Field(() => [String])
+  variables: string[];
+
+  @Field()
+  algorithm: Algorithm;
+
+  @Field()
+  name: string;
+}
+
+@ObjectType()
+export class PartialExperiment extends PartialType(Experiment) {}
diff --git a/api/src/engine/models/experiment/input/algorithm-parameter.input.ts b/api/src/engine/models/experiment/input/algorithm-parameter.input.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8eb4cb26b5dade932440b6fef169275988d33b03
--- /dev/null
+++ b/api/src/engine/models/experiment/input/algorithm-parameter.input.ts
@@ -0,0 +1,10 @@
+import { Field, InputType } from '@nestjs/graphql';
+
+@InputType()
+export class AlgorithmParamInput {
+  @Field()
+  name: string;
+
+  @Field(() => [String])
+  value: string[];
+}
diff --git a/api/src/engine/models/experiment/input/algorithm.input.ts b/api/src/engine/models/experiment/input/algorithm.input.ts
new file mode 100644
index 0000000000000000000000000000000000000000..54d26b7686559c39b311cee5e5496bce530b0cf5
--- /dev/null
+++ b/api/src/engine/models/experiment/input/algorithm.input.ts
@@ -0,0 +1,14 @@
+import { Field, InputType } from '@nestjs/graphql';
+import { AlgorithmParamInput } from './algorithm-parameter.input';
+
+@InputType()
+export class AlgorithmInput {
+  @Field()
+  name: string;
+
+  @Field(() => [AlgorithmParamInput], { nullable: true, defaultValue: [] })
+  parameters: AlgorithmParamInput[];
+
+  @Field()
+  type: string;
+}
diff --git a/api/src/engine/models/experiment/input/experiment-create.input.ts b/api/src/engine/models/experiment/input/experiment-create.input.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b0b57cea85b17b0b49d4cbdf7b6cc12f21b71688
--- /dev/null
+++ b/api/src/engine/models/experiment/input/experiment-create.input.ts
@@ -0,0 +1,38 @@
+import { Field, InputType } from '@nestjs/graphql';
+import { AlgorithmInput } from './algorithm.input';
+
+@InputType()
+export class FormulaTransformation {
+  @Field()
+  name: string;
+
+  @Field()
+  operation: string;
+}
+
+@InputType()
+export class ExperimentCreateInput {
+  @Field(() => [String])
+  datasets: string[];
+
+  @Field(() => String, { nullable: true })
+  filter: string;
+
+  @Field()
+  domain: string;
+
+  @Field(() => [String])
+  variables: string[];
+
+  @Field()
+  algorithm: AlgorithmInput;
+
+  @Field()
+  name: string;
+
+  @Field(() => [FormulaTransformation], { nullable: true })
+  transformations: FormulaTransformation[];
+
+  @Field(() => [[String]], { nullable: true })
+  interactions: string[][];
+}
diff --git a/api/src/engine/models/experiment/input/experiment-edit.input.ts b/api/src/engine/models/experiment/input/experiment-edit.input.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d37deb1e8d008d746fda7214bb6890142b74a12a
--- /dev/null
+++ b/api/src/engine/models/experiment/input/experiment-edit.input.ts
@@ -0,0 +1,10 @@
+import { Field, InputType } from '@nestjs/graphql';
+
+@InputType()
+export class ExperimentEditInput {
+  @Field({ nullable: true })
+  name?: string;
+
+  @Field({ nullable: true })
+  viewed?: boolean;
+}
diff --git a/api/src/engine/models/experiment/list-experiments.model.ts b/api/src/engine/models/experiment/list-experiments.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8f8592e52f1e3d85946d373a73b4edd00ac318f5
--- /dev/null
+++ b/api/src/engine/models/experiment/list-experiments.model.ts
@@ -0,0 +1,17 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+import { Experiment } from './experiment.model';
+
+@ObjectType()
+export class ListExperiments {
+  @Field({ nullable: true, defaultValue: 0 })
+  currentPage?: number;
+
+  @Field({ nullable: true })
+  totalPages?: number;
+
+  @Field({ nullable: true })
+  totalExperiments?: number;
+
+  @Field(() => [Experiment])
+  experiments: Experiment[];
+}
diff --git a/api/src/engine/models/group.model.ts b/api/src/engine/models/group.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6a23f1983398f81eeca84731c3d8cfe4b92b599a
--- /dev/null
+++ b/api/src/engine/models/group.model.ts
@@ -0,0 +1,18 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+import { Entity } from './entity.model';
+
+@ObjectType()
+export class Group extends Entity {
+  @Field({ nullable: true })
+  description?: string;
+
+  @Field(() => [String], { defaultValue: [], nullable: true })
+  groups?: string[];
+
+  @Field(() => [String], {
+    description: "List of variable's ids",
+    defaultValue: [],
+    nullable: true,
+  })
+  variables?: string[];
+}
diff --git a/api/src/engine/models/result/common/chart-axis.model.ts b/api/src/engine/models/result/common/chart-axis.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..16fc1f68a10e379b0bf4e45225c7912710d1d34d
--- /dev/null
+++ b/api/src/engine/models/result/common/chart-axis.model.ts
@@ -0,0 +1,10 @@
+import { ObjectType, Field } from '@nestjs/graphql';
+
+@ObjectType()
+export class ChartAxis {
+  @Field({ nullable: true, defaultValue: '' })
+  label?: string;
+
+  @Field(() => [String], { nullable: true, defaultValue: [] })
+  categories?: string[];
+}
diff --git a/api/src/engine/models/result/common/header.model.ts b/api/src/engine/models/result/common/header.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..27844983c4724df06cd8d8d79caf83ae4e00a6de
--- /dev/null
+++ b/api/src/engine/models/result/common/header.model.ts
@@ -0,0 +1,10 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+
+@ObjectType()
+export class Header {
+  @Field()
+  name: string;
+
+  @Field()
+  type: string;
+}
diff --git a/api/src/engine/models/result/common/result-union.model.ts b/api/src/engine/models/result/common/result-union.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2f56bb4d1f6ef577e9e836972f45dd59ede7fc28
--- /dev/null
+++ b/api/src/engine/models/result/common/result-union.model.ts
@@ -0,0 +1,40 @@
+import { createUnionType } from '@nestjs/graphql';
+import { GroupsResult } from '../groups-result.model';
+import { HeatMapResult } from '../heat-map-result.model';
+import { LineChartResult } from '../line-chart-result.model';
+import { RawResult } from '../raw-result.model';
+import { TableResult } from '../table-result.model';
+
+export const ResultUnion = createUnionType({
+  name: 'ResultUnion',
+  types: () => [
+    TableResult,
+    RawResult,
+    GroupsResult,
+    HeatMapResult,
+    LineChartResult,
+  ],
+  resolveType(value) {
+    if (value.headers) {
+      return TableResult;
+    }
+
+    if (value.rawdata) {
+      return RawResult;
+    }
+
+    if (value.groups) {
+      return GroupsResult;
+    }
+
+    if (value.matrix) {
+      return HeatMapResult;
+    }
+
+    if (value.x) {
+      return LineChartResult;
+    }
+
+    return null;
+  },
+});
diff --git a/api/src/engine/models/result/common/result.model.ts b/api/src/engine/models/result/common/result.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e5e059738c16bb8297558add9aa51e7bce9791e2
--- /dev/null
+++ b/api/src/engine/models/result/common/result.model.ts
@@ -0,0 +1,4 @@
+import { ObjectType } from '@nestjs/graphql';
+
+@ObjectType()
+export abstract class Result {}
diff --git a/api/src/engine/models/result/groups-result.model.ts b/api/src/engine/models/result/groups-result.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5ea51cff97c998309df74518f1efee0efcb0c3bb
--- /dev/null
+++ b/api/src/engine/models/result/groups-result.model.ts
@@ -0,0 +1,25 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+import { ResultUnion } from './common/result-union.model';
+import { Result } from './common/result.model';
+
+@ObjectType()
+export class GroupResult {
+  public constructor(init?: Partial<GroupResult>) {
+    Object.assign(this, init);
+  }
+
+  @Field()
+  name: string;
+
+  @Field({ nullable: true })
+  description?: string;
+
+  @Field(() => [ResultUnion])
+  results: Array<typeof ResultUnion>;
+}
+
+@ObjectType()
+export class GroupsResult extends Result {
+  @Field(() => [GroupResult])
+  groups: GroupResult[];
+}
diff --git a/api/src/engine/models/result/heat-map-result.model.ts b/api/src/engine/models/result/heat-map-result.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..267dc3c20201236e5d11ee1cbcbb6d6d89af15a7
--- /dev/null
+++ b/api/src/engine/models/result/heat-map-result.model.ts
@@ -0,0 +1,18 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+import { ChartAxis } from './common/chart-axis.model';
+import { Result } from './common/result.model';
+
+@ObjectType()
+export class HeatMapResult extends Result {
+  @Field()
+  name: string;
+
+  @Field(() => [[Number]])
+  matrix: number[][];
+
+  @Field(() => ChartAxis)
+  xAxis: ChartAxis;
+
+  @Field(() => ChartAxis)
+  yAxis: ChartAxis;
+}
diff --git a/api/src/engine/models/result/line-chart-result.model.ts b/api/src/engine/models/result/line-chart-result.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..85ea4ff66e9780a1cb01de9ea68621b6486e9de5
--- /dev/null
+++ b/api/src/engine/models/result/line-chart-result.model.ts
@@ -0,0 +1,54 @@
+import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
+import { ChartAxis } from './common/chart-axis.model';
+import { Result } from './common/result.model';
+
+export enum LineType {
+  NORMAL,
+  DASHED,
+}
+
+registerEnumType(LineType, {
+  name: 'LineType',
+});
+
+@ObjectType()
+export class ExtraLineInfo {
+  @Field()
+  label: string;
+
+  @Field(() => [String])
+  values: string[];
+}
+
+@ObjectType()
+export class LineResult {
+  @Field()
+  label: string;
+
+  @Field(() => [Number])
+  x: number[];
+
+  @Field(() => [Number])
+  y: number[];
+
+  @Field(() => [ExtraLineInfo], { nullable: true, defaultValue: [] })
+  extraLineInfos?: ExtraLineInfo[];
+
+  @Field(() => LineType, { nullable: true, defaultValue: LineType.NORMAL })
+  type?: LineType;
+}
+
+@ObjectType()
+export class LineChartResult extends Result {
+  @Field()
+  name: string;
+
+  @Field(() => ChartAxis, { nullable: true })
+  xAxis?: ChartAxis;
+
+  @Field(() => ChartAxis, { nullable: true })
+  yAxis?: ChartAxis;
+
+  @Field(() => [LineResult])
+  lines: LineResult[];
+}
diff --git a/api/src/engine/models/result/raw-result.model.ts b/api/src/engine/models/result/raw-result.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..853362293bbfe6da230e487f8ea139ba36a16f3b
--- /dev/null
+++ b/api/src/engine/models/result/raw-result.model.ts
@@ -0,0 +1,12 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+import GraphQLJSON from 'graphql-type-json';
+import { Result } from './common/result.model';
+
+// field name 'rawdata' was used instead of 'data' because of typing problem on union with same field name
+// see :https://stackoverflow.com/questions/44170603/graphql-using-same-field-names-in-different-types-within-union
+
+@ObjectType()
+export class RawResult extends Result {
+  @Field(() => GraphQLJSON)
+  rawdata: unknown;
+}
diff --git a/api/src/engine/models/result/table-result.model.ts b/api/src/engine/models/result/table-result.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..002d59c5b15ce974e56bc9a9b0d9272212961c31
--- /dev/null
+++ b/api/src/engine/models/result/table-result.model.ts
@@ -0,0 +1,15 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+import { Header } from './common/header.model';
+import { Result } from './common/result.model';
+
+@ObjectType()
+export class TableResult extends Result {
+  @Field()
+  name: string;
+
+  @Field(() => [[String]])
+  data: string[][];
+
+  @Field(() => [Header])
+  headers: Header[];
+}
diff --git a/api/src/engine/models/variable.model.ts b/api/src/engine/models/variable.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bd54afed70a025ce6c5673bf0627a657d77926cb
--- /dev/null
+++ b/api/src/engine/models/variable.model.ts
@@ -0,0 +1,19 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+import { Category } from './category.model';
+import { Entity } from './entity.model';
+import { Group } from './group.model';
+
+@ObjectType()
+export class Variable extends Entity {
+  @Field({ nullable: true })
+  type?: string;
+
+  @Field({ nullable: true })
+  description?: string;
+
+  @Field(() => [Category], { nullable: true, defaultValue: [] })
+  enumerations?: Category[];
+
+  @Field(() => [Group], { nullable: true, defaultValue: [] })
+  groups?: Group[];
+}
diff --git a/api/src/main.ts b/api/src/main.ts
index af521d9150d9eecb68faf0d83fa7fd789b91ce98..d6f85b9d78faf28723a2b38bda2452c0866a8af4 100644
--- a/api/src/main.ts
+++ b/api/src/main.ts
@@ -2,7 +2,7 @@ import { NestFactory } from '@nestjs/core';
 import { AppModule } from './main/app.module';
 
 async function bootstrap() {
-  const app = await NestFactory.create(AppModule);
-  await app.listen(3000);
+  const app = await NestFactory.create(AppModule, { cors: true });
+  await app.listen(process.env.GATEWAY_PORT);
 }
 bootstrap();
diff --git a/api/src/main/app.controller.ts b/api/src/main/app.controller.ts
index 60647307bfcb11d99e62b992889d745b34233fe2..cce879ee622146012901c9adb47ef40c0fd3a555 100644
--- a/api/src/main/app.controller.ts
+++ b/api/src/main/app.controller.ts
@@ -1,10 +1,9 @@
-import { HttpService } from '@nestjs/axios';
 import { Controller, Get } from '@nestjs/common';
 import { AppService } from './app.service';
 
 @Controller()
 export class AppController {
-  constructor(private readonly appService: AppService) { }
+  constructor(private readonly appService: AppService) {}
 
   @Get()
   getHello(): string {
diff --git a/api/src/main/app.module.ts b/api/src/main/app.module.ts
index 302746526a7ea75609e579f01306b0e944413aa5..7638ce2f6f28ce28f57f57974833ba8207669bba 100644
--- a/api/src/main/app.module.ts
+++ b/api/src/main/app.module.ts
@@ -1,14 +1,21 @@
 import { Module } from '@nestjs/common';
+import { ConfigModule } from '@nestjs/config';
 import { EngineModule } from 'src/engine/engine.module';
 import { AppController } from './app.controller';
 import { AppService } from './app.service';
 
 @Module({
   imports: [
+    ConfigModule.forRoot({
+      isGlobal: true,
+      envFilePath: ['.env', '.env.defaults'],
+    }),
     EngineModule.forRootAsync({
-      type: process.env.ENGINE_TYPE || "exareme"
-    })],
+      type: process.env.ENGINE_TYPE,
+      baseurl: process.env.ENGINE_BASE_URL,
+    }),
+  ],
   controllers: [AppController],
   providers: [AppService],
 })
-export class AppModule { }
+export class AppModule {}
diff --git a/api/src/schema.gql b/api/src/schema.gql
index 12b643d401b9084f455a4b8f12a9d2ecd4e013d8..1d30677261172d7d7fdc830d938d329d9b5f2d5d 100644
--- a/api/src/schema.gql
+++ b/api/src/schema.gql
@@ -2,6 +2,214 @@
 # THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
 # ------------------------------------------------------
 
+type Category {
+  id: String!
+  label: String
+}
+
+type Group {
+  id: String!
+  label: String
+  description: String
+  groups: [String!]
+
+  """List of variable's ids"""
+  variables: [String!]
+}
+
+type Variable {
+  id: String!
+  label: String
+  type: String
+  description: String
+  enumerations: [Category!]
+  groups: [Group!]
+}
+
+type Domain {
+  id: String!
+  label: String
+  description: String
+  groups: [Group!]!
+  variables: [Variable!]!
+  datasets: [Category!]!
+  rootGroup: Group!
+}
+
+type AlgorithmParameter {
+  name: String!
+  value: [String!]
+  label: String
+  description: String
+  defaultValue: String
+  isMultiple: Boolean
+  isRequired: Boolean
+  min: String
+  max: String
+  type: String
+}
+
+type Algorithm {
+  name: String!
+  parameters: [AlgorithmParameter!]
+  label: String
+  type: String
+  description: String
+}
+
+type GroupResult {
+  name: String!
+  description: String
+  results: [ResultUnion!]!
+}
+
+union ResultUnion = TableResult | RawResult | GroupsResult | HeatMapResult | LineChartResult
+
+type TableResult {
+  name: String!
+  data: [[String!]!]!
+  headers: [Header!]!
+}
+
+type RawResult {
+  rawdata: JSON!
+}
+
+"""
+The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
+"""
+scalar JSON @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf")
+
+type GroupsResult {
+  groups: [GroupResult!]!
+}
+
+type HeatMapResult {
+  name: String!
+  matrix: [[Float!]!]!
+  xAxis: ChartAxis!
+  yAxis: ChartAxis!
+}
+
+type LineChartResult {
+  name: String!
+  xAxis: ChartAxis
+  yAxis: ChartAxis
+  lines: [LineResult!]!
+}
+
+type ChartAxis {
+  label: String
+  categories: [String!]
+}
+
+type ExtraLineInfo {
+  label: String!
+  values: [String!]!
+}
+
+type LineResult {
+  label: String!
+  x: [Float!]!
+  y: [Float!]!
+  extraLineInfos: [ExtraLineInfo!]
+  type: LineType
+}
+
+enum LineType {
+  NORMAL
+  DASHED
+}
+
+type Header {
+  name: String!
+  type: String!
+}
+
+type Experiment {
+  uuid: String
+  author: String
+  createdAt: Float
+  updateAt: Float
+  finishedAt: Float
+  viewed: Boolean
+  status: String
+  shared: Boolean!
+  results: [ResultUnion!]
+  datasets: [String!]!
+  filter: String
+  domain: String!
+  variables: [String!]!
+  algorithm: Algorithm!
+  name: String!
+}
+
+type PartialExperiment {
+  uuid: String
+  author: String
+  createdAt: Float
+  updateAt: Float
+  finishedAt: Float
+  viewed: Boolean
+  status: String
+  shared: Boolean
+  results: [ResultUnion!]
+  datasets: [String!]
+  filter: String
+  domain: String
+  variables: [String!]
+  algorithm: Algorithm
+  name: String
+}
+
+type ListExperiments {
+  currentPage: Float
+  totalPages: Float
+  totalExperiments: Float
+  experiments: [Experiment!]!
+}
+
 type Query {
-  hello: String!
+  domains(ids: [String!] = []): [Domain!]!
+  experiments(name: String = "", page: Float = 0): ListExperiments!
+  expriment(uuid: String!): Experiment!
+  algorithms: [Algorithm!]!
+}
+
+type Mutation {
+  createExperiment(isTransient: Boolean = false, data: ExperimentCreateInput!): Experiment!
+  editExperiment(data: ExperimentEditInput!, uuid: String!): Experiment!
+  removeExperiment(uuid: String!): PartialExperiment!
+}
+
+input ExperimentCreateInput {
+  datasets: [String!]!
+  filter: String
+  domain: String!
+  variables: [String!]!
+  algorithm: AlgorithmInput!
+  name: String!
+  transformations: [FormulaTransformation!]
+  interactions: [[String!]!]
+}
+
+input AlgorithmInput {
+  name: String!
+  parameters: [AlgorithmParamInput!] = []
+  type: String!
+}
+
+input AlgorithmParamInput {
+  name: String!
+  value: [String!]!
+}
+
+input FormulaTransformation {
+  name: String!
+  operation: String!
+}
+
+input ExperimentEditInput {
+  name: String
+  viewed: Boolean
 }