Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
g3-kit
The shared core of g3 stack apps: the infrastructure every Dioxus fullstack app on the stack needs, written once and fixed once, behind feature flags.
| Area | Feature | What it gives you |
|---|---|---|
| Caching | cache |
use_cached on the device, #[cache_shared] on the server and CDN, invalidate_cached |
| Auth | auth |
Sessions in SurrealDB, the signed-in user on every request, a deny-by-default guard, and #[public] for what a signed-out visitor may call |
Setup
Turn on the areas the app uses on the dependency itself, and each platform feature in the app's feature of the same name:
[]
= { = "0.1", = ["cache", "auth"] }
[]
= ["dioxus/web", "g3-kit/web"] # client cache persists to IndexedDB
= ["dioxus/mobile", "g3-kit/mobile"] # client cache persists to a redb file
= ["dioxus/server", "g3-kit/server"] # server halves of every area; client cache off
Caching
use ;
// A screen: show the last known answer at once, refetch in the background.
let media = use_cached;
// After a mutation: refetch what it changed.
save_rating.await?;
invalidate_cached;
// A public read: cached at the CDN and on the server for 5 minutes.
pub async
Choosing a cache
| Client | Server | CDN | |
|---|---|---|---|
| For | Showing the last known data at once | Not repeating slow or rate-limited work | Answering identical public requests without the server |
| Holds | One user's data, on one device | Shared answers, in one process | Whole shared HTTP responses |
| Refreshed by | Screen opens, invalidate_cached, app focus |
Expiry only | Expiry only |
| API | use_cached, use_cached_key |
#[cache_shared(server = ..)], ServerCache |
#[cache_shared(cdn = ..)], cdn_cache_guard |
The rule: data that depends on who is asking is cached on the client
only. Data that is the same for everyone may also be cached on the server
and at the CDN. #[cache_shared] refuses, at compile time, functions that
bind a session, auth, user or cookie extractor, and anything that isn't a
GET.
For caching part of a server function, declare a cache where it is used:
static DESCRIPTIONS: =
new;
DESCRIPTIONS.get_or_fetch.await
In the app:
use ;
// Server router: once, outside the session layer.
.layer
.layer
Only standard Cache-Control headers are sent (public, s-maxage,
private, no-cache), so any CDN or shared cache works. Most won't cache
API responses until told to (in Cloudflare, a Cache Rule making the API
paths eligible); until then nothing is cached at the CDN, which is safe.
What you still have to handle:
- Invalidate after mutations. Only the mutation knows which reads it changed.
- Write mutations as "set to", not "toggle". A device showing stale state sends what it saw; a toggle then undoes another device's change.
- Only share functions whose answer comes from their arguments. The compile-time check catches session-like extractors, not every way a function can read the visitor.
Auth
Every request needs a signed-in user unless it is for a static asset, or a
page or server function marked #[public]:
/// The splash asks this before it knows whether anyone is signed in.
pub async
Pages are marked on the route enum, next to #[route]:
Forgetting #[public] is the safe mistake: a signed-out caller gets a 401
JSON body the client can decode, and a signed-out page load is redirected
to the splash. Opening an endpoint up is one reviewable line next to the
function or page, not an edit to a list somewhere else.
auth::public_endpoints() and Route::PUBLIC_PATTERNS list them all, for
pinning in a test.
Setup, innermost layer first:
use ;
// table `user`, name field `display_name`
pub type SessionContext = SessionContext;
// Panics at startup if the splash isn't `#[public]`: the redirect would loop.
let guard = for_routes;
router
.layer
.layer
.layer
.layer
Sessions live in SurrealDB through SurrealSessionPool; load
auth::SESSIONS_SCHEMA for its table. Mark the session cookie Secure in
production: SessionConfig::default().with_secure(!cfg!(debug_assertions)).
The derive matches request paths against the marked routes' own patterns and
never parses a path into the enum: a catch-all
#[redirect("/:..segments", ..)] parses every path, including every /api/
one, as its target, so "parses as the splash" would open the whole app. For
the same reason it refuses #[public] on a top-level catch-all route.
A server function called during server-side rendering runs without any
middleware, so the guard covers HTTP requests only. Functions acting on
"the current user" should still check session_user.anonymous.
Test bed
testbed/ is a small Dioxus app using the cache API against real server
functions. just testbed builds it for the server and the browser.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.