<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Iván González Sáiz</title>
    <description>The latest articles on DEV Community by Iván González Sáiz (@dreamingechoes).</description>
    <link>https://dev.to/dreamingechoes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F7735%2Fe4cebbad-5cb1-4e10-836d-a487b6f8d870.jpg</url>
      <title>DEV Community: Iván González Sáiz</title>
      <link>https://dev.to/dreamingechoes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dreamingechoes"/>
    <language>en</language>
    <item>
      <title>Creating an AI-Powered Search Input With Phoenix LiveView</title>
      <dc:creator>Iván González Sáiz</dc:creator>
      <pubDate>Mon, 24 Feb 2025 18:37:55 +0000</pubDate>
      <link>https://dev.to/dreamingechoes/creating-an-ai-powered-search-input-with-phoenix-liveview-16o3</link>
      <guid>https://dev.to/dreamingechoes/creating-an-ai-powered-search-input-with-phoenix-liveview-16o3</guid>
      <description></description>
      <category>ai</category>
      <category>webdev</category>
      <category>phoenixliveview</category>
    </item>
    <item>
      <title>Building a Minimal Blog in Pure Elixir with Notion as a CMS</title>
      <dc:creator>Iván González Sáiz</dc:creator>
      <pubDate>Mon, 17 Feb 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/dreamingechoes/building-a-minimal-blog-in-pure-elixir-with-notion-as-a-cms-13on</link>
      <guid>https://dev.to/dreamingechoes/building-a-minimal-blog-in-pure-elixir-with-notion-as-a-cms-13on</guid>
      <description>&lt;p&gt;A proof of concept for a simple blog using pure Elixir, with Notion as a CMS and only three dependencies: Cowboy, HTTPoison, and Jason.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>notion</category>
      <category>blog</category>
    </item>
    <item>
      <title>How to Change a Field Type in an Ecto Embedded Schema with a Migration</title>
      <dc:creator>Iván González Sáiz</dc:creator>
      <pubDate>Fri, 14 Feb 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/dreamingechoes/how-to-change-a-field-type-in-an-ecto-embedded-schema-with-a-migration-171l</link>
      <guid>https://dev.to/dreamingechoes/how-to-change-a-field-type-in-an-ecto-embedded-schema-with-a-migration-171l</guid>
      <description>&lt;p&gt;Changing a field type in an Ecto embedded schema requires both a code update and a database migration. This guide walks through the process, ensuring data consistency and avoiding common pitfalls.&lt;/p&gt;

</description>
      <category>ecto</category>
      <category>phoenix</category>
      <category>elixir</category>
      <category>migrations</category>
    </item>
    <item>
      <title>Small Signals, Big Impact: How Tiny Acts of Self-Care Can Transform Workplace Well-Being</title>
      <dc:creator>Iván González Sáiz</dc:creator>
      <pubDate>Thu, 13 Feb 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/dreamingechoes/small-signals-big-impact-how-tiny-acts-of-self-care-can-transform-workplace-well-being-2c7o</link>
      <guid>https://dev.to/dreamingechoes/small-signals-big-impact-how-tiny-acts-of-self-care-can-transform-workplace-well-being-2c7o</guid>
      <description>&lt;p&gt;Small, thoughtful ways to support each other at work can have a big impact. Normalizing simple signals—like Slack statuses or images—helps create a healthier, more mindful workplace.&lt;/p&gt;

</description>
      <category>workplacewellness</category>
      <category>selfcare</category>
      <category>mentalhealth</category>
      <category>teamculture</category>
    </item>
    <item>
      <title>Do you know any good resource about mental health in the software industry?</title>
      <dc:creator>Iván González Sáiz</dc:creator>
      <pubDate>Tue, 13 Nov 2018 10:18:25 +0000</pubDate>
      <link>https://dev.to/dreamingechoes/do-you-know-any-good-resource-about-mental-health-in-the-software-industry-2ef9</link>
      <guid>https://dev.to/dreamingechoes/do-you-know-any-good-resource-about-mental-health-in-the-software-industry-2ef9</guid>
      <description>&lt;p&gt;Hi folks!&lt;/p&gt;

&lt;p&gt;I would like to start here a discussion in which we can all together contribute to make an awesome list with all kind of resources (articles, talks, podcasts, organizations...) about mental health in the software industry.&lt;/p&gt;

