From 47bf370919c85e7275ecd99f63dfe6ab8aab0e5e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Bitard?= <bitard.michael@gmail.com>
Date: Wed, 12 Apr 2023 17:50:16 +0200
Subject: [PATCH] chore(api): utilise pg-typed pour remplacer knex et objection
 (#512)

---
 package-lock.json                             | 468 +++++++++++++++++-
 packages/api/.prettierignore                  |   3 +-
 packages/api/config.json                      |  18 +
 packages/api/package.json                     |  20 +-
 .../src/api/rest/statistiques/dgtm.queries.ts |  21 +
 .../rest/statistiques/dgtm.queries.types.ts   |  19 +
 .../api/src/api/rest/statistiques/dgtm.ts     |  27 +-
 ...es-etapes-areas-update.test.integration.ts |   1 +
 .../processes/titres-etapes-areas-update.ts   |   1 +
 .../titres-etapes-heritage-contenu-update.ts  |   1 +
 ...021121056_delete-titres-administrations.ts |   1 +
 .../20221229082906_migre-sdomzones.ts         |   1 +
 packages/api/src/pg-database.ts               |  10 +
 packages/api/src/tools/geojson.ts             |   1 +
 packages/api/src/tools/territoires-update.ts  |   1 +
 packages/api/tests/db-manager.ts              |   1 +
 16 files changed, 578 insertions(+), 16 deletions(-)
 create mode 100644 packages/api/config.json
 create mode 100644 packages/api/src/api/rest/statistiques/dgtm.queries.ts
 create mode 100644 packages/api/src/api/rest/statistiques/dgtm.queries.types.ts
 create mode 100644 packages/api/src/pg-database.ts

diff --git a/package-lock.json b/package-lock.json
index 300d3a7ee..8ab5c8052 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3686,6 +3686,132 @@
         "@octokit/openapi-types": "^16.0.0"
       }
     },
+    "node_modules/@pgtyped/parser": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@pgtyped/parser/-/parser-2.1.0.tgz",
+      "integrity": "sha512-HIHbQ8iQCPvr4JM/UcXkYlB1zFH2abFoZ9ID8S66pEwm2WUjQlwK1r+/4DRnqXFmwi+qkRPcg4KfAs5iN11eMw==",
+      "dev": true,
+      "dependencies": {
+        "antlr4ts": "0.5.0-alpha.4",
+        "chalk": "^4.1.0",
+        "debug": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=14.16"
+      }
+    },
+    "node_modules/@pgtyped/parser/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@pgtyped/parser/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/@pgtyped/parser/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/@pgtyped/parser/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/@pgtyped/runtime": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@pgtyped/runtime/-/runtime-2.1.0.tgz",
+      "integrity": "sha512-PHND3jE4VnPL23OFqAc6e2Z2V09RKp6RWh2kVfz6KsBIzj7saao2HkAVxzJNED4WNGw66OyXVPdZwOP0NY9CbA==",
+      "dev": true,
+      "dependencies": {
+        "@pgtyped/parser": "^2.1.0",
+        "chalk": "^4.1.0",
+        "debug": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=14.16"
+      }
+    },
+    "node_modules/@pgtyped/runtime/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@pgtyped/runtime/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/@pgtyped/runtime/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/@pgtyped/runtime/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
     "node_modules/@pnpm/config.env-replace": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.0.0.tgz",
@@ -8125,6 +8251,12 @@
       "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==",
       "dev": true
     },
+    "node_modules/antlr4ts": {
+      "version": "0.5.0-alpha.4",
+      "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz",
+      "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==",
+      "dev": true
+    },
     "node_modules/anymatch": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -8472,6 +8604,15 @@
         "node": ">=8"
       }
     },
+    "node_modules/astring": {
+      "version": "1.8.4",
+      "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.4.tgz",
+      "integrity": "sha512-97a+l2LBU3Op3bBQEff79i/E4jMD2ZLFD8rHx9B6mXyB2uQwhJQYfiDqUwtfjF4QA1F2qs//N6Cw8LetMbQjcw==",
+      "dev": true,
+      "bin": {
+        "astring": "bin/astring"
+      }
+    },
     "node_modules/async-each": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz",
