diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 63bbe7be64bc420c304f9d6e7594c31cd4f72855..aafc60f0385d560ce76e6056844aadc8e89d9c0b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -29,7 +29,7 @@ build_development:
     - development
   script:
     - export TAG=$CI_COMMIT_SHORT_SHA
-    - export PROXY_MVT_BIND_PORT=9002
+    - export PROXY_QUERY_BIND_PORT=9002
     - docker-compose build 
     - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
     - docker-compose push
@@ -41,7 +41,7 @@ build_release:
     - tags
   script:
     - export TAG=$(echo $CI_COMMIT_TAG | sed 's/v//g')
-    - export PROXY_MVT_BIND_PORT=9002
+    - export PROXY_QUERY_BIND_PORT=9002
     - docker-compose build
     - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
     - docker-compose push
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 8734e3172be3990e022329347601551962a471ba..eabb7c594e51f8fd16876f20462a879acb471ab8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,6 +7,8 @@ WORKDIR /app
 # Install npm dependencies
 RUN npm install
 # Copy the project
-COPY . /app
+COPY ./helpers ./helpers
+COPY ./routes ./routes
+COPY ./index.js .
 
 CMD [ "npm", "start" ]
\ No newline at end of file
diff --git a/README.md b/README.md
index 950300368214dfc135819c080d1a34399e025106..309bb80541da2ece79791ee8f1ce6e54a16014c1 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
-# Web mapping services proxy
+# Query proxy
 
 ## Installation
 
