#Multi Tenant approach

64 messages · Page 1 of 1 (latest)

cursive cave
#

Hey everyone,

I’m trying to build a multi tenant SaaS where each client will have their own storefront.

My question is - how can I modify all the storefront APIs that will now require to pass a store ID? Also, what would be a good approach to store the store ID in the storefront? As I would need it for all the requests.

Thanks!

zealous hatch
#

If you have multiple storefronts, you can save it in env, you also can take a look for publishable api key which serves similar purpose so you have more control

cursive cave
#

thank you! do you know by any chance how would be the approach to change the endpoints that now need the store ID for example?

outer osprey
#

AFAIK, you need to extend n custom almost everything if not everything.

#

One of the approach is putting store id as path (req.params.storeid for instance)

Once u look into the code n design, you know that's a huge work awaiting u ahead. Most of the logic is dead simple working on their paradigm of single store.

cursive cave
#

yes I figured it would be a massive work, but having a single store for each of my clients is still a bigger hassle

#

but sometimes I feel like my use case is not really fit for medusa as I've never seen anyone talking about it ( and it is a fairly common system design )

obsidian lagoon
cursive cave
#

@obsidian lagoon Thanks a lot that would be awesome, of course I can help you if you want

zealous hatch
cursive cave
#

got it - I was also more concerned about when I should use module links or extending the models, from what I understood module links might be better to avoid conflicts with new Medusa releases but it might also have a performance penalty due to the new tables it will create

outer osprey
#

that would requires retrieve(…,storeId), before we could do any DML

cursive cave
#

@outer osprey that's one of my concerns - should I change the existing db schema or use module links to avoid changing them at the cost of having extra tables?

#

from what i've understood module links would avoid breaking the backend when there is a medusa release

outer osprey
#

didn't look into v2.0 medusa module links, so wouldn't be sure; I feel it is good to override the things that we should override, and to encounter buggy override behavior in early phase

multi tenants are harder to enjoy the new updates in my opinion; but if you were just extending fields on existing tables or new tables, it would be easier to integrate new updates, altho it is still a manual task

robust hill
#

extend all tables to include clientId then just reference clientId per client. that way it only pulls releveant info to that client from all tables.

#

can be done rather easily with migrations, just will be a migration for every table to extend

mental shuttle
#

Im trying to extend my clientId in my store tables for example, but im having difficulties understanding the concept for v2

cursive cave
#

You can’t create new fields for the already existing tables afaik, you link them through module links

mint basin
#

This approach would need to modify the code of admin dashboard right? In order to show each tenant their own shop.

cursive cave
#

yes pretty much everything needs to be overwritten

autumn flume
#

Feel like need to extend/link nearly everything.

First, the model link

Second, the API need to have store id, which could use the additionaDataField, in almost every route.

round lagoon
worn arrow
round lagoon
#

@worn arrow cant get your point.

cursive cave
#

@round lagoon with approach 3, I’m concerned about the performance since there will be a lot of pivot tables. Wdyt?

round lagoon
full pike
# round lagoon There are 3 approaches have look here https://www.figma.com/board/MslNGbs8MR50me...

Thanks. All these 3 approaches have their own pros and cons based on cost and long term maintenance.

As per my experience, I still find approach 3 as a good start which can be achieved by adding client_id/tenant_id in all tables because of following reasons:

  1. Relatively Easy to launch MVP with just code and minor schema change. Why easy, because client_id addition should be a uniform change. (No complex logic is required)
  2. Low cost until you get high volume traffic. Good for startups/individuals with limited funds. Shared resources.

You never know, how many months your app will be ideal/low traffic after launch and you may want to launch free tier as well, so approach-1 will definitely painful if there is cost constrain. You will die every month by looking into free users consuming dedicated infra resources. 😄

  1. Easy migrations/roll out features. No need to handle infra level complexity.

Rest 2 approaches can be implemented later when you have a team/fund/ MVP in right direction or any client looking for specific use case like data isolation etc.

Overall, if you have huge fund to pump.. and market clients ready, go with approach-1 or 2 otherwise 3.

full pike
# cursive cave <@345236793991364610> with approach 3, I’m concerned about the performance since...

No issue.. I am working with both SQL and NoSQL since last 13 years. We engineers have solution to every problem. And always have a new problem once solve the old one... never ending process.

To handle performance issues in DB, you can plan sharding, partitioning..
e.g. for postgres https://docs.citusdata.com/en/stable/use_cases/multi_tenant.html

