Skip to main content
Phoenix est un framework web Elixir qui s’appuie sur Ecto et le driver Postgrex pour communiquer avec PostgreSQL. Dans ce guide, vous allez créer une application Phoenix, la connecter à ClickHouse Managed Postgres via une connexion TLS vérifiée, exécuter une migration Ecto et exposer une ressource JSON todos.

Prérequis

  • Elixir 1.15 ou version ultérieure et Erlang/OTP. Ce guide a été testé avec Elixir 1.20.4, Erlang/OTP 29, Phoenix 1.8.15, ecto_sql 3.14.0 et Postgrex 0.22.4.
  • Un compte ClickHouse Cloud

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

Ouvrez votre service et cliquez sur Connect dans la barre latérale gauche. La fenêtre modale affiche votre nom d’utilisateur, votre mot de passe, le serveur et le port, ainsi qu’un sélecteur Directly / via PgBouncer. Laissez Directly sélectionné et activez Use SSL. Copiez le mot de passe et le nom d’hôte du serveur, puis cliquez sur Download CA certificate. Le certificat de CA est propre à votre service. Vous pouvez également le télécharger plus tard depuis Settings → CA Certificate. Connexion directe ou PgBouncer ? Une application Phoenix est un processus de longue durée, et Ecto gère son propre pool de connexions (pool_size, dix par défaut). Dans ce guide, la connexion à Postgres se fait directement sur le port 5432. Si le nombre cumulé de connexions de vos instances de l’application approche de la limite de Postgres, consultez Utiliser PgBouncer.

Créer une application Phoenix

Installez le générateur de projets Phoenix, puis créez une application. Postgres est la base de données par défaut :
Vous partez d’une application existante ?Ignorez l’étape mix phx.new. Si votre application utilise SQLite, la base de données par défaut de mix phx.new --database sqlite3, passez d’abord à Postgres :
  • Dans mix.exs, remplacez {:ecto_sqlite3, ">= 0.0.0"} par {:postgrex, ">= 0.0.0"}, puis exécutez mix deps.get.
  • Dans lib/<your_app>/repo.ex, remplacez adapter: Ecto.Adapters.SQLite3 par adapter: Ecto.Adapters.Postgres.
Passez ensuite à la section Configurer la connexion à la base de données. mix ecto.migrate crée vos tables existantes dans Postgres, mais ne copie pas les données de SQLite. Mettez à jour config/test.exs avant d’exécuter mix test.

Configurer la connexion à la base de données

Déplacez le certificat de CA que vous avez téléchargé dans le répertoire priv de l’application, puis renommez-le en ca-certificate.pem. Le nom du fichier téléchargé correspond à celui de votre service, par exemple your-service-ca-certificate.pem :
Le certificat de CA n’est pas un secret : vous pouvez donc le commiter avec votre application. Les fichiers du répertoire priv sont également inclus dans les versions. Dans config/dev.exs, remplacez les lignes username, password, hostname et database de la configuration Hello.Repo par url et ssl :
config/dev.exs
Lorsque ssl est une liste de mots-clés, Postgrex active la vérification des certificats (verify: :verify_peer) ainsi que celle du nom d’hôte, et transmet le nom d’hôte de l’URL comme nom de serveur TLS. Il vous suffit de fournir le certificat de CA. ssl: true ne fonctionne pas, car cette option vérifie le certificat du serveur auprès du magasin de CA de votre système d’exploitation, qui ne contient pas la CA de votre service. Définissez DATABASE_URL pour qu’elle pointe vers votre service, avec un nouveau nom de base de données. Ce guide utilise guide_phoenix :
N’ajoutez pas de query string à DATABASE_URL. Ecto ignore les paramètres sslmode et sslrootcert présents dans l’URL fournie par la fenêtre modale Connect. Sans le paramètre ssl, Postgrex n’utilise pas TLS et ne vérifie pas le serveur.
Créez la base de données :
mix ecto.create se connecte à la base de données postgres par défaut avec les mêmes paramètres TLS pour exécuter CREATE DATABASE.

Générer une ressource JSON et exécuter la migration

Générez un contexte, un schéma, une migration et un contrôleur JSON pour les todos :
Dans lib/hello_web/router.ex, décommentez le scope /api et ajoutez-y la ressource :
lib/hello_web/router.ex
Appliquez la migration. Dans une application existante, cela crée également les autres tables de votre application dans la nouvelle base de données :
Ecto enregistre les migrations appliquées dans la table schema_migrations et n’exécute que les nouvelles lors des exécutions suivantes.

Exécuter et vérifier

