Skip to main content
sqlx est une boîte à outils SQL asynchrone, écrite entièrement en Rust, qui intègre un pool de connexions et un système de migrations. Dans ce guide, vous allez créer une table todos à l’aide d’une migration sqlx-cli, puis traiter des requêtes de création, de lecture, de mise à jour et de suppression depuis un petit serveur axum. Toutes les connexions utilisent TLS avec une vérification complète des certificats.

Prérequis

  • Rust 1.94 ou version ultérieure. Ce guide a été testé avec Rust 1.98.1, sqlx 0.9.0, sqlx-cli 0.9.0 et axum 0.8.9.
  • Un compte ClickHouse Cloud
  • psql, pour créer la base de données. Vous pouvez aussi exécuter le statement CREATE DATABASE dans la console SQL.

Créer un service ClickHouse Managed Postgres

Dans la console ClickHouse Cloud, cliquez sur New service et sélectionnez Postgres. L’instance est prête en quelques minutes. Pour une présentation pas à pas, consultez le guide de démarrage rapide.

Obtenir vos informations de connexion

Cliquez sur Connect dans la barre latérale gauche de votre service. Laissez Directly sélectionné et activez Use SSL. L’URL de connexion affichée dans l’onglet url se termine désormais par sslmode=verify-full&sslrootcert=<service-name>-ca-certificate.pem, et le port est 5432. Cliquez sur Download CA certificate. Vous pourrez également le télécharger plus tard depuis Settings → CA Certificate. Ce certificat étant propre à votre instance, le driver peut s’en servir pour vérifier qu’il communique bien avec votre serveur. Dans ce guide, l’application comme sqlx-cli se connectent directement à Postgres sur le port 5432. Un serveur axum est un processus de longue durée, et PgPool gère son propre pool de connexions : un second pooler devant Postgres est donc inutile. Les migrations doivent toujours passer par la connexion directe. Si vous exécutez de nombreuses instances de l’application, consultez Utiliser PgBouncer.

Configurer le projet

Créez un projet et ajoutez sqlx avec le driver Postgres, le runtime Tokio et le backend TLS rustls, ainsi qu’axum et quelques crates utilitaires :
Installez sqlx-cli avec le même backend TLS :
Utilisez rustls plutôt que native-tls sur macOSClickHouse Managed Postgres n’accepte que TLS 1.3. Sur macOS, le backend native-tls s’appuie sur la bibliothèque système Secure Transport, qui ne prend pas en charge TLS 1.3. Une simple commande cargo install sqlx-cli utilise native-tls, et toutes les commandes échouent ensuite avec l’erreur error occurred while attempting to establish a TLS connection: One or more parameters passed to a function were not valid. (ou bad protocol version). Il en va de même pour une application compilée avec la fonctionnalité tls-native-tls. Sous Linux, native-tls utilise OpenSSL et parvient à se connecter, mais ne charge que le premier certificat du fichier ca-certificate.pem. rustls prend en charge TLS 1.3 sur toutes les plateformes et charge tous les certificats du fichier.
Vous partez d’une application existante ?Ignorez cargo new. Dans votre projet, exécutez les commandes cargo add et cargo install ci-dessus, puis passez directement à Configurer la connexion. Si l’application utilise sqlx avec SQLite :
  • Supprimez sqlite des fonctionnalités sqlx dans Cargo.toml, puis remplacez SqlitePool par PgPool.
  • Remplacez les caractères de substitution ? par $1, $2, etc.
  • Réécrivez vos migrations en syntaxe Postgres, par exemple bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY au lieu de INTEGER PRIMARY KEY AUTOINCREMENT.

Configurer la connexion

Déplacez le certificat de CA que vous avez téléchargé dans le répertoire du projet, puis renommez-le en ca-certificate.pem. Créez une base de données pour l’application. Remplacez <PASSWORD> et l’hôte par les valeurs affichées dans la fenêtre modale Connect :
Créez un fichier .env à la racine du projet, ou modifiez DATABASE_URL dans votre fichier existant, pour qu’elle pointe vers la base de données guide_rust. L’application et sqlx-cli y lisent tous deux la valeur de DATABASE_URL :
.env
sqlx lit sslmode et sslrootcert dans l’URL. Avec verify-full, il vérifie que le certificat du serveur est signé par la CA de votre instance et qu’il correspond au nom d’hôte. Le path sslrootcert est résolu par rapport au répertoire depuis lequel vous exécutez les commandes. Excluez .env du contrôle de version et, si votre mot de passe contient des caractères tels que @, / ou #, encodez-les en pourcentage.
Définissez toujours sslmode=verify-fullSans sslmode, sqlx utilise prefer : il chiffre la connexion, mais accepte n’importe quel certificat, même lorsque sslrootcert est défini. Avec verify-full, un certificat de CA manquant ou incorrect provoque l’erreur UnknownIssuer, et un hôte qui ne correspond pas au certificat (une adresse IP, par exemple) provoque l’erreur NotValidForNameContext (certificate not valid for name dans sqlx-cli). Un path sslrootcert incorrect provoque l’erreur No such file or directory.

Créer la table à l’aide d’une migration