-This proxy has been developed with [Node.js](https://nodejs.org/en/) and the [http-proxy](https://www.npmjs.com/package/http-proxy) node module.
+This proxy has been written in [Node.js](https://nodejs.org/) using the framework [Express](https://expressjs.com/) and the [http-proxy](https://github.com/http-party/node-http-proxy) node module to proxy requests.
 
 You will need to install Node.js on your computer. Then you will need to install the project's dependencies. Go to the root of the project and run the following command.
 
diff --git a/docker-compose.yml b/docker-compose.yml
index d307d201a2249b4ed56752a2ba36b1525a5d0fba..b3138d16db937813f43d568e4235c1c854fa8ea1 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,12 +1,11 @@
 version: "3"
 
 services:
-  proxy-mvt:
-    restart: unless-stopped
+  proxy-query:
     build: .
     image: registry.forge.grandlyon.com/web-et-numerique/web-et-numerique-internet/data.grandlyon.com/web-portal/components/proxies/web-mapping-services:${TAG}
     ports: 
-      - ${PROXY_MVT_BIND_PORT}:9000
+      - ${PROXY_QUERY_BIND_PORT}:9000
     environment:
       - REDIS_SENTINEL_HOST=${REDIS_SENTINEL_HOST}
       - REDIS_SENTINEL_PORT=${REDIS_SENTINEL_PORT}
@@ -14,12 +13,11 @@ services:
       - REDIS_AUTHORIZED_TTL=${REDIS_AUTHORIZED_TTL}
       - REDIS_UNAUTHORIZED_TTL=${REDIS_UNAUTHORIZED_TTL}
       - ELASTICSEARCH_URL=${ELASTICSEARCH_URL}
+      - LEGACY_AUTH_MIDDLEWARE_URL=${LEGACY_AUTH_MIDDLEWARE_URL}
       - TECHNICAL_ACCOUNT_USERNAME=${TECHNICAL_ACCOUNT_USERNAME}
       - TECHNICAL_ACCOUNT_PASSWORD=${TECHNICAL_ACCOUNT_PASSWORD}
       - PROXY_HOST_TARGET=${PROXY_HOST_TARGET}
       - IGN_KEY=${IGN_KEY}
-    depends_on:
-      - redis-sentinel-1
     restart: unless-stopped
 
   redis-master:
diff --git a/helpers/elasticsearch.helpers.js b/helpers/elasticsearch.helpers.js
new file mode 100644
index 0000000000000000000000000000000000000000..32bb3e64ba03b66adc9f0e3af449207492aeba36
--- /dev/null
+++ b/helpers/elasticsearch.helpers.js
@@ -0,0 +1,76 @@
+const axios = require('axios');
+
+module.exports.getDatasetInfoFromES = async (elasticsearchUrl, layer, fields, cookies, pathToFile) => {
+  let options = {
+    method: 'POST',
+    url: `${elasticsearchUrl}/_search?request_cache=false`,
+    data: {
+      "from": 0,
+      "size": 1,
+      "_source": fields,
+      "query": {
+        "term": {
+          "metadata-fr.link.name.keyword": layer
+        }
+      }
+    },
+  };
+
+  if (pathToFile) {
+    options = {
+      method: 'POST',
+      url: `${elasticsearchUrl}/_search?request_cache=false`,
+      data: {
+        "from": 0,
+        "size": 1,
+        "_source": fields,
+        "query": {
+          "bool": {
+            "should": [
+              {
+                "term": {
+                  "metadata-fr.link.name.keyword": layer
+                }
+              },
+              {
+                "match_phrase": {
+                  "metadata-fr.link.url": {
+                    "query": pathToFile
+                  }
+                }
+              }
+            ]
+          }
+        }
+      }
+    };
+  }
+
+  if (cookies) {
+    options.headers = {
+      cookie: cookies,
+    };
+  }
+
+  let response;
+
+  try {
+    response = await axios(options)
+  } catch (err) {
+    throw {
+      err,
+      status: 500,
+      message: "Request to Elasticsearch failed",
+    };
+  }
+
+  // If no results are found it means the specified dataset mvt layer doesn't exists
+  if (response.data.hits.hits < 1) {
+    throw {
+      status: 404,
+      message: "Layer not found.",
+    };
+  }
+
+  return response.data.hits.hits[0]._source;
+};
\ No newline at end of file
diff --git a/helpers/logs.helpers.js b/helpers/logs.helpers.js
new file mode 100644
index 0000000000000000000000000000000000000000..17d5d984b2ac72810143362dfe70c2a42dcd6507
--- /dev/null
+++ b/helpers/logs.helpers.js
@@ -0,0 +1,7 @@
+module.exports.printLog = (context, value) => {
+  console.log(`${new Date().toLocaleString("fr-FR")} [${context}] [log] `, value);
+}
+
+module.exports.printError = (context, error) => {
+  console.error(`${new Date().toLocaleString("fr-FR")} [${context}] [error] `, error);
+}
\ No newline at end of file
diff --git a/helpers/redis.helpers.js b/helpers/redis.helpers.js
new file mode 100644
index 0000000000000000000000000000000000000000..77986ee54fb066a295d34656e582e0dfc0e06cc9
--- /dev/null
+++ b/helpers/redis.helpers.js
@@ -0,0 +1,61 @@
+const Redis = require("ioredis");
+const printError = require('./logs.helpers.js').printError;
+const printLog = require('./logs.helpers.js').printLog;
+
+module.exports.setRedisValue =  async (redisSentinelHost, redisSentinelPort, redisGroupName, key, value, ttl) => {
+  
+  const redisClient = new Redis({
+    sentinels: [{
+      host: redisSentinelHost,
+      port: redisSentinelPort
+    },],
+    name: redisGroupName,
+  });
+
+  redisClient.on('error', (error) => {
+    printError('Redis client', error);
+    redisClient.disconnect();
+  });
+
+
+  // Set key value with expiration time in seconds
+  const res = await redisClient.set(key, value, 'EX', ttl).catch((error) => {
+    redisClient.disconnect();
+    printError('Redis client', 'Couldn\'t set redis key/value (with ttl).');
+    printError('Redis client', error);
+    return false;
+  });
+
+
+  printLog('Redis client', 'Done setting key/value pair');
+  redisClient.disconnect();
+
+  return res ? true : false;
+};
+
+module.exports.getRedisValue = async (redisSentinelHost, redisSentinelPort, redisGroupName, key) => {
+
+  const redisClient = new Redis({
+    sentinels: [{
+      host: redisSentinelHost,
+      port: redisSentinelPort
+    },],
+    name: redisGroupName,
+  });
+
+  redisClient.on('error', (error) => {
+    printError('Redis client', error);
+    redisClient.disconnect();
+  });
+
+  const res = await redisClient.get(key).catch((error) => {
+    redisClient.disconnect();
+    printError('Redis client', 'Couldn\'t get redis value.');
+    printError('Redis client', error);
+    return false;
+  });
+
+  printLog(`Redis client`, `Value found ${res}`);
+  redisClient.disconnect();
+  return res ? res : null;
+};
\ No newline at end of file
diff --git a/helpers/request-processor.helper.js b/helpers/request-processor.helper.js
new file mode 100644
index 0000000000000000000000000000000000000000..7da0bdae2d0a550af7db6b1d2e313319e8d3eaff
--- /dev/null
+++ b/helpers/request-processor.helper.js
@@ -0,0 +1,62 @@
+const getDatasetInfoFromES = require('./elasticsearch.helpers.js').getDatasetInfoFromES;
+const validateUserAccesses = require('./userAccesses.helpers.js').validateUserAccesses;
+const getRedisValue = require('../helpers/redis.helpers.js').getRedisValue;
+const setRedisValue = require('../helpers/redis.helpers.js').setRedisValue;
+
+module.exports.requestProcessor = async (req, res, layer, service, pathToFile) => {
+    const source = await getDatasetInfoFromES(
+      req.app.locals.config.elasticsearchUrl,
+      layer,
+      [
+        "editorial-metadata.isOpenAccess"
+      ],
+      req.headers.cookie,
+      pathToFile
+    );
+
+    // If dataset is open access proxy the request without adding the technical account credentials
+    if (source['editorial-metadata'].isOpenAccess) {
+      req.app.locals.proxies.unauthenticated.web(req, res, {});
+      return;
+    }
+
+    // If it is a restricted access layer and the user isn't authenticated then directly send a 401 error 
+    if (!source['editorial-metadata'].isOpenAccess && req.headers['x-anonymous-consumer']) {
+      throw {
+        status: 401,
+        message: "Unauthenticated, you need to be authenticated to access this resource."
+      }
+    }
+
+    // Look for an existing value of the user rights for the layer in redis
+    let userAccesses = await getRedisValue(req.app.locals.config.redisSentinelHost, req.app.locals.config.redisSentinelPort, req.app.locals.config.redisGroupName, `download-${layer}-${service}-${req.headers['x-consumer-username']}`);
+
+    // If value found and true, proxy the request adding the technical account credentials
+    if (userAccesses === 'true') {
+      req.app.locals.proxies.authenticated.web(req, res, {});
+      return;
+    }
+
+    // If value found and false, directly send a 403 forbidden error
+    if (userAccesses === 'false') {
+      throw {
+        status: 403,
+        message: "Forbidden access."
+      }
+    }
+
+    // If no pre-existing value for that user, layer and service triple then check the user rights
+    userAccesses = await validateUserAccesses(req.app.locals.config.legacyAuthMiddlewareUrl, req.headers, `${req.params.repo}/${layer}`, service);
+
+    if(!userAccesses) {
+      await setRedisValue(req.app.locals.config.redisSentinelHost, req.app.locals.config.redisSentinelPort, req.app.locals.config.redisGroupName, `download-${layer}-${service}-${req.headers['x-consumer-username']}`, false, req.app.locals.config.redisUnauthorizedTTL);
+      throw {
+        status: 403,
+        message: "Forbidden access."
+      }
+    }
+
+    await setRedisValue(req.app.locals.config.redisSentinelHost, req.app.locals.config.redisSentinelPort, req.app.locals.config.redisGroupName, `download-${layer}-${service}-${req.headers['x-consumer-username']}`, true, req.app.locals.config.redisAuthorizedTTL);
+    req.app.locals.proxies.authenticated.web(req, res, {});
+    return;
+}
\ No newline at end of file
diff --git a/helpers/userAccesses.helpers.js b/helpers/userAccesses.helpers.js
new file mode 100644
index 0000000000000000000000000000000000000000..61ddfdf9037c4116afdcf65eda4ed4e24269ddc0
--- /dev/null
+++ b/helpers/userAccesses.helpers.js
@@ -0,0 +1,31 @@
+const axios = require('axios');
+
+module.exports.validateUserAccesses = async (legacyAuthMiddlewareUrl, headers, pathToLayer, service) => {
+  const options = {};
+
+  if (headers.cookie) {
+    options.Cookie = headers.cookie;
+  }
+
+  if(headers["x-xsrf-token"]) {
+    options["x-xsrf-token"] = headers['x-xsrf-token'];
+  }
+
+  const response = await axios.get(`${legacyAuthMiddlewareUrl}/user/resources`, {
+    headers: options,
+  }).catch((err) => {
+    throw {
+      err,
+      status: err.response.status,
+      message: "Request to Legacy Auth Middleware failed",
+    };
+  });
+
+  if (response.data &&
+      response.data.length > 0 &&
+      response.data.find(e => e.urlPattern === pathToLayer && e.serviceName === service && new Date(e.validUntil) > new Date())) {
+    return true;
+  }
+
+  return false;
+}
\ No newline at end of file
diff --git a/index.js b/index.js
index 8d0a144595cd27a1a5a8924bae120561faf47db0..fc9ff042cc7d19c159a1c0460c7dbdc9d61c0036 100644
--- a/index.js
+++ b/index.js
@@ -1,270 +1,93 @@
-const http = require('http');
+const express = require('express');
+const app = express();
+const port = 9000;
 const httpProxy = require('http-proxy');
-const axios = require('axios');
-const URL = require('url');
-const Redis = require("ioredis");
+const cookieParser = require('cookie-parser');
+const printError = require('./helpers/logs.helpers.js').printError;
+const printLog = require('./helpers/logs.helpers.js').printLog;
 
-// CONFIG
-const redisSentinelHost = process.env.REDIS_SENTINEL_HOST;
-const redisSentinelPort = process.env.REDIS_SENTINEL_PORT;
-const redisGroupName = process.env.REDIS_GROUP_NAME;
-const redisAuthorizedTTL = process.env.REDIS_AUTHORIZED_TTL; // In seconds
-const redisUnauthorizedTTL = process.env.REDIS_UNAUTHORIZED_TTL; // In seconds
-const elasticsearchUrl = process.env.ELASTICSEARCH_URL;
-const technicalAccountUsername = process.env.TECHNICAL_ACCOUNT_USERNAME;
-const technicalAccountPassword = process.env.TECHNICAL_ACCOUNT_PASSWORD;
-const proxyHostTarget = process.env.PROXY_HOST_TARGET;
-const ignKey = process.env.IGN_KEY;
+// Parse the request headers in order to populate req.cookies
+app.use(cookieParser());
 
-// Configuring the different proxy server
-// Proxy IGN Ortho
-var ingProxy = httpProxy.createProxyServer({
-  changeOrigin: true,
-  target: 'https://wxs.ign.fr/' + ignKey + '/geoportail/r/wms/',
-});
-
-// Configuring the different proxy server
-// Proxy WMS
-var wmsProxy = httpProxy.createProxyServer({
-  changeOrigin: true,
-  target: proxyHostTarget,
-  auth: `${technicalAccountUsername}:${technicalAccountPassword}`,
+app.use(function(req, res, next) {
+  res.header("Access-Control-Allow-Origin", "*");
+  next();
 });
 
-// Configure and create an HTTP proxy server
-var mvtProxy = httpProxy.createProxyServer({
-  changeOrigin: true,
-  target: proxyHostTarget,
-  auth: `${technicalAccountUsername}:${technicalAccountPassword}`,
-});
-
-var mvtUnauthProxy = httpProxy.createProxyServer({
-  changeOrigin: true,
-  target: proxyHostTarget,
-});
+// CONFIG
 
-ingProxy.on('proxyReq', function (proxyReq, req) {
-  req._proxyReq = proxyReq;
-});
+app.locals.config = {
+  redisSentinelHost: process.env.REDIS_SENTINEL_HOST,
+  redisSentinelPort: process.env.REDIS_SENTINEL_PORT,
+  redisGroupName: process.env.REDIS_GROUP_NAME,
+  redisAuthorizedTTL: process.env.REDIS_AUTHORIZED_TTL, // In seconds
+  redisUnauthorizedTTL: process.env.REDIS_UNAUTHORIZED_TTL, // In seconds
+  elasticsearchUrl: process.env.ELASTICSEARCH_URL,
+  legacyAuthMiddlewareUrl: process.env.LEGACY_AUTH_MIDDLEWARE_URL,
+  technicalAccountUsername: process.env.TECHNICAL_ACCOUNT_USERNAME,
+  technicalAccountPassword: process.env.TECHNICAL_ACCOUNT_PASSWORD,
+  proxyHostTarget: process.env.PROXY_HOST_TARGET,
+  ignKey: process.env.IGN_KEY,
+}
 
-// keep a referece of the proxyRequest in the req object
-wmsProxy.on('proxyReq', function (proxyReq, req) {
-  req._proxyReq = proxyReq;
-});
 
-mvtProxy.on('proxyReq', function (proxyReq, req) {
-  req._proxyReq = proxyReq;
-});
+// Proxies configuration
 
-mvtUnauthProxy.on('proxyReq', function (proxyReq, req) {
+// IGN Ortho
+const IGNProxy = httpProxy.createProxyServer({
+  changeOrigin: true,
+  target: 'https://wxs.ign.fr/' + app.locals.config.ignKey + '/geoportail/r/wms/',
+}).on('proxyReq', (proxyReq, req) => {
+  // keep a reference of the proxyRequest in the req object
   req._proxyReq = proxyReq;
-});
-
-ingProxy.on('error', function (err, req, res) {
-  // If the client cancelled the request, abort the request to the upstream server
-  if (req.socket.destroyed && err.code === 'ECONNRESET') {
-    req._proxyReq.abort();
-  }
-  return console.log(`ING Error, req.socket.destroyed: ${req.socket.destroyed}, ${err}`);
-});
-
-wmsProxy.on('error', function (err, req, res) {
+}).on('error', (err, req, res) => {
   // If the client cancelled the request, abort the request to the upstream server
   if (req.socket.destroyed && err.code === 'ECONNRESET') {
     req._proxyReq.abort();
   }
-  return console.log(`WMS Error, req.socket.destroyed: ${req.socket.destroyed}, ${err}`);
+  printError(`ING proxy error, req.socket.destroyed: ${req.socket.destroyed}, ${err}`);
+  return;
 });
 
-mvtProxy.on('error', function (err, req, res) {
+const AuthenticatedProxy = httpProxy.createProxyServer({
+  changeOrigin: true,
+  target: app.locals.config.proxyHostTarget,
+  auth: `${app.locals.config.technicalAccountUsername}:${app.locals.config.technicalAccountPassword}`,
+}).on('proxyReq', (proxyReq, req) => {
+  // keep a reference of the proxyRequest in the req object
+  req._proxyReq = proxyReq;
+}).on('error', (err, req, res) => {
   // If the client cancelled the request, abort the request to the upstream server
   if (req.socket.destroyed && err.code === 'ECONNRESET') {
     req._proxyReq.abort();
   }
-  return console.log(`MVT Error, req.socket.destroyed: ${req.socket.destroyed}, ${err}`);
+  printError(`Authenticated proxy error, req.socket.destroyed: ${req.socket.destroyed}, ${err}`);
+  return;
 });
 
-mvtUnauthProxy.on('error', function (err, req, res) {
+var UnauthenticatedProxy = httpProxy.createProxyServer({
+  changeOrigin: true,
+  target: app.locals.config.proxyHostTarget,
+}).on('proxyReq', (proxyReq, req) => {
+  // keep a reference of the proxyRequest in the req object
+  req._proxyReq = proxyReq;
+}).on('error', (err, req, res) => {
   // If the client cancelled the request, abort the request to the upstream server
   if (req.socket.destroyed && err.code === 'ECONNRESET') {
     req._proxyReq.abort();
   }
-  return console.log(`MVT Unauthenticated Error, req.socket.destroyed: ${req.socket.destroyed}, ${err}`);
+  printError(`Unauthenticated proxy Error, req.socket.destroyed: ${req.socket.destroyed}, ${err}`);
+  return;
 });
 
-// Create an HTTP server
-http.createServer(async function (req, res) {
-
-  /******* IGN */
-  if (req.url.includes('/ign')) {
-    req.headers['referer'] = 'grandlyon.com';
-    ingProxy.web(req, res, {});
-    return;
-  }
-
-  /******************* WMS *****************************/
-  if (req.url.includes('/wms')) {
-    wmsProxy.web(req, res, {});
-    return;
-  }
-
-  /******************* MVT *****************************/
-  if (req.url.includes('/mvt')) {
-    // If no cookies then then we can't identify a user and directly proxy the request without credentials
-    if (req.headers["x-anonymous-consumer"]) {
-      mvtUnauthProxy.web(req, res, {});
-      return;
-    }
+app.locals.proxies = {
+  ign: IGNProxy,
+  authenticated: AuthenticatedProxy,
+  unauthenticated: UnauthenticatedProxy,
+};
 
-    // Read the requested layer from the url
-    const layer = getParameterValueFromUrl(req.url, 'LAYERS');
-
-    if (!layer) {
-      res.statusCode = 400;
-      res.end();
-      return;
-    }
-
-    const userRightsOnTheLayer = await getRedisValue(`${layer}-${req.headers['x-consumer-username']}`);
-
-    if (userRightsOnTheLayer === 'true') {
-      mvtProxy.web(req, res, {});
-      return;
-    }
-
-    if (userRightsOnTheLayer === 'false') {
-      res.statusCode = 401;
-      res.end();
-      return;
-    }
-
-    const options = {
-      method: 'POST',
-      url: `${elasticsearchUrl}/_search?&request_cache=false`,
-      data: {
-        "from": 0,
-        "size": 1,
-        "_source": [
-          "editorial-metadata.isSample",
-          "editorial-metadata.isOpenAccess"
-        ],
-        "query": {
-          "term": {
-            "metadata-fr.link.name": layer
-          }
-        }
-      },
-      headers: {
-        cookie: req.headers.cookie,
-      }
-    };
-
-    let response;
-
-    try {
-      response = await axios(options)
-    } catch (err) {
-      printError('Request to ES failed', err);
-      res.statusCode = 500;
-      res.end();
-      return;
-    }
-
-
-    // If no results are found it means the specified dataset mvt layer doesn't exists
-    if (response.data.hits.hits < 1) {
-      printError('Request to ES', 'MVT not found');
-      res.statusCode = 404;
-      res.end();
-      return;
-    }
-
-    const editorialMetadata = response.data.hits.hits[0]._source['editorial-metadata'];
-
-    if (!editorialMetadata.isOpenAccess && editorialMetadata.isSample) {
-      setRedisValue(`${layer}-${req.headers['x-consumer-username']}`, false, redisUnauthorizedTTL);
-      res.statusCode = 401;
-      res.end();
-      return;
-    }
-
-    await setRedisValue(`${layer}-${req.headers['x-consumer-username']}`, true, redisAuthorizedTTL);
-
-    mvtProxy.web(req, res, {});
-  }
-
-}).listen(9000);
-
-// HELPERS
-
-function getParameterValueFromUrl(url, paramName) {
-
-  const queryParams = URL.parse(url, true).query;
-
-  return queryParams && queryParams[paramName] ? queryParams[paramName] : null;
-
-}
-
-async function setRedisValue(key, value, ttl) {
-  const redisClient = new Redis({
-    sentinels: [{
-      host: redisSentinelHost,
-      port: redisSentinelPort
-    },],
-    name: redisGroupName,
-  });
-
-  redisClient.on('error', (error) => {
-    printError('Redis client', error);
-    redisClient.disconnect();
-  });
-
-
-  // Set key value with expiration time in seconds
-  const res = await redisClient.set(key, value, 'EX', ttl).catch((error) => {
-    redisClient.disconnect();
-    printError('Redis client', 'Couldn\'t set redis key/value (with ttl).');
-    printError('Redis client', error);
-    return false;
-  });
-
-
-  printLog('Redis client', 'Done setting key/value pair');
-  redisClient.disconnect();
-
-  return res ? true : false;
-}
-
-async function getRedisValue(key) {
-  const redisClient = new Redis({
-    sentinels: [{
-      host: redisSentinelHost,
-      port: redisSentinelPort
-    },],
-    name: redisGroupName,
-  });
-
-  redisClient.on('error', (error) => {
-    printError('Redis client', error);
-    redisClient.disconnect();
-  });
-
-  const res = await redisClient.get(key).catch((error) => {
-    redisClient.disconnect();
-    printError('Redis client', 'Couldn\'t get redis value.');
-    printError('Redis client', error);
-    return false;
-  });
-
-  printLog(`Redis client`, `Value found ${res}`);
-  redisClient.disconnect();
-  return res ? res : null;
-}
-
-function printLog(context, value) {
-  console.log(`${new Date().toLocaleString("fr-FR")} [${context}] [log] `, value);
-}
+// ROUTES
+app.use(require('./routes/index.js'));
 
-function printError(context, error) {
-  console.error(`${new Date().toLocaleString("fr-FR")} [${context}] [error] `, error);
-}
\ No newline at end of file
+// STARTING SERVER
+app.listen(port, () => printLog('index.js', `Proxy listening on port: ${port}`));
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 1064f9de32e312e11250cca3b0e08d36f91a25a5..0f390b2c62854e026d5662a0e918fb1c3cb70c55 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,9 +1,23 @@
 {
-  "name": "proxy-map-services",
-  "version": "1.0.1",
+  "name": "query-proxy",
+  "version": "2.0.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
+    "accepts": {
+      "version": "1.3.7",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+      "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+      "requires": {
+        "mime-types": "~2.1.24",
+        "negotiator": "0.6.2"
+      }
+    },
+    "array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+    },
     "axios": {
       "version": "0.19.0",
       "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
@@ -36,16 +50,80 @@
         }
       }
     },
+    "body-parser": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+      "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+      "requires": {
+        "bytes": "3.1.0",
+        "content-type": "~1.0.4",
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "http-errors": "1.7.2",
+        "iconv-lite": "0.4.24",
+        "on-finished": "~2.3.0",
+        "qs": "6.7.0",
+        "raw-body": "2.4.0",
+        "type-is": "~1.6.17"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "bytes": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+      "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
+    },
     "cluster-key-slot": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz",
       "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw=="
     },
+    "content-disposition": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+      "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+      "requires": {
+        "safe-buffer": "5.1.2"
+      }
+    },
+    "content-type": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+    },
     "cookie": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
       "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
     },
+    "cookie-parser": {
+      "version": "1.4.5",
+      "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz",
+      "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==",
+      "requires": {
+        "cookie": "0.4.0",
+        "cookie-signature": "1.0.6"
+      }
+    },
+    "cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+    },
     "debug": {
       "version": "3.2.6",
       "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
@@ -59,11 +137,122 @@
       "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
       "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
     },
+    "depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+    },
+    "destroy": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+    },
+    "ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+    },
+    "encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+    },
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+    },
+    "etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+    },
     "eventemitter3": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
       "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg=="
     },