Vérifiez que la connexion est chiffrée. La requête interroge pg_stat_ssl pour la connexion courante :
Démarrez le serveur sur le port 3116 :
Dans un deuxième terminal, créez deux tâches, marquez la première comme terminée, puis supprimez la seconde :
La requête PATCH renvoie la tâche mise à jour :
Ouvrez http://localhost:3116/api/todos dans votre navigateur pour afficher la liste des tâches restantes :
Pour afficher les données dans la console, ouvrez console SQL dans la barre latérale gauche de votre service, développez guide_phoenix puis public, et cliquez sur la table todos. Le schéma public contient également schema_migrations :

Configurer l’environnement de production

En production, Phoenix lit les paramètres de la base de données dans config/runtime.exs. Dans la configuration Hello.Repo du bloc if config_env() == :prod do, remplacez la ligne # ssl: true, par le path du certificat de CA :
config/runtime.exs
Application.app_dir résout le path à l’intérieur de votre build ou de votre version, si bien que le paramètre fonctionne quel que soit le répertoire depuis lequel l’application démarre. Définissez DATABASE_URL dans votre environnement de déploiement, là encore sans query string. Si votre application reposait initialement sur SQLite, config/runtime.exs lit plutôt DATABASE_PATH. Remplacez la variable database_path et l’appel config :hello, Hello.Repo associé par :
config/runtime.exs
Les applications créées avec --database sqlite3 exécutent également les migrations en attente au démarrage d’une version, via le processus enfant Ecto.Migrator déclaré dans lib/<your_app>/application.ex. Ce mécanisme fonctionne toujours avec Postgres.

Utiliser PgBouncer

Si les instances de votre application ouvrent ensemble trop de connexions directes, passez plutôt par le PgBouncer intégré. Sélectionnez via PgBouncer dans la fenêtre modale Connect, utilisez le port 6432 dans DATABASE_URL et ajoutez prepare: :unnamed à la configuration du dépôt, à la fois dans config/dev.exs et dans config/runtime.exs :
config/dev.exs
PgBouncer fonctionne en mode pool de connexions par transaction :
  • Requêtes préparées : par défaut, Ecto exécute chaque requête sous forme de requête préparée nommée, et le PgBouncer intégré prend en charge les statements nommés. Postgrex libère les statements au moyen d’un message Close au niveau du protocole, jamais avec l’instruction SQL DEALLOCATE. Néanmoins, avec la valeur par défaut prepare: :named, deux problèmes surviennent en passant par PgBouncer :
    • Une requête dont le texte SQL fait environ 4 Ko peut rester bloquée jusqu’à expiration du délai. L’appelant reçoit alors DBConnection.ConnectionError au bout d’environ une minute.
    • Après une migration qui modifie le type d’une colonne, les requêtes qui lisent cette colonne continuent d’échouer avec cached plan must not change result type, même après le redémarrage de l’application, car PgBouncer réutilise sa requête préparée sur le serveur.
    Avec prepare: :unnamed, Postgrex prépare de nouveau chaque requête à chaque exécution, ce qui élimine ces deux problèmes, au prix d’un aller-retour réseau supplémentaire par requête.
  • Migrations : mix ecto.migrate fonctionne via PgBouncer avec le verrou de migration par défaut d’Ecto, qui verrouille la table schema_migrations au sein d’une transaction. Ne définissez pas migration_lock: :pg_advisory_lock pour une connexion PgBouncer. Le verrou consultatif de niveau session risque d’être libéré sur une autre connexion Postgres : la libération échoue alors avec failed to release advisory lock et le verrou reste détenu, si bien que les exécutions suivantes, y compris celles sur le port 5432, restent en attente. Pour exécuter les migrations via la connexion directe, définissez une DATABASE_URL pointant vers le port 5432 pour la commande mix ecto.migrate.
  • État de session : les statements SET, comme un SET search_path dans une fonction de rappel after_connect, ne s’appliquent qu’à la connexion Postgres de la transaction en cours. Un search_path défini dans l’option parameters de Postgrex échoue avec unsupported startup parameter: search_path. LISTEN et NOTIFY, utilisés par Postgrex.Notifications, nécessitent une connexion directe.
Consultez la FAQ pour en savoir plus sur les requêtes préparées et PgBouncer.

Dépannage

  • CLIENT ALERT: Fatal - Unknown CA : cacertfile ne pointe pas vers le certificat de CA de votre service, ou vous avez défini ssl: true. Téléchargez de nouveau le certificat depuis Settings → CA Certificate. Chaque service dispose de sa propre CA.
  • CLIENT ALERT: Fatal - Bad Certificate avec hostname_check_failed : l’hôte indiqué dans DATABASE_URL ne correspond pas au certificat du serveur, par exemple parce que vous avez utilisé une adresse IP. Utilisez le nom d’hôte affiché dans la fenêtre modale Connect.
  • Invalid CA certificate file ... no such file or directory : le path indiqué dans cacertfile est incorrect. Vérifiez que le fichier priv/ca-certificate.pem existe bien.

Étapes suivantes

Dernière modification le 14 octobre 2026