Créez un fichier de migration :
Ouvrez le nouveau fichier dans le répertoire migrations et ajoutez-y la définition de la table :
migrations/<timestamp>_create_todos.sql
Appliquez-la :
sqlx migrate run enregistre les migrations appliquées dans la table _sqlx_migrations et n’exécute que les nouvelles lors des appels suivants. Versionnez le répertoire migrations avec votre code. Pour appliquer plutôt les migrations au démarrage de l’application, appelez sqlx::migrate!().run(&pool).await? dans main, à condition que le pool utilise la connexion directe.

Créer l’API

Remplacez src/main.rs par un petit serveur axum qui exécute une requête par route. Dans une application existante, créez le pool de la même manière que dans main, puis ajoutez la struct Todo, les handlers et les routes à votre propre routeur :
src/main.rs
Les valeurs sont toujours transmises via .bind sous forme de paramètres ($1), et ne sont jamais interpolées dans la string SQL. Chaque string SQL distincte n’est préparée qu’une seule fois par connexion, puis conservée dans un cache pouvant contenir jusqu’à 100 statements ; sqlx ferme ces derniers lorsqu’il les évince du cache.
Requêtes vérifiées à la compilationCe guide utilise la fonction query_as, qui vérifie le SQL à l’exécution ; cargo build n’a donc pas besoin d’une base de données. Les macros query! et query_as!, à l’inverse, vérifient le SQL et les types de colonnes auprès de la base de données dès la compilation. Elles lisent DATABASE_URL depuis .env pendant le build : veillez donc à ce que cette variable pointe vers la connexion directe. Pour compiler sans base de données, par exemple en intégration continue, exécutez cargo sqlx prepare, committez le répertoire .sqlx généré et définissez SQLX_OFFLINE=true.

Exécuter et vérifier

Démarrez le serveur :
La première ligne provient de pg_stat_ssl et confirme que la session est chiffrée. Dans un second terminal, créez deux tâches, marquez la première comme terminée, puis supprimez la seconde :
Chaque requête renvoie la ligne concernée. L’option -w '\n' se contente d’ajouter un saut de ligne après chaque réponse :
Ouvrez http://localhost:3117/todos dans votre navigateur pour afficher la liste des tâches restantes :
Pour afficher les données dans la console, ouvrez la console SQL dans la barre latérale gauche de votre service, développez guide_rust puis public, puis cliquez sur la table todos. La table _sqlx_migrations, juste à côté, contient l’historique des migrations. La table todos contient la ligne restante :

Utiliser PgBouncer

Pour connecter l’application via le PgBouncer intégré, sélectionnez via PgBouncer dans la fenêtre modale Connect et utilisez le port 6432 dans la variable DATABASE_URL de l’application. Le même certificat de CA reste valable. Par exemple, conservez la connexion directe dans .env pour sqlx-cli et définissez la variable au démarrage de l’application :
Via PgBouncer, pg_stat_ssl indique la propre connexion de PgBouncer à Postgres, si bien que la vérification affiche none. La connexion de votre application à PgBouncer est néanmoins bien vérifiée : un certificat de CA incorrect provoque le même échec avec UnknownIssuer. PgBouncer fonctionne en mode pool de connexions par transaction : chaque transaction peut donc s’exécuter sur une connexion Postgres différente. Avec sqlx :
  • La connexion nécessite .extra_float_digits(None), comme dans main.rs. Par défaut, sqlx envoie extra_float_digits comme paramètre de démarrage, et PgBouncer rejette la connexion avec unsupported startup parameter: extra_float_digits. Pour la même raison, PgConnectOptions::options échoue avec unsupported startup parameter in options.
  • Les requêtes préparées fonctionnent avec les paramètres par défaut. sqlx libère les statements qu’il évince de son cache au moyen d’un message Close au niveau du protocole, que PgBouncer prend en charge, y compris au sein des transactions. Ne définissez pas statement_cache_capacity(0) : c’est inutile et, lorsque le cache est désactivé, sqlx prépare toujours des statements nommés mais ne les ferme jamais.
  • N’utilisez pas .persistent(false) sur des requêtes exécutées en dehors d’une transaction. sqlx prépare alors un statement sans nom et l’exécute lors d’un second aller-retour, que PgBouncer peut acheminer vers une autre connexion Postgres. La requête peut alors échouer avec unnamed prepared statement does not exist, ou exécuter silencieusement le statement d’un autre client et renvoyer des résultats erronés.
  • Les paramètres de session définis avec SET, y compris dans le hook after_connect d’un pool, ne restent pas attachés à votre connexion et peuvent se propager à d’autres clients. Utilisez plutôt SET LOCAL au sein d’une transaction.
  • Les migrations doivent passer par la connexion directe. sqlx-cli et les macros query! ne peuvent pas se connecter via PgBouncer, car ils envoient systématiquement extra_float_digits. sqlx::migrate! acquiert un verrou consultatif au niveau de la session (pg_advisory_lock) et, via PgBouncer, le déverrouillage peut aboutir sur une autre connexion Postgres. Le verrou reste alors détenu sur une connexion du pool, et les migrations suivantes, y compris via la connexion directe, restent bloquées jusqu’à ce que PgBouncer ferme cette connexion.

Étapes suivantes

  • Connexion : chaînes de connexion, PgBouncer et TLS
  • Paramètres : paramètres de Postgres et de PgBouncer, comme max_connections
  • Répliques en lecture : acheminer les requêtes en lecture seule vers une réplique à l’aide d’un second PgPool
  • Sécurité : listes d’accès IP et réseau privé
Dernière modification le 14 octobre 2026