-
-
Notifications
You must be signed in to change notification settings - Fork 0
Routing SSR And Server APIs
Status: Client routing and layer server APIs are current. A unified route manifest is planned in #218.
const routes = defineRoutes([
{ path: '/', name: 'home', component: HomePage },
{ path: '/users/[id]', name: 'user', component: UserPage },
{ path: '/docs/[...slug]', name: 'docs', component: DocsPage },
{ path: '/shop/[[...slug]]', name: 'shop', component: ShopPage },
] as const);
const router = createRouter({
history: createWebHistory(),
routes,
});
installRouter(router);Supported path forms include:
| Form | Meaning |
|---|---|
:id |
Colon-style dynamic segment. |
[id] |
Bracket dynamic segment. |
[...slug] |
Required catch-all. |
[[...slug]] |
Optional catch-all. |
(group) |
Organizational segment omitted from the URL. |
Route groups remain available in normalized route metadata for architecture and policy decisions. Matching uses deterministic specificity and validates conflicting normalized paths.
Use Link for declarative navigation and RouterView for the matched route
component. useRoute() exposes path, full path, params, query, hash, matched
records, name, groups, pattern, and merged metadata. useRouter() exposes
navigation, resolution, guards, and dynamic route operations.
A parent component that owns child routes must render a nested RouterView.
Parent and child components have independent lifecycle and route identity.
const UserLayer = defineLayer({
name: 'users',
services: {
users: () => ({ find: (id: string) => ({ id, name: 'Chris' }) }),
},
server: {
api: {
'/api/users/[id]': {
GET: ({ params, services }) => services.users.find(params.id),
},
},
actions: {
refreshUser: ({ services }) => services.users.find('u1'),
},
},
});Server routes are matched before SSR fallback. Handlers receive route params, query/body helpers, services, validation, response helpers, and request context.
fromServerFiles converts an imported file map into layer server configuration.
Default roots include API directories and action directories such as
src/server/actions, app/actions, and src/actions. Files can export HTTP
methods, default handlers, named actions, middleware, validation, and metadata.
import type { ServerApiFileModule, ServerActionFileModule } from '@effuse/core';
import { defineLayer, fromServerFiles } from '@effuse/core';
const files = import.meta.glob<
ServerApiFileModule | ServerActionFileModule
>(['/src/server/api/**/*.ts', '/src/server/actions/**/*.ts'], { eager: true });
export const AppServerLayer = defineLayer({
name: 'app-server',
server: fromServerFiles(files),
});A file at src/server/api/users/[id]/route.ts can export method handlers:
export const GET = ({ params }) => ({ id: params.id });
export const POST = async ({ json, params }) => ({
id: params.id,
input: await json(),
});The same adapter recognizes Next-style app/api and app/actions roots. Route
groups are removed from URLs while bracket params retain their runtime names.
This is an adapter into the layer server model, not a second runtime.
Handlers can validate JSON, form data, headers, params, query values, or an
arbitrary value. Validators may be functions or objects with parse or
safeParse methods.
const parseUser = (value: unknown): { name: string } => {
if (!value || typeof value !== 'object' || !('name' in value)) {
throw new Error('name is required');
}
return { name: String(value.name) };
};
const createUser = async ({ validate }) => {
const input = await validate.json(parseUser);
return { id: crypto.randomUUID(), ...input };
};Validation failures become a 400 response with the stable
EFFUSE_VALIDATION_FAILED code, source, message, and normalized issues.
createServerApp and createHandler build a request handler from a root
component and layer graph. The SSR runtime supports rendered HTML, head
collection, hydration data, asset manifests, API/action dispatch, and cleanup.
createStreamingHandler exists as an experimental streaming path.
Head and SEO primitives exist, including useSeoMeta, server SEO collection,
head merging, Open Graph, Twitter metadata, links, and scripts. Full route-tree
metadata generation and merge policy remain part of #218.