@@ -13710,6 +13851,25 @@
         "eslint": "^7.0.0 || ^8.0.0"
       }
     },
+    "node_modules/eslint-plugin-sql": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-sql/-/eslint-plugin-sql-2.3.2.tgz",
+      "integrity": "sha512-cyR9MdqEIkLvBI6DOEQCfAhKcLGkjhzUF+6qYvEesSdxq7IveFQQhgYjH3fSaGAde1U5Q20WjpWdpZ6awYjXzA==",
+      "dev": true,
+      "dependencies": {
+        "astring": "^1.8.3",
+        "debug": "^4.3.4",
+        "lodash": "^4.17.21",
+        "pg-formatter": "^1.3.0",
+        "sql-parse": "^0.1.5"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "peerDependencies": {
+        "eslint": ">=8.1.0"
+      }
+    },
     "node_modules/eslint-plugin-tsdoc": {
       "version": "0.2.17",
       "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz",
@@ -24020,6 +24180,92 @@
       "integrity": "sha512-QfXcpxyN9vT+kvexQpTeNyiwCxs4LPXLAV/C0EeLTJPCF61swhGdDlnHcuDgxJudgKGfQYiCkegg0GsSKSvvEg==",
       "dev": true
     },
+    "node_modules/pg-formatter": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/pg-formatter/-/pg-formatter-1.3.0.tgz",
+      "integrity": "sha512-y1kNdgD+QWzhmYCm91z/k7VGyx6BekQg6ww/krFEEhw1IIB4zEk2xaB0pmueTcc59YFetpiHIKECgHEuw6gyvg==",
+      "dev": true,
+      "dependencies": {
+        "shell-quote": "^1.7.2",
+        "yargs": "^17.2.1"
+      },
+      "bin": {
+        "pg-formatter": "dist/bin/index.js"
+      },
+      "engines": {
+        "node": ">=10.0"
+      }
+    },
+    "node_modules/pg-formatter/node_modules/cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "dev": true,
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/pg-formatter/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
+    },
+    "node_modules/pg-formatter/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pg-formatter/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pg-formatter/node_modules/yargs": {
+      "version": "17.7.1",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
+      "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
+      "dev": true,
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/pg-formatter/node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/pg-int8": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
@@ -27630,6 +27876,15 @@
         "node": ">=8"
       }
     },
+    "node_modules/shell-quote": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
+      "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/shpjs": {
       "version": "4.0.4",
       "resolved": "https://registry.npmjs.org/shpjs/-/shpjs-4.0.4.tgz",
@@ -28181,6 +28436,15 @@
         "node": ">= 6"
       }
     },
+    "node_modules/sql-parse": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/sql-parse/-/sql-parse-0.1.5.tgz",
+      "integrity": "sha512-e2ExBX6iDHoCDC1zN2NvZV49UMhKVLvvwrDjzSVHFS3TKHKtIpl2nMDQkdlbTjDVvf2bxRYBq9iXAAMZvZpGVA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
     "node_modules/ssf": {
       "version": "0.11.2",
       "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
@@ -32910,6 +33174,7 @@
         "xstate": "^4.37.0"
       },
       "devDependencies": {
+        "@pgtyped/runtime": "^2.1.0",
         "@types/carbone": "^3.2.1",
         "@types/cookie-parser": "^1.4.3",
         "@types/stream-json": "^1.7.3",
@@ -32924,6 +33189,7 @@
         "eslint-plugin-import": "^2.27.5",
         "eslint-plugin-n": "^15.6.1",
         "eslint-plugin-promise": "^6.1.1",
+        "eslint-plugin-sql": "^2.3.2",
         "eslint-plugin-tsdoc": "^0.2.1",
         "pg-escape": "^0.2.0",
         "prettier-eslint": "^15.0.1",
@@ -35483,6 +35749,100 @@
         "@octokit/openapi-types": "^16.0.0"
       }
     },
