Nouveau : Datasets open source gratuits disponibles !Decouvrir →
🔥
Avance 25 min Phoenix

WebSockets avec Phoenix

Pourquoi WebSockets avec Phoenix ?

Les développeurs modernes ont besoin de technologies réactives pour permettre une communication bidirectionnelle entre le client et le serveur en temps réel. Cela est particulièrement important dans les applications modernes où la performance, la latence et l'expérience utilisateur sont des priorités.

Un cas d'utilisation concret serait un chat en direct, où chaque message envoyé par un utilisateur doit être instantanément affiché à tous les autres utilisateurs connectés. Avec WebSockets, cette communication peut se faire avec une latence très faible, même pour des applications à grande échelle.

Prerequis

  • Connaissance de base de Elixir et Phoenix
  • Compréhension des bases d'HTTP et de JavaScript
  • Installation de Erlang/OTP (version 23 ou plus récente)
  • Installation de Node.js (pour les outils de développement frontend)

Concepts fondamentaux

WebSockets

WebSockets est une protocole de communication bidirectionnelle qui permet des connexions persistantes entre le client et le serveur. Différentement d'HTTP, qui est un protocole stateless avec des requêtes-réponses, WebSockets maintient une connexion ouverte tout au long du temps.

Schema mental :

Client <-> Server
phoenix
## Connexion WebSocket en Elixir
defmodule MyAppWeb.UserSocket do
  use Phoenix.Socket

  def connect(_params, socket) do
    {:ok, assign(socket, :user_id, "user-123")}
  end
end

Broadcasts et Channels

Les channels sont la couche d'abstraction entre les clients et le serveur. Ils permettent de diffuser des messages à un ensemble spécifique de clients.

Schema mental :

Client <-> Channel -> Server -> Client (diffusion)
phoenix
## Definition d'un channel en Elixir
defmodule MyAppWeb.RoomChannel do
  use Phoenix.Channel

  def join("room:lobby", _params, socket) do
    {:ok, assign(socket, :user_id, "user-123")}
  end

  def handle_in("new_msg", %{"body" => body}, socket) do
    broadcast(socket, "new_msg", %{body: body})
    {:noreply, socket}
  end
end

PubSub et Topic

La publication/abonnement (PubSub) est une architecture de diffusion qui permet aux messages d'être envoyés à des groupes spécifiques de clients. Les topics sont les groupes auxquels un client peut s'abonner.

Schema mental :

Client <-> Topic -> Server -> Client
phoenix
## Publication/abonnement en Elixir
defmodule MyAppWeb.PubSub do
  use Phoenix.PubSub, otp_app: :my_app
end

## Abonnement d'un client
Phoenix.PubSub.subscribe(MyAppWeb.PubSub, "room:lobby")

## Publication d'un message
MyAppWeb.PubSub.broadcast("room:lobby", "new_msg", %{body: "Hello"})

Mise en pratique : projet fil rouge

Nous allons construire un simple chat en direct avec Phoenix. Le mini-projet comprendra une interface web et un serveur backend.

Étape 1 : Configuration du projet

Créez un nouveau projet Phoenix :

mix phx.new chat_app --no-ecto
cd chat_app

Installez les dépendances frontend avec Yarn :

yarn install

Étape 2 : Création des channels et controllers

Ajoutez un channel pour le chat :

## lib/chat_app_web/channels/chat_channel.ex
defmodule ChatAppWeb.ChatChannel do
  use Phoenix.Channel

  def join("chat:lobby", _params, socket) do
    {:ok, assign(socket, :user_id, "user-123")}
  end

  def handle_in("new_msg", %{"body" => body}, socket) do
    broadcast(socket, "new_msg", %{body: body})
    {:noreply, socket}
  end
end

Ajoutez un controller pour les messages :

## lib/chat_app_web/controllers/message_controller.ex
defmodule ChatAppWeb.MessageController do
  use ChatAppWeb, :controller

  def index(conn, _params) do
    render(conn, "index.html")
  end
end

Étape 3 : Configuration des routes

