title: Comment a-t-on bouchonné les développeurs backend ?
url: http://tech.m6web.fr/comment-a-t-on-bouchonne-les-developpeurs-backend.html
hash_url: 32a9117b2071a63de5ea214edca57f52

<p>Chez M6Web, nous travaillons actuellement sur la nouvelle version d’un site web pour lequel sont dédiées deux teams :</p>

<ul>
  <li>l’équipe backend fournit l’accès aux données via des API sous Symfony2,</li>
  <li>nous, l’équipe frontend, développons une application SPA <a href="http://isomorphic.net/javascript">isomorphe</a> utilisant <a href="http://facebook.github.io/react/">React.JS</a> et le <a href="https://facebook.github.io/flux/">pattern Flux</a>.</li>
</ul>

<h1 id="dvelopper-le-front-avant-les-api">Développer le front avant les API…</h1>

<p>Nous avons démarré le projet au même moment que l’équipe backend, donc sans avoir accès aux API qui nous fournissent les données nécessaires au fonctionnement de l’application. Nous nous sommes alors interrogé sur la meilleur façon de développer notre front sans dépendre des API tout en impactant un minimum le code cible.</p>

<h1 id="le-contrat-dinterface">Le contrat d’interface</h1>

<p>Le choix technique pour notre SPA a été guidé par <a href="http://tech.m6web.fr/isomorphic-single-page-app-parfaite-react-flux/">une réflexion poussée sur les app isomorphique</a>. Cette approche, React, Flux et tout l’environnement qui tourne autour nous étaient alors totalement inconnu. Nous avons eu une phase importante en amont pour poser les bases de l’architecture du site, démontrer la faisabilité du projet et documenter l’ensemble.</p>

<p>Ce petit délai a permis à l’équipe backend d’établir des contrats d’interface pour les principales routes de l’API. À partir de ces informations, plus ou moins précises, nous avons établi des fichiers de fixtures. L’idée était donc de retourner les données bouchonnées pour chaque appel à une route d’API non existante.</p>

<h1 id="superagent-et-superagent-mock">Superagent et superagent-mock</h1>

<p>Pour réaliser les requêtes aux API, nous utilisons la librairie <a href="http://visionmedia.github.io/superagent/">superagent</a>, un client HTTP javascript facilement extensible. Il est isomorphe, c’est-à-dire qu’il fonctionne aussi bien sur un serveur node.js via npm que côté browser dans une application packagée via un bundler (<a href="http://webpack.github.io/">webpack</a>, <a href="http://browserify.org/">browserify</a>).</p>

<p>Nous avons développé <a href="https://github.com/M6Web/superagent-mock">superagent-mock</a>, un plugin pour superagent, dont le rôle est de simuler les appels HTTP lancés par superagent en retournant des données de fixtures en fonction de l’URL appelée.</p>

<h1 id="en-pratique">En pratique</h1>

<p>Comme superagent, superagent-mock s’installe via npm et peut être utilisé sur des applications serveurs ou clientes (via un bundler). Tout d’abord, il faut rajouter la dépendance à la librairie dans son  <code>package.json</code>.</p>

<div class="highlight"><pre><code class="language-bash" data-lang="bash">npm install superagent-mock --save-dev</code></pre></div>

<p>Il faut ensuite créer le fichier de configuration. C’est ici que vous allez décider des routes à bouchonner. Prenons l’exemple d’une route qui n’existe pas et qui devra nous retourner la liste des auteurs du blog technique de M6Web : <code>http://tech.m6web.fr/api/authors</code>.</p>

<p>Voici la structure du fichier de configuration à mettre en place :</p>

<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// ./config.js file</span>
<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">[</span>
  <span class="p">{</span>
    <span class="nx">pattern</span><span class="o">:</span> <span class="s1">'http://tech.m6web.fr/api/authors'</span><span class="p">,</span>
    <span class="nx">fixtures</span><span class="o">:</span> <span class="s1">'./authors.js'</span><span class="p">,</span>
    <span class="nx">callback</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">match</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="p">{</span> <span class="nx">body</span> <span class="o">:</span> <span class="nx">data</span> <span class="p">};</span>
    <span class="p">}</span>
<span class="p">];</span></code></pre></div>