+    "@pgtyped/parser": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@pgtyped/parser/-/parser-2.1.0.tgz",
+      "integrity": "sha512-HIHbQ8iQCPvr4JM/UcXkYlB1zFH2abFoZ9ID8S66pEwm2WUjQlwK1r+/4DRnqXFmwi+qkRPcg4KfAs5iN11eMw==",
+      "dev": true,
+      "requires": {
+        "antlr4ts": "0.5.0-alpha.4",
+        "chalk": "^4.1.0",
+        "debug": "^4.1.1"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        }
+      }
+    },
+    "@pgtyped/runtime": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@pgtyped/runtime/-/runtime-2.1.0.tgz",
+      "integrity": "sha512-PHND3jE4VnPL23OFqAc6e2Z2V09RKp6RWh2kVfz6KsBIzj7saao2HkAVxzJNED4WNGw66OyXVPdZwOP0NY9CbA==",
+      "dev": true,
+      "requires": {
+        "@pgtyped/parser": "^2.1.0",
+        "chalk": "^4.1.0",
+        "debug": "^4.1.1"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        }
+      }
+    },
     "@pnpm/config.env-replace": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.0.0.tgz",
@@ -39011,6 +39371,12 @@
       "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==",
       "dev": true
     },
+    "antlr4ts": {
+      "version": "0.5.0-alpha.4",
+      "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz",
+      "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==",
+      "dev": true
+    },
     "anymatch": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -39284,6 +39650,12 @@
       "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
       "dev": true
     },
+    "astring": {
+      "version": "1.8.4",
+      "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.4.tgz",
+      "integrity": "sha512-97a+l2LBU3Op3bBQEff79i/E4jMD2ZLFD8rHx9B6mXyB2uQwhJQYfiDqUwtfjF4QA1F2qs//N6Cw8LetMbQjcw==",
+      "dev": true
+    },
     "async-each": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz",
@@ -40309,6 +40681,7 @@
       "requires": {
         "@graphql-tools/graphql-file-loader": "^7.5.16",
         "@graphql-tools/load": "^7.8.13",
+        "@pgtyped/runtime": "^2.1.0",
         "@sentry/node": "^7.43.0",
         "@sentry/types": "^7.43.0",
         "@sindresorhus/slugify": "^1.1.2",
@@ -40353,6 +40726,7 @@
         "eslint-plugin-import": "^2.27.5",
         "eslint-plugin-n": "^15.6.1",
         "eslint-plugin-promise": "^6.1.1",
+        "eslint-plugin-sql": "^2.3.2",
         "eslint-plugin-tsdoc": "^0.2.1",
         "express": "^4.18.2",
         "express-graphql": "^0.12.0",
@@ -40365,7 +40739,7 @@
         "graphql-type-json": "^0.3.2",
         "graphql-upload": "^13.0.0",
         "html-to-text": "^9.0.4",
-        "jsondiffpatch": "*",
+        "jsondiffpatch": "^0.4.1",
         "jsonwebtoken": "^9.0.0",
         "jszip": "^3.10.1",
         "knex": "^2.4.2",
@@ -43583,6 +43957,19 @@
       "dev": true,
       "requires": {}
     },
+    "eslint-plugin-sql": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-sql/-/eslint-plugin-sql-2.3.2.tgz",
+      "integrity": "sha512-cyR9MdqEIkLvBI6DOEQCfAhKcLGkjhzUF+6qYvEesSdxq7IveFQQhgYjH3fSaGAde1U5Q20WjpWdpZ6awYjXzA==",
+      "dev": true,
+      "requires": {
+        "astring": "^1.8.3",
+        "debug": "^4.3.4",
+        "lodash": "^4.17.21",
+        "pg-formatter": "^1.3.0",
+        "sql-parse": "^0.1.5"
+      }
+    },
     "eslint-plugin-tsdoc": {
       "version": "0.2.17",
       "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz",
@@ -51161,6 +51548,73 @@
       "integrity": "sha512-QfXcpxyN9vT+kvexQpTeNyiwCxs4LPXLAV/C0EeLTJPCF61swhGdDlnHcuDgxJudgKGfQYiCkegg0GsSKSvvEg==",
       "dev": true
     },
