Quatre langages, un seul contrat : s'authentifier, lire avec des filtres, paginer, gérer les erreurs ; et la même table en SQL avec la clé utilisateur. Les exemples ci-dessous sont générés depuis le vrai catalogue : choisissez un dataset et une table.
Lire /anc/anc par l'API clé API requise sur prod
import os
import requests
DATA_API_URL = os.environ.get("DATA_API_URL", "https://data-prod.flturbo.dev") # dev / staging / prod : seule l'URL change
headers = {"Authorization": f"Bearer {os.environ['DATA_API_TOKEN']}"}
def lire_anc(filtres: dict | None = None, limit: int = 1000) -> list[dict]:
"""Lit anc/anc page par page. Filtres PostgREST : {"numero": "eq.VALEUR"}."""
rows, offset = [], 0
while True:
params = {"select": "numero,article__ref,date_ouverture,atelier", "order": "numero", "limit": limit, "offset": offset, **(filtres or {})}
r = requests.get(f"{DATA_API_URL}/anc/anc", params=params, headers=headers, timeout=30)
r.raise_for_status() # 401 : jeton absent/expiré, 403 : pas lecteur de ce dataset
page = r.json()
rows.extend(page)
if len(page) < limit:
return rows
offset += limit
lignes = lire_anc({"numero": "eq.VALEUR"})
print(len(lignes), "lignes")
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
// dotnet add package System.Net.Http.Json
public static class DataPlateforme
{
static readonly string BaseUrl = Environment.GetEnvironmentVariable("DATA_API_URL") ?? "https://data-prod.flturbo.dev";
public static async Task<List<JsonElement>> LireAncAsync(string filtre = "numero=eq.VALEUR", int limit = 1000)
{
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("DATA_API_TOKEN"));
var rows = new List<JsonElement>();
for (var offset = 0; ; offset += limit)
{
var url = $"{BaseUrl}/anc/anc?select=numero,article__ref,date_ouverture,atelier&order=numero&limit={limit}&offset={offset}&{filtre}";
using var resp = await http.GetAsync(url);
resp.EnsureSuccessStatusCode(); // 401 : jeton absent/expiré, 403 : pas lecteur de ce dataset
var page = await resp.Content.ReadFromJsonAsync<List<JsonElement>>() ?? new();
rows.AddRange(page);
if (page.Count < limit) return rows;
}
}
}
let
// Jeton stocké dans un paramètre de classeur ou de rapport, jamais en dur dans une requête partagée
Jeton = Excel.CurrentWorkbook(){[Name="DATA_API_TOKEN"]}[Content]{0}[Column1],
Base = "https://data-prod.flturbo.dev",
// Pagination : PostgREST renvoie au plus "limit" lignes par appel
Page = (offset as number) =>
Json.Document(Web.Contents(Base, [
RelativePath = "anc/anc",
Query = [ select = "numero,article__ref,date_ouverture,atelier", order = "numero", limit = "1000", offset = Text.From(offset), numero = "eq.VALEUR" ], Headers = [ Authorization = "Bearer " & Jeton ]
])),
Pages = List.Generate(() => [i = 0, p = Page(0)], each List.Count([p]) > 0, each [i = [i] + 1000, p = Page([i] + 1000)], each [p]),
Lignes = List.Combine(Pages),
Table = Table.FromRecords(Lignes)
in
Table
# 20 premières lignes, colonnes choisies, filtre sur numero
curl "https://data-prod.flturbo.dev/anc/anc?select=numero,article__ref,date_ouverture,atelier&numero=eq.VALEUR&order=numero&limit=20" \
-H "Authorization: Bearer $DATA_API_TOKEN"
# total de lignes sans les lire
curl -I "https://data-prod.flturbo.dev/anc/anc?select=numero" -H "Prefer: count=exact" \
-H "Authorization: Bearer $DATA_API_TOKEN"
# export CSV
curl "https://data-prod.flturbo.dev/anc/anc?select=numero,article__ref,date_ouverture,atelier" -H "Accept: text/csv" \
-H "Authorization: Bearer $DATA_API_TOKEN" -o anc.csv
La même table en SQL (clé utilisateur)
import os
import psycopg # pip install "psycopg[binary]"
# Clé utilisateur : identifiant u_<login>, mot de passe remis une fois (portail > mes accès)
conn = psycopg.connect(host="sql.data.flturbo.dev", port=5432, dbname="data_prod", user="u_<login>", password=os.environ["DATA_SQL_PASSWORD"], sslmode="require")
with conn.cursor() as cur:
cur.execute("SELECT * FROM anc.anc LIMIT 20")
for row in cur:
print(row)
using Npgsql; // dotnet add package Npgsql
var cs = $"Host=sql.data.flturbo.dev;Port=5432;Database=data_prod;Username=u_<login>;Password={Environment.GetEnvironmentVariable("DATA_SQL_PASSWORD")};SSL Mode=Require;Trust Server Certificate=true";
await using var conn = new NpgsqlConnection(cs);
await conn.OpenAsync();
await using var cmd = new NpgsqlCommand("SELECT * FROM anc.anc LIMIT 20", conn);
await using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) Console.WriteLine(reader[0]);
let
// Excel / Power BI : Données > Obtenir des données > Base de données PostgreSQL (connecteur natif)
// Serveur : sql.data.flturbo.dev:5432 Base : data_prod Identifiants : u_<login> / mot de passe (clé utilisateur)
Source = PostgreSQL.Database("sql.data.flturbo.dev:5432", "data_prod"),
anc = Source{[Schema="anc", Item="anc"]}[Data]
in
anc
psql "postgresql://u_<login>@sql.data.flturbo.dev:5432/data_prod?sslmode=require" -c "SELECT * FROM anc.anc LIMIT 20"
Règles d'usage
Une URL de base par environnement, lue dans DATA_API_URL ; votre CI la renseigne (branche de revue → dev, main → staging ou prod). Jamais d'URL de prod en dur dans le code.
La clé API dans une variable (DATA_API_TOKEN), jamais dans le code ni dans un classeur partagé. Pour une application, utilisez l'accès de l'application, pas le vôtre.
Paginer : PostgREST renvoie au plus limit lignes ; les exemples bouclent avec offset. Pour un total sans lire : Prefer: count=exact et l'en-tête Content-Range.
Ne pas copier un référentiel dans une base applicative : lisez l'API (ou le SQL) à la demande, mettez en cache si besoin, avec une durée inférieure au rythme de rafraîchissement du dataset.
Codes d'erreur : 401 jeton absent ou expiré (renouvelez-le dans Accès), 403 vous n'êtes pas lecteur de ce dataset (demandez au responsable), 404 dataset ou table inconnu sur cet environnement, 416 pagination hors limites.
Formats : JSON par défaut ; Accept: text/csv pour un export ; Accept-Profile est posé automatiquement par l'URL, ne l'envoyez pas.
Changements de schéma : une colonne ajoutée n'est pas une rupture ; nommez les colonnes que vous lisez (select=) plutôt que de dépendre de l'ordre. Un changement incompatible est refusé par le pipeline, il n'arrive pas par surprise.
SQL : connexion TLS obligatoire (sslmode=require), lecture seule sur les datasets, écriture dans votre schéma work_<login> uniquement. Pas de SELECT * sur des tables volumineuses dans un outil interactif sans LIMIT.
Agents de code : donnez-leur https://data.flturbo.dev/llms.txt (toutes les tables, colonnes et URL en texte plat) plutôt que de les laisser deviner un schéma.