&lt;p&gt;One month ago I made a repository for this purpose. The initial idea was motivated by this tweet of Jess Lee on the World Mental Health Day: &lt;a href="https://twitter.com/jessleenyc/status/1050107830794096641"&gt;https://twitter.com/jessleenyc/status/1050107830794096641&lt;/a&gt;, so I guess it makes sense to share here the repository I made and so we can create something nice among the whole community 😊&lt;/p&gt;

&lt;p&gt;This is the Github repository: &lt;a href="https://github.com/dreamingechoes/awesome-mental-health"&gt;https://github.com/dreamingechoes/awesome-mental-health&lt;/a&gt;. Do you know any good resource? I'll appreciate so much if you leave it in a comment here, or if you want you can make a pull request in the repository.&lt;/p&gt;

&lt;p&gt;Thank you! 🤗 💖&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>opensource</category>
      <category>mentalhealth</category>
      <category>health</category>
    </item>
    <item>
      <title>Generate UUID fields in Phoenix with Postgresql</title>
      <dc:creator>Iván González Sáiz</dc:creator>
      <pubDate>Fri, 10 Aug 2018 16:34:15 +0000</pubDate>
      <link>https://dev.to/dreamingechoes/generate-uuid-fields-in-phoenix-with-postgresql--2a02</link>
      <guid>https://dev.to/dreamingechoes/generate-uuid-fields-in-phoenix-with-postgresql--2a02</guid>
      <description>&lt;p&gt;If for some reason you need to create a &lt;code&gt;UUID&lt;/code&gt; field for your schema (a non-primary key field), and you don't want to manage the creation of the value to store on the new database record, you can specify in the migration to use as default value the &lt;code&gt;uuid_generate_v4()&lt;/code&gt; &lt;strong&gt;Postgresql&lt;/strong&gt; method, so it will be auto-generated on the insert:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;App&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;AddUuidFieldToUser&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:some_new_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"uuid_generate_v4()"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the configuration of your database, maybe you will see this error running the migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;**&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Postgrex.Error&lt;span class="o"&gt;)&lt;/span&gt; ERROR 42883 &lt;span class="o"&gt;(&lt;/span&gt;undefined_function&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="k"&gt;function &lt;/span&gt;uuid_generate_v4&lt;span class="o"&gt;()&lt;/span&gt; does not exist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because your database hasn't the &lt;code&gt;uuid-ossp&lt;/code&gt; module activated. You can activate by login to your database and execute this query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="nv"&gt;"uuid-ossp"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if you don't have access to the database, you can create a migration in order to execute the query like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;App&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;AddUuidGenerateV4ExtensionToDatabase&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"CREATE EXTENSION IF NOT EXISTS &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;uuid-ossp&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see more in-depth information about &lt;code&gt;uuid-ossp&lt;/code&gt; module &lt;a href="https://www.postgresql.org/docs/9.4/static/uuid-ossp.html"&gt;here&lt;/a&gt;. So when the migrations are executed properly, add to your schema the new field, and there you have it! Your new shiny auto-generated &lt;code&gt;UUID&lt;/code&gt; field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;App&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changeset&lt;/span&gt;

  &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:some_new_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:binary_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This post is originally published &lt;a href="http://http://dreamingecho.es/til/generate-uuid-fields-in-phoenix-with-postgresql"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>elixir</category>
      <category>postgres</category>
    </item>
    <item>
      <title>A dive into database multi-tenancy in Elixir with Ecto</title>
      <dc:creator>Iván González Sáiz</dc:creator>
      <pubDate>Wed, 18 Jul 2018 09:13:38 +0000</pubDate>
      <link>https://dev.to/dreamingechoes/a-dive-into-database-multi-tenancy-in-elixir-with-ecto-171h</link>
      <guid>https://dev.to/dreamingechoes/a-dive-into-database-multi-tenancy-in-elixir-with-ecto-171h</guid>
      <description>&lt;p&gt;Some time ago I had to tackle the migration of an application with a regular database to a multi-tenancy one, and I would like to share my experience here.&lt;/p&gt;

&lt;p&gt;Despite the existence of libraries like &lt;a href="https://github.com/Dania02525/apartmentex"&gt;apartmentex&lt;/a&gt;, &lt;a href="https://github.com/ateliware/triplex"&gt;triplex&lt;/a&gt; or &lt;a href="https://github.com/jeffdeville/tenantex"&gt;tenantex&lt;/a&gt;, I wanted to make the migration as dependency free as possible, so I made my own management for the multi-tenancy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Ecto prefix option
&lt;/h2&gt;