+    "express": {
+      "version": "4.17.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+      "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+      "requires": {
+        "accepts": "~1.3.7",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.19.0",
+        "content-disposition": "0.5.3",
+        "content-type": "~1.0.4",
+        "cookie": "0.4.0",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "~1.1.2",
+        "fresh": "0.5.2",
+        "merge-descriptors": "1.0.1",
+        "methods": "~1.1.2",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.7",
+        "proxy-addr": "~2.0.5",
+        "qs": "6.7.0",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.1.2",
+        "send": "0.17.1",
+        "serve-static": "1.14.1",
+        "setprototypeof": "1.1.1",
+        "statuses": "~1.5.0",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "finalhandler": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+      "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+      "requires": {
+        "debug": "2.6.9",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.3",
+        "statuses": "~1.5.0",
+        "unpipe": "~1.0.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
     "follow-redirects": {
       "version": "1.9.0",
       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz",
@@ -72,10 +261,27 @@
         "debug": "^3.0.0"
       }
     },
-    "http": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/http/-/http-0.0.0.tgz",
-      "integrity": "sha1-huYybSnF0Dnen6xYSkVon5KfT3I="
+    "forwarded": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+    },
+    "fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+    },
+    "http-errors": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+      "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+      "requires": {
+        "depd": "~1.1.2",
+        "inherits": "2.0.3",
+        "setprototypeof": "1.1.1",
+        "statuses": ">= 1.5.0 < 2",
+        "toidentifier": "1.0.0"
+      }
     },
     "http-proxy": {
       "version": "1.18.0",
@@ -87,6 +293,19 @@
         "requires-port": "^1.0.0"
       }
     },
