diff --git a/src/libs/middlewares/downloadDatabase.js b/src/libs/middlewares/downloadDatabase.js
new file mode 100644
index 0000000000000000000000000000000000000000..d1566b8c11c334e688da4f34641c728820c10e2c
--- /dev/null
+++ b/src/libs/middlewares/downloadDatabase.js
@@ -0,0 +1,67 @@
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const execute = require(`${libs}/middlewares/query`).execute;
+
+const request = require(`request`);
+
+const config = require(`${libs}/config`);
+
+const Download = require(`${libs}/models/download`);
+
+module.exports = function download(table, mappingTable) {
+    return (req, res, next) => {
+        // First, query the mapping
+        execute(`SELECT target_name, name FROM ${mappingTable}`, undefined, (err, result) => {
+            if(err) {
+                log.error(err.stack);
+                next(new Error('Request could not be satisfied due to a database error.'));
+            } else {
+                let header = '';
+                req.sql.from(table);
+                result.forEach((field) => {
+                    req.sql.field(table + '.' + field.name, field.target_name);
+                    if(header === '') header += field.target_name;
+                    else header = header + ';' + field.target_name;
+                });
+
+                let form = {
+                    query: req.sql.toString(),
+                    table: req.sql.tableFrom,
+                    name: req.sql.tableFrom,
+                    username: req.user.name,
+                    email: req.user.email,
+                    header
+                };
+
+                request.post(config.cdn.url + '/api/v1/file', {form}, (err, response, body) => {
+                    if(err) {
+                        log.error(err);
+                        return res.json({error: err});
+                    }
+
+                    Download.findOne({query: req.sql.toString()}, (err, download) => {
+                        if(download) {
+                            download.updatedAt = Date.now();
+                        } else {
+                            download = new Download({
+                                userId: req.user._id,
+                                table,
+                                mappingTable,
+                                query: req.sql.toString()
+                            });
+                        }
+
+                        download.save((err) => {
+                            if(err) {
+                                log.error(err);
+                            }
+                            res.json({msg: 'Wait for download email', waitForIt: true});
+                        });
+                    });
+                });
+            }
+        });
+    }
+};
\ No newline at end of file
diff --git a/src/libs/middlewares/query.js b/src/libs/middlewares/query.js
index b76f6ff7759efeefc89ef0d394770871e7bd4c05..ae002df2fb13727152497dc9ad61c1371d98d194 100644
--- a/src/libs/middlewares/query.js
+++ b/src/libs/middlewares/query.js
@@ -26,4 +26,4 @@ function execute(text, values, cb) {
     });
 }
 
-module.exports = query;
+module.exports = {query, execute};
diff --git a/src/libs/models/download.js b/src/libs/models/download.js
new file mode 100644
index 0000000000000000000000000000000000000000..8cd16000bf7716ebfeaf64ffb9133a95ee79dd3f
--- /dev/null
+++ b/src/libs/models/download.js
@@ -0,0 +1,37 @@
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const libs = `${process.cwd()}/libs`;
+const log = require(`${libs}/log`)(module);
+const User = require(`${libs}/models/user`);
+
+let Download = new Schema({
+    userId: {
+        type: Schema.Types.ObjectId,
+        required: true,
+        ref: 'User'
+    },
+    table: {
+        type: String,
+        required: true
+    },
+    mappingTable: {
+        type: String,
+        required: true
+    },
+    query: {
+        type: String,
+        required: true
+    },
+    createdAt: {
+        type: Date,
+        required: true,
+        default: Date.now
+    },
+    updatedAt: {
+        type: Date,
+        required: true,
+        default: Date.now
+    }
+});
+
+module.exports = mongoose.model('Download', Download);
diff --git a/src/libs/routes/api.js b/src/libs/routes/api.js
index e205726df4fdc6fe117337cebd5eaf4b66f0091f..8dbc4cb0bfd35289924e9469fac17f95ec5f5f39 100644
--- a/src/libs/routes/api.js
+++ b/src/libs/routes/api.js
@@ -46,6 +46,8 @@ const resetToken = require(`${libs}/routes/resetToken`);
 
 const educationYears = require(`${libs}/routes/educationYears`);
 
+const downloads = require(`${libs}/routes/downloads`);
+
 const infrastructure = require(`${libs}/routes/infrastructure`);
 
 const distributionFactor = require(`${libs}/routes/distributionFactor`);