&lt;p&gt;Most of the methods provided by &lt;a href="https://hexdocs.pm/ecto/Ecto.html"&gt;Ecto&lt;/a&gt; which directly interacts with the database have a series of options, in which the most important one for multi-tenancy is &lt;strong&gt;prefix&lt;/strong&gt;. According to the documentation, for &lt;strong&gt;Postgres&lt;/strong&gt; users, &lt;strong&gt;prefix&lt;/strong&gt; will specify &lt;strong&gt;the schema where the table is located&lt;/strong&gt;, while for &lt;strong&gt;MySQL&lt;/strong&gt; users will specify &lt;strong&gt;the database where the table is located&lt;/strong&gt;. When no &lt;strong&gt;prefix&lt;/strong&gt; is set, &lt;strong&gt;Postgres&lt;/strong&gt; queries are assumed to be executed in the public schema, while &lt;strong&gt;MySQL&lt;/strong&gt; queries are assumed to be executed in the database set in the config for the repo.&lt;/p&gt;

&lt;p&gt;For example, if we want to insert a new element in our database with &lt;a href="https://hexdocs.pm/ecto/Ecto.Repo.html#c:insert/2"&gt;Repo.insert/2&lt;/a&gt;, we execute something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;MyAppRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;       &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# Inserted with success&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# Something went wrong&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To do this in a multi-tenancy application, we need to specify the &lt;strong&gt;prefix&lt;/strong&gt; option for the insertion, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;MyAppRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;prefix: &lt;/span&gt;&lt;span class="s2"&gt;"some_tenant"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;       &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# Inserted with success&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# Something went wrong&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Main changes in our application
&lt;/h2&gt;

&lt;p&gt;In order to prepare our application to work with a multi-tenant database, we have to make some changes in our code. Let's see the main changes with examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Default Phoenix scaffold
&lt;/h3&gt;

&lt;p&gt;When we use the &lt;code&gt;mix phx.gen.html&lt;/code&gt; task, a series of files are generated, among which is the context with all the functions to interact with the repo. For example, the function responsible for creating a new element will look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="p"&gt;\\&lt;/span&gt; &lt;span class="sx"&gt;%{}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To enable multi-tenancy capabilities, we have to include in all the functions which interact with the repo (&lt;code&gt;create&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;...) the &lt;code&gt;tenant&lt;/code&gt; parameter to specify the &lt;strong&gt;prefix&lt;/strong&gt; option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="p"&gt;\\&lt;/span&gt; &lt;span class="sx"&gt;%{}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;prefix: &lt;/span&gt;&lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating a new tenant
&lt;/h3&gt;

&lt;p&gt;If we want to create a new tenant, we need to execute the proper query depending on the database we use (for &lt;strong&gt;Postgres&lt;/strong&gt; will be &lt;code&gt;CREATE SCHEMA&lt;/code&gt;). So we'll have a function like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"CREATE SCHEMA &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Listing available tenants
&lt;/h3&gt;

&lt;p&gt;List all the available tenants will allow us to easily switch between them. To get this information, we have to make a query against the &lt;code&gt;information_schema&lt;/code&gt; schema and select the &lt;code&gt;schema_name&lt;/code&gt; field of the &lt;code&gt;schemata&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;To be able to identify the schemas of my application, I like to use some &lt;strong&gt;&lt;em&gt;prefix&lt;/em&gt;&lt;/strong&gt; to differentiate my schemas from other ones (like &lt;code&gt;tenant_&lt;/code&gt;), that's why I use the &lt;code&gt;schema_prefix&lt;/code&gt; parameter on the query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_tenants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema_prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;schemata&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"schemata"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;select: &lt;/span&gt;&lt;span class="n"&gt;schemata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schema_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;where: &lt;/span&gt;&lt;span class="n"&gt;like&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schemata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schema_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;schema_prefix&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;prefix: &lt;/span&gt;&lt;span class="s2"&gt;"information_schema"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you want to check if this function returns the correct information, you can enter the &lt;strong&gt;Postgres&lt;/strong&gt; console and execute the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;database_dev&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# SELECT schema_name FROM information_schema.schemata WHERE schema_name LIKE 'tenant_%';&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Deleting a specific tenant
&lt;/h3&gt;