+    "iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      }
+    },
+    "inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+    },
     "ioredis": {
       "version": "4.14.1",
       "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.14.1.tgz",
@@ -113,6 +332,11 @@
         }
       }
     },
+    "ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+    },
     "is-buffer": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
@@ -128,20 +352,96 @@
       "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
       "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
     },
+    "media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+    },
+    "merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+    },
+    "methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+    },
+    "mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+    },
+    "mime-db": {
+      "version": "1.43.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
+      "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
+    },
+    "mime-types": {
+      "version": "2.1.26",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
+      "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
+      "requires": {
+        "mime-db": "1.43.0"
+      }
+    },
     "ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
     },
-    "punycode": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
-      "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
+    "negotiator": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+      "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
+    },
+    "on-finished": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+      "requires": {
+        "ee-first": "1.1.1"
+      }
+    },
+    "parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+    },
+    "path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+    },
+    "proxy-addr": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
+      "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
+      "requires": {
+        "forwarded": "~0.1.2",
+        "ipaddr.js": "1.9.1"
+      }
+    },
+    "qs": {
+      "version": "6.7.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+      "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
     },
-    "querystring": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
-      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
+    "range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+    },
+    "raw-body": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+      "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+      "requires": {
+        "bytes": "3.1.0",
+        "http-errors": "1.7.2",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      }
     },
     "redis-commands": {
       "version": "1.5.0",
@@ -166,19 +466,112 @@
       "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
       "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
     },