Ajoutez les routes pour le chat et les messages :

## lib/chat_app_web/router.ex
defmodule ChatAppWeb.Router do
  use ChatAppWeb, :router

  scope "/", ChatAppWeb do
    pipe_through :browser

    get "/", MessageController, :index
  end

  socket "/socket", ChatAppWeb.UserSocket do
    channel "chat:lobby", ChatAppWeb.ChatChannel
  end
end

Étape 4 : Création de la vue frontend

Créez une interface simple en HTML pour le chat :

<!-- lib/chat_app_web/templates/message/index.html.eex -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Chat App</title>
  <script src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</head>
<body>
  <h1>Chat Room</h1>
  <div id="chat-messages">
    <!-- Messages seront affichés ici -->
  </div>
  <input type="text" id="message-input" placeholder="Type a message...">
  <button id="send-button">Send</button>

  <script>
    let socket = new Phoenix.Socket("/socket")
    socket.connect()

    let channel = socket.channel("chat:lobby", {})

    channel.join()
      .receive("ok", resp => { console.log("Joined successfully", resp) })
      .receive("error", resp => { console.log("Unable to join", resp) })

    channel.on("new_msg", payload => {
      let messageElement = document.createElement("div")
      messageElement.textContent = payload.body
      document.getElementById("chat-messages").appendChild(messageElement)
    })

    document.getElementById("send-button").addEventListener("click", () => {
      let input = document.getElementById("message-input")
      channel.push("new_msg", { body: input.value })
      input.value = ""
    })

    document.getElementById("message-input").addEventListener("keypress", (e) => {
      if (e.key === "Enter") {
        e.preventDefault()
        let input = document.getElementById("message-input")
        channel.push("new_msg", { body: input.value })
        input.value = ""
      }
    })
  </script>
</body>
</html>

Étape 5 : Compilation des assets

Compilez les assets frontend :

yarn run deploy

Erreurs fréquentes et debugging

  1. Erreur de connexion WebSocket

    # Mauvais
    channel.join()
      .receive("ok", resp => { console.log("Joined successfully", resp) })
      .receive("error", resp => { console.log("Unable to join", resp) })
    
    # Correct
    channel.join()
      .receive("ok", resp => { console.log("Joined successfully", resp) })
      .receive("error", resp => {
        console.error("Unable to join", resp)
      })
    
  2. Erreur de publication

    # Mauvais
    MyAppWeb.PubSub.broadcast("room:lobby", "new_msg", %{body: "Hello"})
    
    # Correct
    MyAppWeb.PubSub.broadcast!("room:lobby", "new_msg", %{body: "Hello"})
    
  3. Erreur de channel non trouvé

    # Mauvais
    let channel = socket.channel("chat:lobby")
    
    # Correct
    let channel = socket.channel("chat:lobby", {})
    

Pour aller plus loin

  1. Authentification et sécurité : Ajouter une authentification JWT pour sécuriser les channels.
  2. Scalabilité : Utiliser Redis avec Phoenix.PubSub.RedisAdapter pour améliorer la performance de diffusion.
  3. Tests unitaires : Écrire des tests unitaires pour les channels.

Défi pratique

Implémentez une fonctionnalité qui permet aux utilisateurs d'envoyer des messages privés entre eux en utilisant des topics personnalisés.

Besoin d'aide sur Phoenix ?

Besoin d'aide sur un projet technique ? Decrivez-le pour des conseils personnalises.

Recevoir des conseils

Questions frequentes

Qu'est-ce que Phoenix?
Phoenix est une framework Elixir pour construire des applications web en temps réel et à grande échelle.
Comment installer Phoenix WebSockets?
Pour installer Phoenix WebSockets, vous devez d'abord avoir Elixir installé. Ensuite, exécutez `mix phx.new my_app` pour créer un nouveau projet et suivez les instructions fournies dans le terminal.
Quelles sont les principales avantages de l'utilisation de WebSockets avec Phoenix?
Les avantages principaux incluent des connexions à deux sens, la communication en temps réel et la possibilité de créer des applications dynamiques et réactives.

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.