Closed hrueger closed 1 year ago
If each subdomain's application has the same basic shape (which it should if you want to use a single application for all of them), then you should be able to just expose the Host
header to the rest of your app either in your handle
hook or in a top-level +layout.server.js
.
Hi @Conduitry, thanks for the fast response. I haven't thought about that. However, I don't think that it would work for the described usecase. The routes are not the same for all subdomains. There's the difference between user-frontend and tenant-admin-backend as well as the registration pages. They do share a lot of UI components and server logic though, so I'd prefer not to move that into a package make separate apps...
Edit: Logic in the +layout.server.ts
file does not allow my to switch between bulks of routes dynamically, right?
It seems possible with Nuxt: https://ynechaev.medium.com/subdomains-with-nuxt-js-483df826c788
I would +1 this, I am looking into making an application where each user has his own subdomain and having a build in way to get it would be really convenient. This is something that is supported in other frameworks as far as I remember.
The routes are not the same for all subdomains. There's the difference between user-frontend and tenant-admin-backend as well as the registration pages.
It sounds like you have three separate apps, two of which could use @Conduitry's idea of exposing Host
...
...and you just need to route requests to one of the three apps based on the hostname.
Is there any reason you need those to instead be a single app?
I have the same use case : ~4 apps that share most of their UI components, stores, utils, but with different routes. The apps are deployed on different domains. (ex1, ex2)
The way I solve it currently is to have a monorepo with a routes
directory per app, like routes/app1
. and routes/app2
I then use the kit.files.routes
config to set the correct directory depending on an env variable PUBLIC_APP=app1 npm run dev
.
I deploy these apps on Vercel where I have 4 different "projects" connected to the same repo, but with a different env variable.
This works very well, except that now the deployment is slow. Vercel needs to rebuild the app 4 times and it isn't done in parallel (extra workers are costly on Vercel). If Svelte Kit supported this "multi-tenant application" pattern to build a single "app" to serve my 4 distinct sites, I would be happy to switch to it to simplify the development and deployment processes.
Is there any reason you need those to instead be a single app?
I'd say it's about convenience for the developer. Svelte Kit is already so great at dev experience and I (and if I understand @mquandalle and @georgeiliadis91 correctly them, too) feel like being forced to split apps - just because the routing technique we'd like to use is partly based on (sub)domains and not exclusively on paths - impairs that quite a bit.
Advantages I see with a single app are the following:
Don't get me wrong. I could probably achieve the same thing with the current version of Svelte Kit, it would just not be nice code and a good dev experience. You could also completely ignore the router, parse the path segment of the URL and have a single template with a ton of {#if
s. It would just not be as convenient, usable and (most importantly) maintainable then using the router.
I see the (sub)domain routing feature kind of related to this example.
Can you see my points?
Best regards
Couldn't you just deploy a single app, and assign all of your desired subdomains to it in vercel's config?
then you can look at the host header in a hook, and just respond accordingly.
single app, single deployment, multiple assigned domains
supporting this sort of thing in SvelteKit, even though others have done, feels like the wrong thing to do. Domains are supposed to be different computers (based on the original design for domain names), and handling this within a framework like SK feels out of scope.
(oh and personally, for scalability and sanity, we have done this type of setup with a monorepo and multiple apps - it sounds like you're forcing a monolith where your architecture screams out for microfrontends)
Couldn't you just deploy a single app, and assign all of your desired subdomains to it in vercel's config?
I would like to, but I don't know how to benefit from file system routing for multiple apps simultaneously.
routes/
app1.com/
app2.fr/
subdomain.app1.com/
Maybe it's possible to rewrite the query in a hook to append the host name to the query path so that http://app1.com/about
is routed into /app1.com/about/
?
But then it also need to work in dev mode, probably by defining a base path, so that a <a href="/about"
goes to localhost:5173/app1.com/about
, which maybe need some support from Svelte Kit?
(oh and personally, for scalability and sanity, we have done this type of setup with a monorepo and multiple apps - it sounds like you're forcing a monolith where your architecture screams out for microfrontends)
For instance I use it for app variants for specific countries : france, italy, where I want to customize the routes and the content for each country but still reuse most of the functionalities. Another example : https://mesaidesvelo.fr and https://aideretrofit.fr which are based on the same repo using the kit.files.routes
parameter I mentioned earlier https://github.com/mquandalle/mesaidesvelo/blob/master/svelte.config.js#L10
Honestly, this feels like 'scenario solving' to me. Bottom line, these are separate apps and should be treated as such. Introducing significant extra complexity to the routing system just to cut down on build time a bit is entirely the wrong trade-off.
'I don't want to set up a monorepo' simply isn't a good enough reason to make the framework itself more complicated. This is what monorepos are for.
There are workable approaches to do the things described in this thread, so I'm going to close this issue.
OK, if you say that's out of scope, we need to accept that.
@mquandalle Did you manage to get your approach (multiple routes
folders and switching them with an env variable) working with the VSCode Intellisense? I've just tried it and ran into a lot of problems. When having a single .svelte-kit
directory, multiple processes try to write to the same file (I need to have all apps running when developing).
When having different .svelte-kit
folders, the Intellisense doesn't like that, as the tsconfig.json
at the root needs to point to one of the generated tsconfig.json
s explicitely...
@Rich-Harris is there an example of a monorepo available? I'm struggeling with some points. For example: a .env
file at the workspace root is not parsed for $env/static/private
. Copying the .env
file to all the child packages seems kinda redundant...
Edit: Seems like even copying does not work if lib
is a folder in the workspace (with shared logic). I guess I need to manually load the .env
file for shared logic.
Edit2: Seems like I can't overwrite $lib
in the alias
config. I can use something like $myLib
to point to the shared lib folder, but then I loose the svelte kit warning in case I'd import $lib/server
in client-facing code... That's a bummer
Hi @hrueger - please use discord (https://svelte.dev/chat) for usage questions, where people will be better able to discuss and help with your specific requirements.
Discord is fun, but it's hard to find things on it and it's closed (you need a login). I always find it much better if there's a proper documentation on publicly available sites like github for issues like these that many people might want to google for.
While the debate is settled, I was exploring this as I had a small personal use.
@hrueger How about handling subdomain via hooks.
My usecase was very specific and personal. This code also handles just that. It's not generic and I do not recommend you use this for production systems without due diligence.
// src/hooks.ts
import { redirect } from '@sveltejs/kit'
import type { Handle } from '@sveltejs/kit'
export const handle: Handle = async ({ event, resolve }) => {
const host = event.request.headers.get('host')
const url = new URL(event.request.url)
// Check if the request is from a subdomain in both localhost and production
if (host && url.pathname.slice(0, 5) !== '/help') {
// Assuming production domain is 'example.com'
if ((host !== 'localhost:3000' && host.endsWith('localhost')) ||
(host !== 'example.com' && host.endsWith('support.example.com'))) {
// Redirect to '/help' when accessed via any subdomain
throw redirect(307, '/help');
}
}
// Continue with normal flow if no subdomain
return resolve(event);
};
This is super easy to do with the reroute hook:
I'm planning on using it for the "each user has their own subdomain" use-case.
This is super easy to do with the reroute hook:
- ramonmalcolm.com/articles/dynamic-domain-subdomain-routing-sveltekit-guide
- kit.svelte.dev/docs/hooks#universal-hooks-reroute
I'm planning on using it for the "each user has their own subdomain" use-case.
This is excellent, but I wish if we could've accessed DB in this hook. Every reroute requires you to know them ahead of time, we can't do anything dynamic. Especially, looking up custom domains is not at all possible.
How do you implement Vercel Platforms like application in SvelteKit now? Whatever examples I saw are not what I'm looking at.
You can see how I do it in my app here if you want: https://github.com/muni-town/weird/blob/main/src/hooks.ts
You don't need to access the database in that hook. What you do is you check if it's a subdomain, and then you route to a subdomain specific route in your app.
Then when it gets to that route, your route checks the database to see whether or not that subdomain exists in the database or should return a "page not found" because it's not there.
@zicklag This is true, and a wonderful growth hack to get users to create a profile under your subdomain. It's perfect, but imagine you gave your users the ability to point their custom domain, now unless we know that it came from a specific domain that is valid (by checking the DB), we shouldn't rewrite them to the path. Given what you have already implemented, how will you go about adding custom domain into your product.
I actually have custom domains working, too!
Basically you could do something like:
hooks.ts
we check if the url.host == env.PUBLIC_DOMAIN
, if so, we just return the route unchanged.\
/subsite/${subdomain}``\
/subsite/${url.host}``Then, in our /subsite/[usernameOrCustomDomain]
route we check the database to see if usernameOrCustomDomain
is a username in our database. If it is, we render a subdomain site from the user's data.
If usernameOrCustomDomain
isn't a username, we look in our database for a registered custom domain, and we render the user's site that has that custom domain set.
If there is no matching sub-domain, we just return a 404.
I also some stuff in there, when the user is configuring their custom domain, it does a little DNS "challenge" to make sure that the domain they are inputting is actually resolving to to the app server, but that's just around how you set the domain, not how you do that routing.
There's also some stuff in there to generate a Traefik reverse proxy configuration for each custom domain, so that we can provide automatic HTTPS certificates for everything. But again, that's kind of unrelated to the routing.
The code is running at https://weird.one/members.
And you can see a subdomain site: https://zicklag.weird.one And a custom domain site: https://erlend.sh
@zicklag dude this is genius. Dayum
Another solution could be to have a separation at the configuration level. You can create a separate config file for each sub-app and specify parameters such as files
, appDir
, etc in there. Then you create a separate deployment/development scripts for each app.
Another solution could be to have a separation at the configuration level. You can create a separate config file for each sub-app and specify parameters such as
files
,appDir
, etc in there. Then you create a separate deployment/development scripts for each app.
Apparently separate config file are not supported. The approach is still valid though by using command args and detecting them within the config file as it is shown in this example: https://github.com/sveltejs/kit/issues/2973#issuecomment-986050439
Describe the problem
Hi, first of all, thanks for this excellent piece of software! I truely love it.
I'm currently building a platform where I have multiple tenants. They can configure some settings and provide a frontend for their users. This should live on
https://tenant-slug.my-platform.tld
. Then, they should also have an admin-dashboard athttps://admin.tenant-slug.my-platform.tld
. I'll also have some registration / onboarding ui athttps://registration.my-platform.tld
.Currently, this does not seem possible with a single Svelte app. I can't build one for each tenant, because they change quite often.
At DNS level it would be solved via a wildcard subdomain and a wildcard SSL certificate.
Describe the proposed solution
As proxies (see below) are complicated to setup and didn't work for my usecase, I'd like to be able to have different routes for each subdomain and the pure domain. Maybe this is kind-of linked: https://vercel.com/guides/nextjs-multi-tenant-application
Here are my thoughts: Currently, there's the
routes
directory with all the routes inside. In order to support multiple (sub)domains, there could be multipleroutes
folders likeroutes_registration.
,routes_[tenant].
androutes_admin.[tenant].
. Notice the dot at the end, which means that it is a subdomain. Without the dot, we could support thinks likesite-with-german-title.de
andenglish-site.co.uk
as then, it would match against the whole host.The only thing I don't like about this, however, is the fact that we're breaking the convention, that each folder name equals one segment of the path exactly (as we add the
routes_
prefix. One way to work around that would be to have a new folder calleddomains
in thesrc
directory, and inside there other folders with the names / wildcards of the subdomains. However, we'd then need a way to split that part from where the actual path-based routing begins. This could be done with a convention (like a folder named_routes
in there), however, that would prevent people from having a_routes
subdomain.Please let me know what you think about that, I'm happy to hear other opinions!
Alternatives considered
What I've tried so far is using a reverse proxy (traefik in this case) to map the subdomains to path prefixes (e.g.
registration.my-platform.tld
tomy-platform.tld/registration
internally. This, however, does not work, as the base path is always wrong in that case. Maybe this is related to #595, but I don't think its the same case as with that proxy it would need a multiple different base paths (or rather hosts).Importance
would make my life easier
Additional Information
This feature is rather important for my application, as there is the requirement for nice urls using subdomains.
I'm also happy to try implementing something once we have a proposed solution and someone can point me in the right direction.