+    "safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "send": {
+      "version": "0.17.1",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+      "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+      "requires": {
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "destroy": "~1.0.4",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "~1.7.2",
+        "mime": "1.6.0",
+        "ms": "2.1.1",
+        "on-finished": "~2.3.0",
+        "range-parser": "~1.2.1",
+        "statuses": "~1.5.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          },
+          "dependencies": {
+            "ms": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+              "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+            }
+          }
+        },
+        "ms": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+        }
+      }
+    },
+    "serve-static": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+      "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+      "requires": {
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.17.1"
+      }
+    },
+    "setprototypeof": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+      "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+    },
     "standard-as-callback": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz",
       "integrity": "sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg=="
     },
-    "url": {
-      "version": "0.11.0",
-      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
-      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+    "statuses": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+      "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+    },
+    "toidentifier": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+      "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+    },
+    "type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
       "requires": {
-        "punycode": "1.3.2",
-        "querystring": "0.2.0"
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
       }
+    },
+    "unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+    },
+    "utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+    },
+    "vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
     }
   }
 }
diff --git a/package.json b/package.json
index 9585de3de0e58e64c9e98ebe36f719a7a22db28b..03304e211c3867fa5baa3acdced96a729c2c07b4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
-  "name": "proxy-map-services",
-  "version": "1.1.0",
+  "name": "query-proxy",
+  "version": "2.0.0",
   "description": "",
   "main": "index.js",
   "scripts": {
@@ -11,10 +11,9 @@
   "license": "GNU Affero General Public License v3.0",
   "dependencies": {
     "axios": "^0.19.0",
-    "cookie": "^0.4.0",
-    "http": "0.0.0",
+    "cookie-parser": "^1.4.5",
+    "express": "^4.17.1",
     "http-proxy": "^1.18.0",
-    "ioredis": "^4.14.1",
-    "url": "^0.11.0"
+    "ioredis": "^4.14.1"
   }
 }