for mysql https://vitess.io/ This is used by slack to maintain their MySQL instances.
https://vitess.io/docs/18.0/user-guides/vschema-guide/advanced-vschema/#multi-column-vindexes

or if you are cloud native lover, you can think of Tidb (mysql compatible client) or yugabyte (pg client). They also have good sharding/partitioning capabilities.

I have just tried their playgounds on local. And using tidb/yugabyte for MVP seems bit bulky because of production hardware requirements. But you can explore them and take your own decisions.

cursive cave
#

Thanks for all the input! It was really valuable

unborn comet
#

@cursive cave did you drop the Publishable API Key approach for multi-tenancy support? Which approach are you going ahead with?

cursive cave
pseudo plover
#

Meaning you will generate a new api key for each new tenant?

cursive cave
#

Yes exactly

full pike
#

Is publishable api key really making Medusa multi tenant? Because as per documentation, this key is associated with Sales channel only.

Not sure, if it can make other component specific to tenant like payment, pricing etc..

cursive cave
surreal phoenix
#

I just started with your approach 3rd @cursive cave . After go that way I find we have a table tenant and at least user or customer table have a link with tenant_id. but I think after we finish that we also need to override all inside admin/store api to have a filter with tenant_id base on jwt token. And then I think if use medusa to build multitenancy platform we need to put alot of hard word on that.

unborn comet
cursive cave
unborn comet
#

oh, but then it means you will have to store tenant ID against each order, customer etc.?

cursive cave
#

yes that's inevitable, each table should have a reference to the tenant ID (not ALL of them tbh, but the majority yes)

unborn comet
#

In that case what does publishable api key help with? If your product's have a tenant id, that should be enough to fetch the right products for each tenant no?

cursive cave
#

Each of my tenants can have their own store, so it’s much more convenient for me to provide them with a unique key. This way, I can revoke access anytime if necessary

#

also I dont need to modify every request to include the store_id. The MedusaJS client already handles passing the key

keen coyote
#

Hi everyone,
I'm also working on approach 3 at the moment

In medusa 1.x we could extend models and override services, so Admin panel will be using same API requests and the service will pick the user_id from token and pass it in DB requests to filter the data

The thing with Medusa 2 is a different.
Instead of extending models we must use models linking. This is probably fine.
But the main question is can we override existing services? Does anyone know?

keen coyote
#

For example, i'm gonna have multiple Store objects in DB.

And then link Store to Product, so each product will be connected to particular Store.
From my understanding, I can attach Store to Product in a 'createProductsWorkflow.hooks.productsCreated' hook.

But what I do not understand is when call existing retrive products API, how can I change the underlaying query so that it will return only products assosiated with particular store. I did not find this in documentation

robust hill
sudden ginkgo
#

you can fetch a product with a relation or all products for that matter

keen coyote
#

the issue is not with query

in V1 I can extend/override existing medusa service, e.g. ProductService,
and override e.g. prepareListQuery_ so then all existing requests to products list will use my custom implementation

https://docs.medusajs.com/development/services/extend-service

how can I do the same in V2?

keen coyote
robust hill
#

Just add it where it should be in the file and it will override. Look at the digital product recipe and how it overrides store/carts/[id]/complete

you just place your new route where the old route would be and it’ll override the endpoint

keen coyote
keen coyote
cursive cave
#

@keen coyote thanks a lot! I'm reading it now,

quick question, when it came to link the vendor to the product I did something like this:

const vendorProductsLink = defineLink( MarketplaceModule.linkable.vendor, { linkable: ProductModule.linkable.product, isList: true, } )

while you did this:

export default defineLink( ProductModule.linkable.product, StoreModule.linkable.store );

the way I thought about it was a vendor can have many products, what's the difference between my approach and yours?

keen coyote
cursive cave
#

got it!

but as it is today, I don't think we can have a prod-ready multi-tenant stores yet. I see they are working on a Index module, we would need it otherwise we will come across a few problems like filtering things by vendor_id (it's not possible to do that in a straightforward manner for now)

tacit gust
#

hope Medusa support multi tenant natively soon 😄

round lagoon
#

there is no hope

#

because they are working on their cloud.

tacit gust
#

Really? I'm trying another option like Vendure which already support multi tenant

prisma cove
#

Hey guys ! I'm maybe late, but I'm working on a multi-tenant solution, and on store api, I pass a x-store-id header.