+    "pg-formatter": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/pg-formatter/-/pg-formatter-1.3.0.tgz",
+      "integrity": "sha512-y1kNdgD+QWzhmYCm91z/k7VGyx6BekQg6ww/krFEEhw1IIB4zEk2xaB0pmueTcc59YFetpiHIKECgHEuw6gyvg==",
+      "dev": true,
+      "requires": {
+        "shell-quote": "^1.7.2",
+        "yargs": "^17.2.1"
+      },
+      "dependencies": {
+        "cliui": {
+          "version": "8.0.1",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+          "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+          "dev": true,
+          "requires": {
+            "string-width": "^4.2.0",
+            "strip-ansi": "^6.0.1",
+            "wrap-ansi": "^7.0.0"
+          }
+        },
+        "emoji-regex": {
+          "version": "8.0.0",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "4.2.3",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.1"
+          }
+        },
+        "yargs": {
+          "version": "17.7.1",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
+          "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
+          "dev": true,
+          "requires": {
+            "cliui": "^8.0.1",
+            "escalade": "^3.1.1",
+            "get-caller-file": "^2.0.5",
+            "require-directory": "^2.1.1",
+            "string-width": "^4.2.3",
+            "y18n": "^5.0.5",
+            "yargs-parser": "^21.1.1"
+          }
+        },
+        "yargs-parser": {
+          "version": "21.1.1",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+          "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+          "dev": true
+        }
+      }
+    },
     "pg-int8": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
@@ -53843,6 +54297,12 @@
       "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
       "dev": true
     },
+    "shell-quote": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
+      "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
+      "dev": true
+    },
     "shpjs": {
       "version": "4.0.4",
       "resolved": "https://registry.npmjs.org/shpjs/-/shpjs-4.0.4.tgz",
@@ -54303,6 +54763,12 @@
         }
       }
     },
+    "sql-parse": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/sql-parse/-/sql-parse-0.1.5.tgz",
+      "integrity": "sha512-e2ExBX6iDHoCDC1zN2NvZV49UMhKVLvvwrDjzSVHFS3TKHKtIpl2nMDQkdlbTjDVvf2bxRYBq9iXAAMZvZpGVA==",
+      "dev": true
+    },
     "ssf": {
       "version": "0.11.2",
       "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
diff --git a/packages/api/.prettierignore b/packages/api/.prettierignore
index 2431a55cf..ec601c276 100644
--- a/packages/api/.prettierignore
+++ b/packages/api/.prettierignore
@@ -8,4 +8,5 @@ node_modules
 sources
 CHANGELOG.md
 oct.cas.json
-titre-phases-find.cas.json
\ No newline at end of file
+titre-phases-find.cas.json
+**/*.queries.types.ts
\ No newline at end of file
diff --git a/packages/api/config.json b/packages/api/config.json
new file mode 100644
index 000000000..5fb69e8d0
--- /dev/null
+++ b/packages/api/config.json
@@ -0,0 +1,18 @@
+{
+  "transforms": [
+    {
+      "mode": "ts",
+      "include": "**/*.queries.ts",
+      "emitTemplate": "{{dir}}/{{name}}.types.ts"
+    }
+  ],
+  "srcDir": "./src/",
+  "failOnError": false,
+  "camelCaseColumnNames": false,
+  "db": {
+    "host": "localhost",
+    "user": "postgres",
+    "dbName": "camino",
+    "password": "password"
+  }
+}
diff --git a/packages/api/package.json b/packages/api/package.json
index 5e17482f6..f82c05111 100644
--- a/packages/api/package.json
+++ b/packages/api/package.json
@@ -17,6 +17,7 @@
     "db:recreate": "dropdb --host=localhost --username=postgres camino && createdb --host=localhost --username=postgres camino",
     "db:migrate": "node --loader ts-node/esm/transpile-only ./src/knex/migrate.ts",
     "db:add-migration": "NODE_OPTIONS='--loader ts-node/esm/transpile-only' knex migrate:make",
+    "db:watch": "npx --package=@pgtyped/cli pgtyped -w -c config.json",
     "dev": "node --watch --loader ts-node/esm/transpile-only --inspect ./src/index.ts",
     "daily-debug": "node  --inspect-brk=3000 --loader ts-node/esm/transpile-only ./src/scripts/daily.ts",
     "dev:backups-archive": "tar -zcvf backups/`date +%Y%m%d_%H%M%S`-camino.tar.gz backups/files/* backups/camino.sql",