@@ -77,6 +79,7 @@ api.use('/auth/token', oauth2.token);
 api.use('/verify', verifyToken);
 api.use('/reset', resetToken);
 api.use('/education_years', educationYears);
+api.use('/downloads', downloads);
 api.use('/infrastructure', infrastructure);
 api.use('/distribution_factor', distributionFactor);
 api.use('/siope', siope);
diff --git a/src/libs/routes/city.js b/src/libs/routes/city.js
index 65fba27ea92db049f156a196b7fdbd2da0fc0756..1f6aee12630ba58b48f2f53bb17fc9ab10b4fad1 100644
--- a/src/libs/routes/city.js
+++ b/src/libs/routes/city.js
@@ -6,7 +6,7 @@ const libs = `${process.cwd()}/libs`;
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
diff --git a/src/libs/routes/class.js b/src/libs/routes/class.js
index 63e782292cfba57c5eb61cd2551a55aaff981aee..1d944ccd1a45bd47a8409daf804fbc61480a0bb3 100644
--- a/src/libs/routes/class.js
+++ b/src/libs/routes/class.js
@@ -8,7 +8,7 @@ const log = require(`${libs}/log`)(module);
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
@@ -18,7 +18,9 @@ const id2str = require(`${libs}/middlewares/id2str`);
 
 const config = require(`${libs}/config`); 
 
-const request = require(`request`);
+const download = require(`${libs}/middlewares/downloadDatabase`);
+
+const passport = require('passport');
 
 const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
 
@@ -342,42 +344,6 @@ classApp.get('/', rqfCount.parse(), rqfCount.build(), (req, res, next) => {
    next();
 }, query, id2str.transform(), response('class'));
 
-classApp.get('/download', (req, res, next) => {
-    // first, query the mapping
-    req.sql.from('mapping_turma')
-        .field('target_name')
-        .field('name');
-    next();
-}, query, (req, res, next) => {
-    req.resetSql();
-    next();
-}, rqfCount.parse(), rqfCount.build(), (req, res, next) => {
-    let username = req.query.user;
-    let email = req.query.email;
-
-    req.sql.from('turma')
-    .field('*');
-    let header = '';
-    req.result.forEach((result) => {
-        if(header === '') header += result.name;
-        else header = header + ';' + result.name;
-    });
-
-    let form = {
-        query: req.sql.toString(),
-        table: req.sql.tableFrom,
-        name: req.sql.tableFrom,
-        username,
-        email,
-        header
-    };
-    request.post(config.cdn.url + '/api/v1/file', {form}, (err, response, body) => {
-        if(err) {
-            log.error(err);
-            return res.json({error: err});
-        }
-        res.json({msg: 'Wait for download email'});
-    });
-});
+classApp.get('/download', passport.authenticate('bearer', { session: false }), rqfCount.parse(), rqfCount.build(), download('turma', 'mapping_turma'));
 
 module.exports = classApp;
diff --git a/src/libs/routes/classroom.js b/src/libs/routes/classroom.js
index f2dd5a3b5051509e1b3c62ba74f7a1267e807117..084291a740014d2811897b62fc16f51aeef3c0d1 100644
--- a/src/libs/routes/classroom.js
+++ b/src/libs/routes/classroom.js
@@ -6,7 +6,7 @@ const libs = `${process.cwd()}/libs`;
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
diff --git a/src/libs/routes/distributionFactor.js b/src/libs/routes/distributionFactor.js
index f7d40502e6a0e6802b630405d916d0829b65be11..5cd08ebacb275672df829fe86a0ad0ae4ba42796 100644
--- a/src/libs/routes/distributionFactor.js
+++ b/src/libs/routes/distributionFactor.js
@@ -6,7 +6,7 @@ const libs = `${process.cwd()}/libs`;
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const multiQuery = require(`${libs}/middlewares/multiQuery`);
 