<ul>
  <li>L’attribut <code>pattern</code> peut être une expression régulière, dans le cas d’une route qui contiendrait des paramètres variables (ex : <code>https://tech.m6web.fr/api/authors/(\\d+)</code>).</li>
  <li>L’attribut <code>fixtures</code> représente le lien vers le fichier de fixtures ou une callback.</li>
  <li>L’attribut <code>callback</code> est une fonction à deux arguments. <code>match</code> est le résultat de la résolution de l’expression régulière et <code>data</code> correspond aux données retournées par les fixtures. <code>match</code> permet d’utiliser certains paramètres de l’appel (ex : l’id de l’auteur) pour retourner des données ciblées (ex : l’auteur dans le fichier de fixtures correspondant à cette id).</li>
</ul>

<p>Ensuite, il faut créer le fichier de fixtures. C’est un fichier JS qui exporte une fonction retournant les données bouchonnées.</p>

<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// ./authors.js file</span>
<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">[</span>
    <span class="p">{</span>
      <span class="nx">id</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
      <span class="nx">name</span><span class="o">:</span> <span class="s2">"John Doe"</span><span class="p">,</span>
      <span class="nx">description</span><span class="o">:</span> <span class="s2">"unidentified person"</span>
    <span class="p">},</span>
    <span class="p">...</span>
  <span class="p">];</span>
<span class="p">};</span></code></pre></div>

<p>Pour finir, au début du fichier JS appelé par node, il suffit de patcher <a href="http://visionmedia.github.io/superagent/">superagent</a> avec le plugin <a href="https://github.com/M6Web/superagent-mock">superagent-mock</a> de cette manière :</p>

<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// ./server.js file</span>
<span class="kd">var</span> <span class="nx">request</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'superagent'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">config</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'./config.js'</span><span class="p">);</span>
<span class="nx">require</span><span class="p">(</span><span class="s1">'superagent-mock'</span><span class="p">)(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">config</span><span class="p">);</span></code></pre></div>

<p>Ces quelques lignes permettent de surcharger certaines méthodes de <a href="http://visionmedia.github.io/superagent/">superagent</a> pour lui appliquer la configuration et simuler les requêtes bouchonnées. Pour comprendre plus en détail le fonctionnement, <a href="https://github.com/M6Web/superagent-mock/blob/master/superagent-mock.js">c’est par ici</a>.</p>

<h1 id="et-aprs-">Et après ?</h1>

<p>Avec cette astuce, vous pouvez développer votre front sans qu’aucune API en face ne soit accessible. C’est très pratique pour travailler en local, sans accès au net, ou pour rendre les tests fonctionnels de son application complètement indépendants d’un service tiers externe.</p>

<p>La partie délicate de cette approche intervient lorsque l’on câble son application avec la vraie API… et que l’on s’aperçoit que le contrat d’interface n’a pas été respecté ! Nous avons souvent des corrections à réaliser dans notre code lors de cette étape, mais les changements sont généralement mineurs et le gain de temps apporté par l’utilisation du bouchon en amont n’est pas remis en cause. La partie fastidieuse reste de maintenir ses fichiers de fixtures avec l’évolution de l’API, particulièrement nécessaire si on s’en sert dans ses tests fonctionnels.</p>

<h1 id="toujours-plus">Toujours plus</h1>

<p>Notre application forge elle-même l’URL des images récupérées via l’API : elle nous fournit un id et nous reconstituons l’URL finale grâce à un paramètre de configuration. Ce n’est pas REST compliant mais nous avons de bonnes raisons de le faire. Cette génération d’URL utilise la librairie <a href="https://github.com/alexei/sprintf.js">sprintf-js</a>. Pour avoir une application complètement indépendante de toute requête externe, nous avons dû également bouchonner ces appels sur des images locales. Dans cette optique, nous avons développé <a href="https://github.com/M6Web/sprintf-mock">sprintf-mock</a> dont le mode de fonctionnement est étrangement similaire à celui de superagent-mock.</p>

<p>Les projets <a href="https://github.com/M6Web/superagent-mock">superagent-mock</a> et <a href="https://github.com/M6Web/sprintf-mock">sprintf-mock</a> sont open source. Très simple d’utilisation, ils nous permettent de paralléliser nos développements avec l’équipe backend et de rendre autonomes nos tests fonctionnels. Alors n’attendez plus la finalisation de vos API pour commencer vos développements front !</p>