@@ -99,6 +100,7 @@
     "xstate": "^4.37.0"
   },
   "devDependencies": {
+    "@pgtyped/runtime": "^2.1.0",
     "@types/carbone": "^3.2.1",
     "@types/cookie-parser": "^1.4.3",
     "@types/stream-json": "^1.7.3",
@@ -113,6 +115,7 @@
     "eslint-plugin-import": "^2.27.5",
     "eslint-plugin-n": "^15.6.1",
     "eslint-plugin-promise": "^6.1.1",
+    "eslint-plugin-sql": "^2.3.2",
     "eslint-plugin-tsdoc": "^0.2.1",
     "pg-escape": "^0.2.0",
     "prettier-eslint": "^15.0.1",
@@ -139,9 +142,24 @@
       "node": true
     },
     "plugins": [
-      "@typescript-eslint"
+      "@typescript-eslint",
+      "sql"
     ],
     "rules": {
+      "sql/format": [
+        2,
+        {
+          "ignoreExpressions": false,
+          "ignoreInline": true,
+          "ignoreTagless": true
+        }
+      ],
+      "sql/no-unsafe-query": [
+        2,
+        {
+          "allowLiteral": false
+        }
+      ],
       "newline-before-return": 1,
       "no-use-before-define": 0,
       "no-redeclare": 0,
diff --git a/packages/api/src/api/rest/statistiques/dgtm.queries.ts b/packages/api/src/api/rest/statistiques/dgtm.queries.ts
new file mode 100644
index 000000000..06ab4defc
--- /dev/null
+++ b/packages/api/src/api/rest/statistiques/dgtm.queries.ts
@@ -0,0 +1,21 @@
+import { sql } from '@pgtyped/runtime'
+import { SubstanceFiscaleId } from 'camino-common/src/static/substancesFiscales'
+import { Redefine } from '../../../pg-database.js'
+import { IGetProductionOrQuery } from './dgtm.queries.types.js'
+
+// On peut récupérer le nombre de producteurs d’or que à partir de l’année 2018. L’année à laquelle nous avons commencé à récolter les productions dans Camino
+export const getProductionOr = sql<Redefine<IGetProductionOrQuery, { substance: SubstanceFiscaleId }>>`
+select distinct
+    ta.annee,
+    count(distinct tt.entreprise_id)
+from
+    titres_activites ta
+    left join titres t on ta.titre_id = t.id
+    left join titres_titulaires tt on tt.titre_etape_id = t.props_titre_etapes_ids ->> 'titulaires'
+where
+    ta.type_id in ('gra', 'grx')
+    and (ta.contenu -> 'substancesFiscales' -> $ substance)::int > 0
+    and ta.annee > 2017
+group by
+    ta.annee
+`
diff --git a/packages/api/src/api/rest/statistiques/dgtm.queries.types.ts b/packages/api/src/api/rest/statistiques/dgtm.queries.types.ts
new file mode 100644
index 000000000..4715e1fc1
--- /dev/null
+++ b/packages/api/src/api/rest/statistiques/dgtm.queries.types.ts
@@ -0,0 +1,19 @@
+/** Types generated for queries found in "src/api/rest/statistiques/dgtm.queries.ts" */
+
+/** 'GetProductionOr' parameters type */
+export interface IGetProductionOrParams {
+  substance?: string | null | void;
+}
+
+/** 'GetProductionOr' return type */
+export interface IGetProductionOrResult {
+  annee: number;
+  count: string | null;
+}
+
+/** 'GetProductionOr' query type */
+export interface IGetProductionOrQuery {
+  params: IGetProductionOrParams;
+  result: IGetProductionOrResult;
+}
+
diff --git a/packages/api/src/api/rest/statistiques/dgtm.ts b/packages/api/src/api/rest/statistiques/dgtm.ts
index 99dc1e928..bf9a5bb38 100644
--- a/packages/api/src/api/rest/statistiques/dgtm.ts
+++ b/packages/api/src/api/rest/statistiques/dgtm.ts
@@ -2,12 +2,13 @@ import { AdministrationId } from 'camino-common/src/static/administrations.js'
 import { TitreTypeId } from 'camino-common/src/static/titresTypes.js'
 import { getTitreTypeIdsByAdministration } from 'camino-common/src/static/administrationsTitresTypes.js'
 import { StatistiquesDGTM, TitreTypeIdDelai, titreTypeIdDelais } from 'camino-common/src/statistiques.js'
-import { CaminoAnnee, CaminoDate, getAnnee, daysBetween } from 'camino-common/src/date.js'
+import { CaminoAnnee, CaminoDate, getAnnee, daysBetween, toCaminoAnnee } from 'camino-common/src/date.js'
 import { knex } from '../../../knex.js'
 import { SDOMZoneId, SDOMZoneIds } from 'camino-common/src/static/sdom.js'
 import { ETAPES_TYPES, EtapeTypeId } from 'camino-common/src/static/etapesTypes.js'
-import { SUBSTANCES_FISCALES_IDS } from 'camino-common/src/static/substancesFiscales.js'
 import { EtapeStatutId } from 'camino-common/src/static/etapesStatuts.js'
+import { getProductionOr } from './dgtm.queries.js'
+import { pool } from '../../../pg-database.js'
 
 const anneeDepartStats = 2015
 
@@ -215,18 +216,18 @@ export const getDGTMStatsInside = async (administrationId: AdministrationId): Pr
     }
   })
 