&lt;p&gt;Like with the creation, if we want to delete a specific tenant, we need to execute the proper query depending on the database we use (for &lt;strong&gt;Postgres&lt;/strong&gt; will be &lt;code&gt;DROP SCHEMA&lt;/code&gt;). So we'll have a function like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drop_schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"DROP SCHEMA &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; CASCADE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating and running migrations on a specific tenant
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;mix ecto.gen.migration&lt;/code&gt; task creates a new file with the migration we want into the &lt;code&gt;priv/repo/migrations&lt;/code&gt; folder of the application. In case we need to be able to run migrations on the public schema and on the custom generated ones, we need to create a new &lt;code&gt;priv/repo/tenant_migrations&lt;/code&gt; folder, and store the migrations which only will be run on the custom generated schemas on this folder.&lt;/p&gt;

&lt;p&gt;We can create a custom task in order to generate and store new migrations directly on this folder. To avoid putting too much code in this post, &lt;a href="https://github.com/dreamingechoes/multi_tenancex/blob/master/lib/multi_tenancex/mix/tasks/multi_tenancex.gen.tenant_migration.ex"&gt;here you have&lt;/a&gt; an example of the task so we can run something like this on the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;user@computer:~&lt;span class="nv"&gt;$ &lt;/span&gt;mix app_name.gen.tenant_migration add_users_table
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once we have the new migration in our &lt;code&gt;priv/repo/tenant_migrations&lt;/code&gt; folder, we can create as well a custom task to run the migrations for all the custom generated schemas. Again, to avoid putting too much code in this post, &lt;a href="https://github.com/dreamingechoes/multi_tenancex/blob/master/lib/multi_tenancex/mix/tasks/multi_tenancex.ecto.migrate_tenants.ex"&gt;here you have&lt;/a&gt; an example of the task so we can run something like this on the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;user@computer:~&lt;span class="nv"&gt;$ &lt;/span&gt;mix app_name.ecto.migrate_tenants
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And finally, we can create a custom task to rollback the migrations for all the custom generated schemas. &lt;a href="https://github.com/dreamingechoes/multi_tenancex/blob/master/lib/multi_tenancex/mix/tasks/multi_tenancex.ecto.rollback_tenants.ex"&gt;Here you have&lt;/a&gt; an example of the task so we can run something like this on the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;user@computer:~&lt;span class="nv"&gt;$ &lt;/span&gt;mix app_name.ecto.rollback_tenants
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;At the end of the post I'll leave a link to an example &lt;strong&gt;Phoenix&lt;/strong&gt; application with all the examples, so don't worry if I don't specify the code here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storing the current tenant in our application
&lt;/h3&gt;

&lt;p&gt;We'll need to have the current tenant in which we have to execute all repo functions all along the application available. For this, which worked the best for me it's using &lt;a href="https://github.com/ueberauth/guardian"&gt;Guardian&lt;/a&gt; to store the current tenant as a claim with &lt;code&gt;sign_in&lt;/code&gt;. So, for example, if we have a controller to manage sessions, the &lt;code&gt;create&lt;/code&gt; function will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%{
      "session" =&amp;gt; %{
        "tenant" =&amp;gt; tenant,
        "email" =&amp;gt; email,
        "password" =&amp;gt; password
      }
    }&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Guardian&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticate_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;conn&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%{current_tenant: tenant}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;put_flash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Welcome to AppName!"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="n"&gt;page_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;conn&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;put_flash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="n"&gt;session_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once the user logs in successfully, we'll be able to access that claim on the &lt;code&gt;assigns&lt;/code&gt; of the &lt;code&gt;conn&lt;/code&gt;, so, using the example seen before about the creation of a user, we can do something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_attrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_tenant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Things to take in mind
&lt;/h2&gt;

&lt;p&gt;Once you have completed the migration of the code of our application, we have to take care of the dependencies used which directly interact with the repo. Most of the packages I saw do not allow the user to propagate the &lt;code&gt;opts&lt;/code&gt; for the repo, so we'll can't use multi-tenancy in them, &lt;strong&gt;so be careful&lt;/strong&gt;. And if you're going to create a new package, &lt;strong&gt;please allow the user to propagate the repo&lt;/strong&gt; &lt;code&gt;opts&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example of a multi-tenancy application
&lt;/h2&gt;

&lt;p&gt;I have developed a small application in &lt;strong&gt;Phoenix&lt;/strong&gt; to be able to check the real behavior of all the concepts seen here. You can check the code &lt;a href="https://github.com/dreamingechoes/multi_tenancex"&gt;in this repo&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This post is originally published &lt;a href="http://dreamingecho.es/blog/a-dive-into-database-multi-tenancy-in-elixir-with-ecto"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>elixir</category>
      <category>ecto</category>
      <category>multitenancy</category>
    </item>
  </channel>
</rss>
