Is there a plug for setting the tenant?

I am building an app where users can belong to different organizations. I want each organization to be it’s own tenant ( Multitenancy — ash v2.15.17 ). When the user is logged in, they select a tenant, and can only have access to data from that tenant.

Is there a way call set_tenant in a router plug ( similar to Ash.PlugHelpers.set_actor(user) ) so that I can globally set the current tenant, without for any queries that might need it?

1 Like

Hi there :wave:

There is Ash.PlugHelpers.set_tenant/2.

The thing to note is that the Ash plughelpers are there to provide a consistent interface across various extensions that interact with plugs - ie AshJsonApi, AshGraphQl and AshAuthentication. Note that these won’t do anything for your code - you would still need to call get_tenant/2 and pass it into your actions as an option.

There is also Ash.set_tenant/1 which sets it in the process dictionary but that feature is going away in 3.0 on favour of requiring it to be explicitly passed as above.

1 Like

Thank you! What you are saying about set_tenant totally makes sense; however, I don’t quite understand how I would call get_tenant and pass it in as an option. Like, if someone wanted to split a simple resource like the one below across tenants, what changes would you have to make?


defmodule App.API.Visit do

  use App.Resource,
    api: App.API,
    data_layer: AshPostgres.DataLayer,
    extensions: [AshGraphql.Resource],
    authorizers: [Ash.Policy.Authorizer]


   # OTHER STUFF .....

  actions do
    defaults [:read, :update]

    read :list_visits_paginated do
      pagination keyset?: true, default_limit: 100
    end

    update :update_visit do
      accept [:scheduled_for]
      require_attributes [:scheduled_for]
    end

    create :create_visit do
      accept [ :scheduled_for]
      change relate_actor(:user, allow_nil?: false)
   end

end

Right. Well the first think is to decide what sort of multitenancy you want and set the resource DSL appropriately. See the guide for a starting point.

Next when you call your actions you set the tenant on the changeset or query using their respective set_tenant functions. If you’re using the code interface you can pass a tenant: option in the options keyword list as the last argument in the call.

You can also use the tenant option to Ash.Query.for_read and Ash.Changeset.for_create/update/destroy :slight_smile:

Ok. Thank you ( and thanks for your patience with me ). It sounds like I need to set the tenant as option when I call the actions in code, or when I do a query. That makes sense to me now

I think I am still confused about AshGraphQl / AshAdmin / AshAuthentication. If I use Ash.PlugHelpers.set_tenant/2 in the router, does that automatically set_tenant on those services and I just don’t need to worry about it? Or is there something I need to do inside of my router or Ash.Api to make it work?

Yes, AshGraphql and AshJsonApi will automatically use tenant/actor information set using Ash.PlugHelpers :slight_smile:

So you only need to manually thread the tenant through in things like LiveView or any custom calls to Ash.