diff --git a/routes/download.routes.js b/routes/download.routes.js
new file mode 100644
index 0000000000000000000000000000000000000000..c3993a8453091494076765833fa940c645c318c4
--- /dev/null
+++ b/routes/download.routes.js
@@ -0,0 +1,112 @@
+var router = require('express').Router();
+const requestProcessor = require('../helpers/request-processor.helper.js').requestProcessor;
+const printError = require('../helpers/logs.helpers.js').printError;
+
+router.get('/wms/:repo*', async (req, res) => {
+
+  const layer = req.query.layers || req.query.LAYERS;
+
+  try {
+
+    return await requestProcessor(req, res, layer, 'wms');
+
+  } catch (err) {
+    printError(`/wms/${req.params.repo}?layers=${layer}`, err);
+    res.status(err.status).send(err);
+    return;
+  }
+});
+
+router.get('/wfs/:repo*', async (req, res) => {
+
+  const layer = req.query.typename || req.query.TYPENAME;
+
+  try {
+
+    return await requestProcessor(req, res, layer, 'wfs');
+
+  } catch (err) {
+    printError(`/wfs/${req.params.repo}?typename=${layer}`, err);
+    res.status(err.status).send(err);
+    return;
+  }
+});
+
+router.get('/ws/:repo/:layer*', async (req, res) => {
+
+  let layer = req.params.layer;
+
+  if(layer.includes('.shp')) {
+    layer = layer.split('.shp')[0];
+  }
+
+  try {
+  
+    return await requestProcessor(req, res, layer, 'ws');
+
+  } catch (err) {
+    printError(`/ws/${req.params.repo}/${layer}`, err);
+    res.status(err.status).send(err);
+    return;
+  }
+});
+
+router.get('/kml/:repo*', async (req, res) => {
+
+  const layer = req.query.typename || req.query.TYPENAME;
+
+  try {
+
+    return await requestProcessor(req, res, layer, 'kml');
+
+  } catch (err) {
+    printError(`/kml/${req.params.repo}?typename=${layer}`, err);
+    res.status(err.status).send(err);
+    return;
+  }
+});
+
+router.get('/wcs/:repo*', async (req, res) => {
+
+  const layer = req.query.identifiers || req.query.IDENTIFIERS;
+
+  try {
+
+    return await requestProcessor(req, res, layer, 'wcs');
+
+  } catch (err) {
+    printError(`/wcs/${req.params.repo}?identifiers=${layer}`, err);
+    res.status(err.status).send(err);
+    return;
+  }
+});
+
+router.get('/files/:repo/:layer*', async (req, res) => {
+
+  const layer = req.params.layer;
+
+  try {
+  
+    return await requestProcessor(req, res, layer, 'files', req.path);
+
+  } catch (err) {
+    printError(`/files/${req.params.repo}/${layer}`, err);
+    res.status(err.status).send(err);
+    return;
+  }
+});
+
+router.get('/catalogue*', async (req, res) => {
+
+  try {
+  
+    req.app.locals.proxies.unauthenticated.web(req, res, {});
+
+  } catch (err) {
+    printError(`/catalogue`, err);
+    res.status(err.status).send(err);
+    return;
+  }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/routes/index.js b/routes/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..80eee0e2e1f9e48ba3d94bbb166518104c36f5d2
--- /dev/null
+++ b/routes/index.js
@@ -0,0 +1,6 @@
+var router = require('express').Router();
+
+router.use('/map', require('./map.routes.js'));
+router.use('/download', require('./download.routes.js'));
+
+module.exports = router;
\ No newline at end of file
diff --git a/routes/map.routes.js b/routes/map.routes.js
new file mode 100644
index 0000000000000000000000000000000000000000..ae1af067699b70aaf9d469718d20560ec845d35e
--- /dev/null
+++ b/routes/map.routes.js
@@ -0,0 +1,77 @@
+const router = require('express').Router();
+const getRedisValue = require('../helpers/redis.helpers.js').getRedisValue;
+const setRedisValue = require('../helpers/redis.helpers.js').setRedisValue;
+const getDatasetInfoFromES = require('../helpers/elasticsearch.helpers.js').getDatasetInfoFromES;
+const printError = require('../helpers/logs.helpers.js').printError;
+
+router.get('/ign', (req, res, next) => {
+  req.headers['referer'] = 'grandlyon.com';
+  req.app.locals.proxies.ign.web(req, res, {});
+  return;
+});
+
+router.get('/wms*', (req, res, next) => {
+  req.app.locals.proxies.authenticated.web(req, res, {});
+  return;
+});
+
+router.get('/mvt*', async (req, res, next) => {
+  
+  // If no cookies then then we can't identify a user and directly proxy the request without credentials
+  if (req.headers["x-anonymous-consumer"]) {
+    req.app.locals.proxies.unauthenticated.web(req, res, {});
+    return;
+  }
+
+  // Read the requested layer from the url
+  const layer = req.query.LAYERS;
+
+  if (!layer) {
+    res.status(400).send({err: "Bad request, missing LAYERS parameter."})
+    return;
+  }
+
+  const userRightsOnTheLayer = await getRedisValue(req.app.locals.config.redisSentinelHost, req.app.locals.config.redisSentinelPort, req.app.locals.config.redisGroupName, `map-mvt-${layer}-${req.headers['x-consumer-username']}`);
+
+  if (userRightsOnTheLayer === 'true') {
+    req.app.locals.proxies.authenticated.web(req, res, {});
+    return;
+  }
+
+  if (userRightsOnTheLayer === 'false') {
+    res.status(401).send({err: 'Unauthenticated, you don\'t have access to this layer'});
+    return;
+  }
+
+  let source;
+
+  try {
+    source = await getDatasetInfoFromES(
+      req.app.locals.config.elasticsearchUrl,
+      layer,
+      [
+        "editorial-metadata.isSample",
+        "editorial-metadata.isOpenAccess"
+      ],
+      req.headers.cookie,
+    );
+  } catch(err) {
+    printError('/mvt', err);
+    res.status(err.status).send(err);
+    return;
+  }
+
+  const editorialMetadata = source['editorial-metadata'];
+
+  if (!editorialMetadata.isOpenAccess && editorialMetadata.isSample) {
+    await setRedisValue(req.app.locals.config.redisSentinelHost, req.app.locals.config.redisSentinelPort, req.app.locals.config.redisGroupName, `map-mvt-${layer}-${req.headers['x-consumer-username']}`, false, req.app.locals.config.redisUnauthorizedTTL);
+    res.status(401).send();
+    return;
+  }
+
+  await setRedisValue(req.app.locals.config.redisSentinelHost, req.app.locals.config.redisSentinelPort, req.app.locals.config.redisGroupName, `map-mvt-${layer}-${req.headers['x-consumer-username']}`, true, req.app.locals.config.redisAuthorizedTTL);
+
+  req.app.locals.proxies.authenticated.web(req, res, {});
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/template.env b/template.env
index 01d8d57e6cc9a05cade59498107a0313bc43ec11..746a801ab3fe4464f114307e720dee1a62827ab5 100644
--- a/template.env
+++ b/template.env
@@ -1,9 +1,10 @@
 TAG=<tag of the image to be used>
-PROXY_MAP_SERVICES_BIND_PORT=<proxy port>
+PROXY_QUERY_BIND_PORT=<proxy port>
 TECHNICAL_ACCOUNT_PASSWORD=<username of the technical account which has access to all wms and mvt>
 TECHNICAL_ACCOUNT_USERNAME=<password of the technical account>
 ELASTICSEARCH_URL=<url of the elasticsearch instance>
 PROXY_HOST_TARGET=<url of the map services>
+LEGACY_AUTH_MIDDLEWARE_URL=<url of the legacy auth middleware>
 REDIS_MASTER_HOST=<host of the redis master>
 REDIS_SENTINEL_PORT=<port of the redis sentinel>
 REDIS_SENTINEL_HOST=<host of the redis sentinel>