diff --git a/src/libs/routes/downloads.js b/src/libs/routes/downloads.js
new file mode 100644
index 0000000000000000000000000000000000000000..7a6c4172b690b08a9382336db00b9c361c3fa3b6
--- /dev/null
+++ b/src/libs/routes/downloads.js
@@ -0,0 +1,30 @@
+const express = require('express');
+
+const downloadApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const Download = require(`${libs}/models/download`);
+
+const User = require(`${libs}/models/user`);
+
+const passport = require('passport');
+
+downloadApp.get('/', passport.authenticate('bearer', {session: false}), (req, res, next) => {
+    Download.find({userId: req.user._id}, (err, downloads) => {
+        if (err) {
+            log.error(err);
+            return next(err);
+        }
+
+        if(!downloads) {
+            res.statusCode = 404;
+            return res.json({msg: 'Nenhum download encontrado'});
+        }
+        res.json(downloads);
+    });
+});
+
+module.exports = downloadApp;
diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js
index 84e47d99518fd10741d9cb78d3cd695eecbf3629..1b9ad6ba46ea8bdff092529bfe504355eeff414a 100644
--- a/src/libs/routes/enrollment.js
+++ b/src/libs/routes/enrollment.js
@@ -8,7 +8,7 @@ const log = require(`${libs}/log`)(module);
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
@@ -18,7 +18,9 @@ const id2str = require(`${libs}/middlewares/id2str`);
 
 const config = require(`${libs}/config`);
 
-const request = require(`request`);
+const passport = require('passport');
+
+const download = require(`${libs}/middlewares/downloadDatabase`);
 
 const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
 
@@ -479,42 +481,6 @@ simRqf.addField({
     }
 });
 
-enrollmentApp.get('/download', (req, res, next) => {
-    // first, query the mapping
-    req.sql.from('mapping_matricula')
-    .field('target_name')
-    .field('name');
-next();
-}, query, (req, res, next) => {
-    req.resetSql();
-    next();
-}, rqf.parse(), rqf.build(), (req, res, next) => {
-    let username = req.query.user;
-    let email = req.query.email;
-
-    req.sql.from('matricula')
-    .field('*');
-    let header = '';
-    req.result.forEach((result) => {
-        if(header === '') header += result.name;
-        else header = header + ';' + result.name;
-    });
-
-    let form = {
-        query: req.sql.toString(),
-        table: req.sql.tableFrom,
-        name: req.sql.tableFrom,
-        username,
-        email,
-        header
-    };
-    request.post(config.cdn.url + '/api/v1/file', {form}, (err, response, body) => {
-        if(err) {
-            log.error(err);
-            return res.json({error: err});
-        }
-        res.json({msg: 'Wait for download email'});
-    });
-});
+enrollmentApp.get('/download', passport.authenticate('bearer', { session: false }), rqf.parse(), rqf.build(), download('matricula', 'mapping_matricula'));
 
 module.exports = enrollmentApp;
diff --git a/src/libs/routes/idhm.js b/src/libs/routes/idhm.js
index 4ac014d7ba6fb8b8e83fa1bccac4af3bdf8471db..278ca24b319c30b0b9a38aead8d09abc5dfbeb5b 100644
--- a/src/libs/routes/idhm.js
+++ b/src/libs/routes/idhm.js
@@ -8,7 +8,7 @@ const log = require(`${libs}/log`)(module);
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
diff --git a/src/libs/routes/idhme.js b/src/libs/routes/idhme.js
index 8b00559e3ace7f434eccae0c3506ab14481844dd..f9ba82b60b8312e492d71fecbba1e2e46b5c8789 100644
--- a/src/libs/routes/idhme.js
+++ b/src/libs/routes/idhme.js
@@ -6,7 +6,7 @@ const libs = `${process.cwd()}/libs`;
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
diff --git a/src/libs/routes/idhml.js b/src/libs/routes/idhml.js
index 3a36276aebde7dc949e8e9c3db5780ba4bebb0ca..d8dd79c324a00ae03176bdcc4465e6493e779bf4 100644
--- a/src/libs/routes/idhml.js
+++ b/src/libs/routes/idhml.js
@@ -6,7 +6,7 @@ const libs = `${process.cwd()}/libs`;
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
diff --git a/src/libs/routes/idhmr.js b/src/libs/routes/idhmr.js
index 6a1d0207faca396a3e2c9ca0bb5374c8917bca78..c2d29659d0aa0f063bce90b683fa197127655b11 100644
--- a/src/libs/routes/idhmr.js
+++ b/src/libs/routes/idhmr.js
@@ -8,7 +8,7 @@ const log = require(`${libs}/log`)(module);
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
diff --git a/src/libs/routes/infrastructure.js b/src/libs/routes/infrastructure.js
index ab9331cb8cb5dda65015b33ec013c32c493c4ad2..2e5ebec3aac4b03d8a8c89d5c6069b7760666a0f 100644
--- a/src/libs/routes/infrastructure.js
+++ b/src/libs/routes/infrastructure.js
@@ -8,7 +8,7 @@ const log = require(`${libs}/log`)(module);
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const multiQuery = require(`${libs}/middlewares/multiQuery`);
 