-  // On peut récupérer le nombre de producteurs d’or que à partir de l’année 2018. L’année à laquelle nous avons commencé à récolter les productions dans Camino
-  const producteursOr: { rows: { annee: CaminoAnnee; count: string }[] } = await knex.raw(`select distinct ta.annee, count ( distinct tt.entreprise_id)
-  from titres_activites ta
-      left join titres t on ta.titre_id = t.id
-      left join titres_titulaires tt  on tt.titre_etape_id = t.props_titre_etapes_ids->>'titulaires'
-  where ta.type_id in ('gra', 'grx') and (ta.contenu -> 'substancesFiscales' -> '${SUBSTANCES_FISCALES_IDS.or}')::int > 0
-  and ta.annee > 2017
-  group by ta.annee;`)
+  const producteursOr = await getProductionOr.run(
+    {
+      substance: 'auru',
+    },
+    pool
+  )
 
-  if (producteursOr && producteursOr.rows?.length) {
-    result.producteursOr = producteursOr.rows.reduce<Record<CaminoAnnee, number>>((acc, r) => {
-      acc[r.annee] = parseInt(r.count, 10)
+  if (producteursOr && producteursOr?.length) {
+    result.producteursOr = producteursOr.reduce<Record<CaminoAnnee, number>>((acc, r) => {
+      if (r.annee && r.count) {
+        acc[toCaminoAnnee(r.annee)] = parseInt(r.count, 10)
+      }
 
       return acc
     }, {})
diff --git a/packages/api/src/business/processes/titres-etapes-areas-update.test.integration.ts b/packages/api/src/business/processes/titres-etapes-areas-update.test.integration.ts
index ec2012a3c..151311696 100644
--- a/packages/api/src/business/processes/titres-etapes-areas-update.test.integration.ts
+++ b/packages/api/src/business/processes/titres-etapes-areas-update.test.integration.ts
@@ -1,3 +1,4 @@
+/* eslint-disable sql/no-unsafe-query */
 import { dbManager } from '../../../tests/db-manager.js'
 import { Knex } from 'knex'
 import Titres from '../../database/models/titres.js'
diff --git a/packages/api/src/business/processes/titres-etapes-areas-update.ts b/packages/api/src/business/processes/titres-etapes-areas-update.ts
index 7f2845f89..c7bcc5769 100644
--- a/packages/api/src/business/processes/titres-etapes-areas-update.ts
+++ b/packages/api/src/business/processes/titres-etapes-areas-update.ts
@@ -1,3 +1,4 @@
+/* eslint-disable sql/no-unsafe-query */
 import { geojsonFeatureMultiPolygon, geojsonIntersectsCommunes, geojsonIntersectsSecteursMaritime, geojsonIntersectsForets, geojsonIntersectsSDOM } from '../../tools/geojson.js'
 import { titresEtapesGet } from '../../database/queries/titres-etapes.js'
 import TitresCommunes from '../../database/models/titres-communes.js'
diff --git a/packages/api/src/business/processes/titres-etapes-heritage-contenu-update.ts b/packages/api/src/business/processes/titres-etapes-heritage-contenu-update.ts
index 2a13ac06a..96dbc8393 100644
--- a/packages/api/src/business/processes/titres-etapes-heritage-contenu-update.ts
+++ b/packages/api/src/business/processes/titres-etapes-heritage-contenu-update.ts
@@ -1,3 +1,4 @@
+/* eslint-disable sql/no-unsafe-query */
 import { DemarcheId, IContenu, IHeritageContenu, ITitreEtape } from '../../types.js'
 
 import { titreEtapeUpdate } from '../../database/queries/titres-etapes.js'
diff --git a/packages/api/src/knex/migrations-schema/20221021121056_delete-titres-administrations.ts b/packages/api/src/knex/migrations-schema/20221021121056_delete-titres-administrations.ts
index c9dfa4767..343964d27 100644
--- a/packages/api/src/knex/migrations-schema/20221021121056_delete-titres-administrations.ts
+++ b/packages/api/src/knex/migrations-schema/20221021121056_delete-titres-administrations.ts
@@ -1,3 +1,4 @@
+/* eslint-disable sql/no-unsafe-query */
 import { Knex } from 'knex'
 export const up = async (knex: Knex) => {
   await knex.schema.alterTable('titres_etapes', function (table) {
diff --git a/packages/api/src/knex/migrations-schema/20221229082906_migre-sdomzones.ts b/packages/api/src/knex/migrations-schema/20221229082906_migre-sdomzones.ts
index 5546587a4..c6a64f7dc 100644
--- a/packages/api/src/knex/migrations-schema/20221229082906_migre-sdomzones.ts
+++ b/packages/api/src/knex/migrations-schema/20221229082906_migre-sdomzones.ts
@@ -1,3 +1,4 @@
+/* eslint-disable sql/no-unsafe-query */
 import { Knex } from 'knex'
 export const up = async (knex: Knex) => {
   await knex.schema.alterTable('titres_etapes', function (table) {
diff --git a/packages/api/src/pg-database.ts b/packages/api/src/pg-database.ts
new file mode 100644
index 000000000..b61d900c8
--- /dev/null
+++ b/packages/api/src/pg-database.ts
@@ -0,0 +1,10 @@
+import pg from 'pg'
+
+export const pool = new pg.Pool({
+  host: process.env.PGHOST,
+  user: process.env.PGUSER,
+  password: process.env.PGPASSWORD,
+  database: process.env.PGDATABASE,
+})
+
+export type Redefine<T, V> = T extends { params: infer A } ? (keyof A extends keyof V ? Omit<T, 'params'> & { params: V } : false) : false
diff --git a/packages/api/src/tools/geojson.ts b/packages/api/src/tools/geojson.ts
index d4183d856..3a69fff50 100644
--- a/packages/api/src/tools/geojson.ts
+++ b/packages/api/src/tools/geojson.ts
@@ -1,3 +1,4 @@
+/* eslint-disable sql/no-unsafe-query */
 import rewind from 'geojson-rewind'
 import center from '@turf/center'
 
diff --git a/packages/api/src/tools/territoires-update.ts b/packages/api/src/tools/territoires-update.ts
index c222a73a0..bea36001c 100644
--- a/packages/api/src/tools/territoires-update.ts
+++ b/packages/api/src/tools/territoires-update.ts
@@ -1,3 +1,4 @@
+/* eslint-disable sql/no-unsafe-query */
 import '../init.js'
 import { knex } from '../knex.js'
 import fetch from 'node-fetch'
diff --git a/packages/api/tests/db-manager.ts b/packages/api/tests/db-manager.ts
index 5780ecbc3..5f2c4214e 100644
--- a/packages/api/tests/db-manager.ts
+++ b/packages/api/tests/db-manager.ts
@@ -1,3 +1,4 @@
+/* eslint-disable sql/no-unsafe-query */
 import { join } from 'path'
 
 import { idGenerate } from '../src/database/models/_format/id-create.js'
-- 
GitLab