Skip to content

Commit 44fa57d

Browse files
Sync docs from wheels@b9b70ff
1 parent be7e14b commit 44fa57d

File tree

4 files changed

+444
-0
lines changed

4 files changed

+444
-0
lines changed

docs/3.1.0/guides/database-interaction-through-models/using-multiple-data-sources.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,20 @@ Because the `photo` model is the main model being used in the following example,
6464
myPhotos = model("photo").findAll(include="photoGalleries");
6565
```
6666
{% endcode %}
67+
68+
### Automatic Datasource Switching with Multi-Tenancy
69+
70+
If you're building a multi-tenant application where each tenant has its own database, Wheels can automatically switch the datasource on every request — no manual `dataSource()` calls needed.
71+
72+
When the **TenantResolver** middleware is active, all model queries are automatically routed to the current tenant's datasource. Models that should always use the central (default) datasource — like a `Tenant` registry table — can opt out by calling `sharedModel()` in their `config()`:
73+
74+
```javascript
75+
// app/models/Tenant.cfc — always uses the default datasource
76+
component extends="Model" {
77+
function config() {
78+
sharedModel();
79+
}
80+
}
81+
```
82+
83+
For the full setup guide, see [Multi-Tenancy](../working-with-wheels/multi-tenancy.md).

docs/3.1.0/guides/handling-requests-with-controllers/middleware.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,32 @@ set(middleware = [
153153

154154
For simple CORS needs, you may prefer the existing [CORS Requests](cors-requests.md) guide which covers header-only approaches.
155155

156+
### TenantResolver
157+
158+
Resolves the current tenant from the incoming request and sets `request.wheels.tenant` for automatic per-request datasource switching. Supports three resolution strategies.
159+
160+
| Parameter | Default | Description |
161+
|-----------|---------|-------------|
162+
| `resolver` | `""` | Closure that receives the request struct and returns a tenant struct (`{id, dataSource, config}`). Return `{}` for unrecognized tenants. |
163+
| `strategy` | `"custom"` | Resolution strategy: `"custom"`, `"header"`, or `"subdomain"` |
164+
| `headerName` | `"X-Tenant-ID"` | HTTP header to read when strategy is `"header"` |
165+
166+
```javascript
167+
set(middleware = [
168+
new wheels.middleware.TenantResolver(
169+
resolver = function(req) {
170+
var t = model("Tenant").findOne(where="domain='#cgi.server_name#'");
171+
if (IsObject(t)) return {id: t.id, dataSource: t.dsName};
172+
return {};
173+
}
174+
)
175+
]);
176+
```
177+
178+
The middleware locks the tenant for the duration of the request and cleans up automatically. All model queries are transparently routed to the tenant's datasource (except models marked with `sharedModel()`).
179+
180+
For the full multi-tenancy guide — including shared models, tenant migrations, and background jobs — see [Multi-Tenancy](../working-with-wheels/multi-tenancy.md).
181+
156182
## Writing Custom Middleware
157183

158184
Create a CFC that implements `wheels.middleware.MiddlewareInterface`:

docs/3.1.0/guides/working-with-wheels/background-jobs.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,22 @@ The formula is `baseDelay * 2^attempt`, capped at `maxDelay`:
346346
| 5th retry | 64s | 320s |
347347
| 6th retry | 128s | 600s (capped) |
348348

349+
## Multi-Tenant Jobs
350+
351+
If you're using [multi-tenancy](multi-tenancy.md), background jobs automatically capture the current tenant context when enqueued and restore it before `perform()` runs. No extra code is needed.
352+
353+
```javascript
354+
// Enqueued during a tenant request — context is captured automatically
355+
job = new app.jobs.GenerateInvoiceJob();
356+
job.enqueue(data={userId: user.id});
357+
```
358+
359+
When the job processes (possibly later, on a different server, or outside a web request), Wheels restores the original tenant context so all model queries inside `perform()` run against the correct tenant database.
360+
361+
{% hint style="info" %}
362+
The tenant context is stored internally as `$wheelsTenantContext` in the job's data. It's automatically removed before your `perform()` method receives the data struct — you don't need to handle it yourself.
363+
{% endhint %}
364+
349365
## Best Practices
350366

351367
1. **Keep jobs small and focused**: Each job should do one thing. Chain multiple jobs for complex workflows.

0 commit comments

Comments
 (0)