@@ -24,27 +24,29 @@ const cache = require('apicache').options({ debug: config.debug, statusCodes: {i
 
 let rqf = new ReqQueryFields();
 
-infrastructureApp.get('/year_range', cache('15 day'), (req, res, next) => {
+infrastructureApp.use(cache('15 day'));
+
+infrastructureApp.get('/year_range', (req, res, next) => {
     req.sql.from('escola')
     .field('MIN(escola.ano_censo)', 'start_year')
     .field('MAX(escola.ano_censo)', 'end_year');
     next();
 }, query, response('range'));
 
-infrastructureApp.get('/years', cache('15 day'), (req, res, next) => {
+infrastructureApp.get('/years', (req, res, next) => {
     req.sql.from('escola')
     .field('DISTINCT escola.ano_censo', 'year');
     next();
 }, query, response('years'));
 
-infrastructureApp.get('/source', cache('15 day'), (req, res, next) => {
+infrastructureApp.get('/source', (req, res, next) => {
     req.sql.from('fonte')
     .field('fonte', 'source')
     .where('tabela = \'escola\'');
     next();
 }, query, response('source'));
 
-infrastructureApp.get('/location', cache('15 day'), (req, res, next) => {
+infrastructureApp.get('/location', (req, res, next) => {
     req.result = [
         {id: 1, name: 'Urbana'},
         {id: 2, name: 'Rural'}
@@ -52,7 +54,7 @@ infrastructureApp.get('/location', cache('15 day'), (req, res, next) => {
     next();
 }, response('location'));
 
-infrastructureApp.get('/location_detailed', cache('15 day'), (req, res, next) => {
+infrastructureApp.get('/location_detailed', (req, res, next) => {
     req.result = [
         {id: 1, name: "Urbana"},
         {id: 2, name: "Rural"},
@@ -64,7 +66,7 @@ infrastructureApp.get('/location_detailed', cache('15 day'), (req, res, next) =>
     next();
 }, response('location_detailed'));
 
-infrastructureApp.get('/adm_dependency', cache('15 day'), (req, res, next) => {
+infrastructureApp.get('/adm_dependency', (req, res, next) => {
     req.sql.from('dependencia_adm')
     .field('id')
     .field('nome', 'name')
@@ -72,7 +74,7 @@ infrastructureApp.get('/adm_dependency', cache('15 day'), (req, res, next) => {
     next();
 }, query, response('adm_dependency'));
 
-infrastructureApp.get('/adm_dependency_detailed', cache('15 day'), (req, res, next) => {
+infrastructureApp.get('/adm_dependency_detailed', (req, res, next) => {
     req.sql.from('dependencia_adm_priv')
     .field('id', 'id')
     .field('nome', 'name');
@@ -251,7 +253,7 @@ function matchQueries(queryTotal, queryPartial) {
     return match;
 }
 
-infrastructureApp.get('/', rqf.parse(), rqf.build(), cache('15 day'), (req, res, next) => {
+infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.querySet = [];
     req.queryIndex = {};
 
diff --git a/src/libs/routes/pibpercapita.js b/src/libs/routes/pibpercapita.js
index b503dca1283979fe8d5c3e8d44ce67b3397b145b..c7aaebc1662f2ade26dd2e1e280a4b5a23ec2ab0 100644
--- a/src/libs/routes/pibpercapita.js
+++ b/src/libs/routes/pibpercapita.js
@@ -8,7 +8,7 @@ const log = require(`${libs}/log`)(module);
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
diff --git a/src/libs/routes/population.js b/src/libs/routes/population.js
index c362941be6a1f07c129e64eaa60f104423a714d0..f08991b308147b7747b10ed461ccf45ac2424110 100644
--- a/src/libs/routes/population.js
+++ b/src/libs/routes/population.js
@@ -8,7 +8,7 @@ const log = require(`${libs}/log`)(module);
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
diff --git a/src/libs/routes/region.js b/src/libs/routes/region.js
index fdffe00fb8eb5d6ec406b3b62febf397e7ecb375..b1076e6484836b8a4911b8d0ada3631f1304e3c0 100644
--- a/src/libs/routes/region.js
+++ b/src/libs/routes/region.js
@@ -8,7 +8,7 @@ const log = require(`${libs}/log`)(module);
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
diff --git a/src/libs/routes/school.js b/src/libs/routes/school.js
index 933db24631cb0bd8741c46e049797610dbb980ff..4cbd1314327eb0b12147041cacff73dbfdae306e 100644
--- a/src/libs/routes/school.js
+++ b/src/libs/routes/school.js
@@ -8,7 +8,7 @@ const log = require(`${libs}/log`)(module);
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
@@ -20,6 +20,10 @@ const request = require(`request`);
 
 const config = require(`${libs}/config`);
 
+const passport = require('passport');
+
+const download = require(`${libs}/middlewares/downloadDatabase`);
+
 const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
 
 let rqf = new ReqQueryFields();
@@ -469,42 +473,6 @@ schoolApp.get('/count', cache('15 day'), rqfCount.parse(), rqfCount.build(), (re
     next();
 }, query, id2str.transform(), response('school'));
 
-schoolApp.get('/count/download', (req, res, next) => {
-    // first, query the mapping
-    req.sql.from('mapping_escola')
-        .field('target_name')
-        .field('name');
-    next();
-}, query, (req, res, next) => {
-    req.resetSql();
-    next();
-}, rqfCount.parse(), rqfCount.build(), (req, res, next) => {
-    let username = req.query.user;
-    let email = req.query.email;
-
-    req.sql.from('escola').field('*');
-    let header = '';
-    req.result.forEach((result) => {
-        req.sql.field(result.name, result.target_name);
-        if(header === '') header += result.target_name;
-        else header = header + ';' + result.target_name;
-    });
-
-    let form = {
-        query: req.sql.toString(),
-        table: req.sql.tableFrom,
-        name: req.sql.tableFrom,
-        username,
-        email,
-        header
-    };
-    request.post(config.cdn.url + '/api/v1/file', {form}, (err, response, body) => {
-        if(err) {
-            log.error(err);
-            return res.json({error: err});
-        }
-        res.json({msg: 'Wait for download email'});
-    });
-});
+schoolApp.get('/count/download', passport.authenticate('bearer', { session: false }), rqfCount.parse(), rqfCount.build(), download('escola', 'mapping_escola'));
 
 module.exports = schoolApp;
diff --git a/src/libs/routes/simulation.js b/src/libs/routes/simulation.js
index 5e02c03a09bb71996c2fc279fd9a5563e32f825e..13dcd9e5f9b0d87edf4c9e22a75b975c55b0a2f5 100644
--- a/src/libs/routes/simulation.js
+++ b/src/libs/routes/simulation.js
@@ -8,7 +8,7 @@ const log = require(`${libs}/log`)(module);
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
diff --git a/src/libs/routes/siope.js b/src/libs/routes/siope.js
index 622da904d074ff80b1d328bcada59ad23eb317ec..51c1c36833b99ff097cf8a85a0c90c20598f737b 100644
--- a/src/libs/routes/siope.js
+++ b/src/libs/routes/siope.js
@@ -8,7 +8,7 @@ const log = require(`${libs}/log`)(module);
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const multiQuery = require(`${libs}/middlewares/multiQuery`);
 
diff --git a/src/libs/routes/spatial.js b/src/libs/routes/spatial.js
new file mode 100644
index 0000000000000000000000000000000000000000..844255316dc35da602201aae0bdb1268070309f6
--- /dev/null
+++ b/src/libs/routes/spatial.js
@@ -0,0 +1,355 @@
+const express = require('express');
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const log = require(`${libs}/log`)(module);
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const sqlQuery = require(`${libs}/db/query_exec`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const spatialApp = express();
+
+let rqf = new ReqQueryFields();
+
+function processResultSet(querySet, querySetLabels = ["result"], singleResult = false) {
+    const resultMap = new Map();
+    let resultIdx = 0;
+    // loop relies on the fact that Promise.all maintains the order of the original iterable
+    for(let result of querySet) {
+        const resultLbl = querySetLabels[resultIdx];
+        resultMap[resultLbl] = [];
+        if (singleResult) {
+            resultMap[resultLbl] = result[0];
+        } else {
+            for(let row of result) {
+                log.debug(row);
+                resultMap[resultLbl].push(row);
+            }
+        }
+        resultIdx++;
+    }
+    log.debug(resultMap);
+    return resultMap;
+}
+
+function dbExecAll(querySet = []) {
+    // Issue all queries concurrently to the database, for every query object in the iterable
+    // NOTE: Array.map() returns a copy of the original array with each object 'mapped'.
+    return querySet.map((qry) => { return sqlQuery(qry.toString()); });
+}
+
+rqf.addField({
+    name: 'filter',
+    field: true,
+    where: true
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: 'nome',
+    resultField: 'region_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: 'nome',
+    resultField: 'state_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id'
+    }
+}).addValue({
+    name: 'school',
+    table: 'escola',
+    tableField: 'nome_escola',
+    resultField: 'school_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    }
+});
+
+spatialApp.get('/sociodemographic', rqf.parse(), rqf.build(), (req, res, next) => {
+    const populationYearQry = squel.select()
+        .field('MAX(ibge_populacao.ano_censo)')
+        .from('ibge_populacao');
+
+    const populationQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('SUM(populacao)', 'population')
+        .field('ibge_populacao.ano_censo', 'census_year')
+        .from('ibge_populacao')
+        .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`)
+        .group('ibge_populacao.ano_censo');
+
+    const pibYearQry = squel.select()
+        .field('MAX(ibge_pib.ano_censo)')
+        .from('ibge_pib');
+
+    const pibQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita')
+        .field('ibge_pib.ano_censo', 'census_year')
+        .from('ibge_pib')
+        .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`)
+        .group('ibge_pib.ano_censo');
+
+    const idhYearQry = squel.select()
+        .field('MAX(adh_idh.ano_censo)')
+        .from('adh_idh');
+
+    const idhQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('AVG(idhm)', 'idhm')
+        .field('adh_idh.ano_censo', 'census_year')
+        .from('adh_idh')
+        .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`)
+        .group('adh_idh.ano_censo');
+
+    const analfabYearQry = squel.select()
+        .field('MAX(adh_analfabetismo.ano_censo)')
+        .from('adh_analfabetismo');
+
+    const analfabQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('AVG(t_analf15m)', 'analfabetism')
+        .field('adh_analfabetismo.ano_censo', 'census_year')
+        .from('adh_analfabetismo')
+        .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`)
+        .group('adh_analfabetismo.ano_censo');
+
+    const giniYearQry = squel.select()
+        .field('MAX(adh_gini.ano_censo)')
+        .from('adh_gini');
+
+    const giniQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('AVG(gini)', 'gini')
+        .field('adh_gini.ano_censo', 'census_year')
+        .from('adh_gini')
+        .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`)
+        .group('adh_gini.ano_censo');
+
+    // map query objects to their respective response labels
+    const queryLabels = [ "population", "gdp", "idh", "analfab", "gini" ];
+    const querySet = [ populationQry, pibQry, idhQry, analfabQry, giniQry ];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet)).then((queryResults) => {
+        req.result = processResultSet(queryResults, queryLabels, true);
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('spatial'));
+
+spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
+    const censusYearQry = squel.select()
+        .field('MAX(escola.ano_censo)', 'ano_censo')
+        .from('escola')
+        .toString();
+
+    const totalSchoolsQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('\'Total\'', 'location_name')
+        .field('COUNT(DISTINCT(escola.id))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .from('turma')
+        .from('escola')
+        .where('escola.ano_censo=turma.ano_censo AND escola.id=turma.escola_id')
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .where('turma.tipo_turma_id = 0')
+        .group('escola.ano_censo');
+
+    const schoolsPerLocationQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('COUNT(DISTINCT(escola.id))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .field('localizacao.descricao', 'location_name')
+        .from('localizacao')
+        .from('turma')
+        .from('escola')
+        .where('escola.cod_localizacao=localizacao.id')
+        .where('escola.ano_censo=turma.ano_censo AND escola.id=turma.escola_id')
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .where('turma.tipo_turma_id = 0')
+        .group('escola.cod_localizacao')
+        .group('escola.ano_censo')
+        .group('localizacao.descricao')
+        .order('localizacao.descricao');
+
+    const schoolsPerAdmDependencyQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('COUNT(DISTINCT(escola.id))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .field('dependencia_adm.nome', 'adm_dependency_name')
+        .from('dependencia_adm')
+        .from('escola')
+        .where('escola.dependencia_adm_id=dependencia_adm.id')
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .group('escola.ano_censo')
+        .group('dependencia_adm.nome')
+        .order('escola.ano_censo')
+        .order('dependencia_adm.nome');
+
+    const enrollmentsQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('COALESCE(SUM(uc201.matriculas), 0)', 'total')
+        .field('uc201.ano_censo', 'census_year')
+        .from('uc201')
+        .group('uc201.ano_censo');
+
+    const enrollmentsPerAdmDepQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('COALESCE(SUM(uc201.matriculas), 0)', 'total')
+        .field('uc201.ano_censo', 'census_year')
+        .field('dependencia_adm.nome', 'adm_dependency_name')
+        .from('dependencia_adm')
+        .from('uc201')
+        .where('uc201.dependencia_adm_id=dependencia_adm.id')
+        .group('dependencia_adm.nome')
+        .group('uc201.ano_censo');
+
+    const enrollmentsPerSchoolLevelQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('COALESCE(SUM(uc201.matriculas), 0)', 'total')
+        .field('uc201.ano_censo', 'census_year')
+        .field('etapa_ensino.desc_etapa', 'school_level_name')
+        .field('dependencia_adm.nome', 'adm_dependency_name')
+        .from('etapa_ensino')
+        .from('dependencia_adm')
+        .from('uc201')
+        .where('uc201.etapas_mod_ensino_segmento_id=etapa_ensino.id')
+        .where('uc201.dependencia_adm_id=dependencia_adm.id')
+        .group('etapa_ensino.desc_etapa')
+        .group('dependencia_adm.nome')
+        .group('uc201.ano_censo');
+
+    const enrollmentsPerLocationQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('COALESCE(SUM(uc201.matriculas), 0)', 'total')
+        .field('uc201.ano_censo', 'census_year')
+        .field('localizacao.descricao', 'location_name')
+        .from('localizacao')
+        .from('uc201')
+        .where('uc201.localizacao=localizacao.id')
+        .group('localizacao.descricao')
+        .group('uc201.ano_censo');
+
+    const queryLabels = [ "school", "school_per_location", "school_per_adm_dependency", "enrollment", "enrollment_per_adm_dep",
+        "enrollment_per_school_level", "enrollment_per_location" ];
+    const querySet = [ totalSchoolsQry, schoolsPerLocationQry, schoolsPerAdmDependencyQry, enrollmentsQry,
+        enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry, enrollmentsPerLocationQry];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet)).then((queryResults) => {
+        req.result = processResultSet(queryResults, queryLabels);
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('spatial'));
+
+spatialApp.get('/educational/school_level', rqf.parse(), rqf.build(), (req, res, next) => {
+    const enrollmentsPerSchoolLevelYearQry = squel.select()
+        .field('MAX(matricula.ano_censo)', 'census_year')
+        .from('matricula');
+
+    const enrollmentsPerSchoolLevelQry = req.sql.clone()
+        .field('COALESCE(COUNT(matricula.id), 0)', 'total')
+        .field('matricula.ano_censo', 'census_year')
+        .field('matricula.serie_ano_id', 'school_year')
+        .field('etapa_ensino.desc_etapa', 'school_level')
+        .from('etapa_ensino')
+        .from('matricula')
+        .where(`matricula.ano_censo IN (${enrollmentsPerSchoolLevelYearQry.toString()})`)
+        .where('matricula.etapa_ensino_id = etapa_ensino.id')
+        .where('matricula.tipo <= 3')
+        .group('etapa_ensino.desc_etapa')
+        .group('etapa_ensino.id')
+        .group('matricula.serie_ano_id')
+        .group('matricula.ano_censo')
+        .order('etapa_ensino.id')
+        .order('matricula.serie_ano_id')
+        .order('matricula.ano_censo');
+
+    const queryLabels = [ 'enrollment_per_school_level', 'enrollment_census_year' ];
+    const querySet = [ enrollmentsPerSchoolLevelQry, enrollmentsPerSchoolLevelYearQry ];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet, enrollmentsPerSchoolLevelYearQry)).then((queryResults) => {
+        const result = queryResults[0];
+        const censusYear = queryResults[1][0]['census_year'];
+
+        let school_levels = {};
+        for(let i = 0; i < result.length; ++i) {
+            const school_year  = id2str.schoolYear(result[i].school_year);
+            const school_level = result[i].school_level;
+            const census_year = result[i].census_year;
+            if (typeof school_levels[school_level] === 'undefined') {
+                school_levels[school_level] = {};
+            }
+            school_levels[school_level][school_year] = parseInt(result[i].total, 10);
+        }
+
+        let response = [];
+        for(let level in school_levels) {
+            if (school_levels.hasOwnProperty(level)) {
+                let sclevel = {};
+                sclevel["degree"] = level;
+                sclevel["census_year"] = parseInt(censusYear, 10);
+                sclevel["table"] = [];
+                for(let school_year in school_levels[level]) {
+                    if (school_levels[level].hasOwnProperty(school_year)) {
+                        let enrollment = { 'title' : school_year,
+                                           'value' : school_levels[level][school_year] };
+                        sclevel["table"].push(enrollment);
+                    }
+                }
+                response.push(sclevel);
+            }
+        }
+        req.result = response;
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('spatial'));
+
+module.exports = spatialApp;
diff --git a/src/libs/routes/state.js b/src/libs/routes/state.js
index 67bdfc1bfe86f6976d5af8bf6a31e32ac9dc997b..33778869342216fe40e8e5104f327d4f2de804ef 100644
--- a/src/libs/routes/state.js
+++ b/src/libs/routes/state.js
@@ -6,7 +6,7 @@ const libs = `${process.cwd()}/libs`;
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
diff --git a/src/libs/routes/teacher.js b/src/libs/routes/teacher.js
index 1d357763c3b3be2c55996fd756258fe0e75064b9..5dd885489544239f35d067d1bd0a3aede03bdeeb 100644
--- a/src/libs/routes/teacher.js
+++ b/src/libs/routes/teacher.js
@@ -8,7 +8,7 @@ const log = require(`${libs}/log`)(module);
 
 const squel = require('squel');
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const response = require(`${libs}/middlewares/response`);
 
@@ -18,7 +18,9 @@ const id2str = require(`${libs}/middlewares/id2str`);
 
 const config = require(`${libs}/config`);
 
-const request = require(`request`);
+const passport = require('passport');
+
+const download = require(`${libs}/middlewares/downloadDatabase`);
 
 const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]}  }).middleware;
 
@@ -410,42 +412,6 @@ teacherApp.get('/', rqf.parse(), (req, res, next) => {
     next();
 }, id2str.transform(), response('teacher'));
 
-teacherApp.get('/download', (req, res, next) => {
-    // first, query the mapping
-    req.sql.from('mapping_docente')
-        .field('target_name')
-        .field('name');
-    next();
-}, query, (req, res, next) => {
-    req.resetSql();
-    next();
-}, rqf.parse(), rqf.build(), (req, res, next) => {
-    let username = req.query.user;
-    let email = req.query.email;
-
-    req.sql.from('docente')
-    .field('*');
-    let header = '';
-    req.result.forEach((result) => {
-        if(header === '') header += result.name;
-        else header = header + ';' + result.name;
-    });
-
-    let form = {
-        query: req.sql.toString(),
-        table: req.sql.tableFrom,
-        name: req.sql.tableFrom,
-        username,
-        email,
-        header
-    };
-    request.post(config.cdn.url + '/api/v1/file', {form}, (err, response, body) => {
-        if(err) {
-            log.error(err);
-            return res.json({error: err});
-        }
-        res.json({msg: 'Wait for download email'});
-    });
-});
+teacherApp.get('/download', passport.authenticate('bearer', { session: false }), rqf.parse(), rqf.build(), download('docente', 'mapping_docente'));
 
 module.exports = teacherApp;
diff --git a/src/test/query.js b/src/test/query.js
index 8b6c4541859c20f3ea733046cddc8af81b01d0b3..cad0102ff882c4eb2121cb99c804b3b8b84f0fa4 100644
--- a/src/test/query.js
+++ b/src/test/query.js
@@ -22,7 +22,7 @@ const libs = `${process.cwd()}/libs`;
 
 const server = require(`${libs}/app`);
 
-const query = require(`${libs}/middlewares/query`);
+const query = require(`${libs}/middlewares/query`).query;
 
 const squel = require('squel');