@neuralangular/angular-ssr
v0.2.0
Published
Angular-first SSR, hydration, route rules, cache policy and request context helpers for Neural Angular.
Maintainers
Readme
@neuralangular/angular-ssr
Angular-first SSR, hydration, route rules, cache policy, and request context helpers for Neural Angular.
This package is intentionally thin. It builds on Angular's official server rendering APIs instead of replacing them, then adds typed conventions for the repetitive pieces that production SSR apps usually need: hydration setup, route declarations, cache headers, host validation, request context, and test helpers.
Status
@neuralangular/angular-ssr is an early package. The current package version is 0.2.0, and the public surface is small and focused:
- Client hydration helpers.
- Server rendering provider composition.
- Typed server route helpers.
- Cache policy presets and header serialization.
- Request context creation and DI.
- Testing utilities for request and cache assertions.
The UI package does not depend on this package.
Version 0.2 adds request-scoped runtime configuration for serverless and
multi-tenant deployments. Provider-specific deployment output remains outside
this package in @neuralangular/angular-adapters.
Installation
pnpm add @neuralangular/angular-ssrPeer dependencies:
@angular/common^22.0.0@angular/core^22.0.0@angular/platform-browser^22.0.0@angular/ssr^22.0.0
The package declares Node >=22.12.0.
Entry Points
import {
defineNeuralServerRoutes,
neuralRoute,
provideNeuralHydration,
provideNeuralServerRuntime,
} from '@neuralangular/angular-ssr';
import {
cachePolicyToHeaders,
neuralCache,
serializeCacheControl,
} from '@neuralangular/angular-ssr/cache';
import {
createNeuralMockRequest,
createNeuralMockRequestContext,
expectSafePrivateCache,
} from '@neuralangular/angular-ssr/testing';Inside this repository, Nx project names and TypeScript path aliases use @neural/angular-ssr. Published application code should use @neuralangular/angular-ssr.
Client Hydration
provideNeuralHydration wraps Angular's provideClientHydration and gives you a compact configuration for event replay, incremental hydration behavior, and HTTP Transfer Cache filtering.
import { provideNeuralHydration } from '@neuralangular/angular-ssr';
export const appConfig = {
providers: [
provideNeuralHydration({
eventReplay: true,
incremental: true,
transferCache: {
disabled: false,
includeAuthenticatedRequests: false,
includePostRequests: false,
includeHeaders: ['cache-control', 'etag'],
},
}),
],
};Defaults are conservative:
- Event replay is enabled only when
eventReplay: trueis passed. - Incremental hydration remains enabled unless
incremental: falseis passed. - Authenticated requests are not included in Transfer Cache unless explicitly enabled.
- Transfer Cache headers are sanitized to a small allowlist and sensitive headers are denylisted.
Server Rendering
Use Angular's official provideServerRendering(withRoutes(serverRoutes)) directly, then add Neural runtime providers next to it. Keeping withRoutes() visible lets Angular's hybrid build discover routes correctly.
import { provideServerRendering, withRoutes } from '@angular/ssr';
import { provideNeuralServerRuntime } from '@neuralangular/angular-ssr';
import { serverRoutes } from './app.routes.server';
export const serverConfig = {
providers: [
provideServerRendering(withRoutes(serverRoutes)),
provideNeuralServerRuntime({
allowedHosts: ['example.com'],
trustProxyHeaders: false,
canonicalOrigin: 'https://example.com',
}),
],
};For serverless environments, pass a factory. It runs independently for every Angular render request:
provideNeuralServerRuntime((request) => ({
allowedHosts: [new URL(request.url).host],
canonicalOrigin: process.env['NEURAL_CANONICAL_ORIGIN'],
}));For Vercel Build Output API generation and Node Function helpers, install
@neuralangular/angular-adapters and import its /vercel entry point.
provideNeuralServerRendering({ routes }) remains available for compatibility with 0.0.1, but it is deprecated. New applications should not use it.
Server Routes
defineNeuralServerRoutes and neuralRoute create Angular ServerRoute objects with cache/header helpers and validation.
import {
defineNeuralServerRoutes,
neuralRoute,
} from '@neuralangular/angular-ssr';
export const serverRoutes = defineNeuralServerRoutes([
neuralRoute.prerender(''),
neuralRoute.server('products/:id', {
cache: 'public-swr',
headers: {
'X-App-Route': 'product-detail',
},
}),
neuralRoute.client('dashboard/**', {
cache: 'private-no-store',
}),
neuralRoute.prerender('blog/:slug', {
fallback: 'server',
getPrerenderParams: async () => [
{ slug: 'getting-started' },
{ slug: 'theme-tokens' },
],
}),
]);Route validation currently catches:
- Leading slash compatibility.
/products/:idandproducts/:idproduce the same Angular route path. - Duplicate routes with the same path and render mode.
- Invalid wildcard patterns. A wildcard must be exactly
**or end with/**. - Absolute URLs, query strings, fragments, double slashes, and control characters.
- Prerender routes that try to define an HTTP status.
- Prerender fallback usage without
getPrerenderParams.
Cache Policies
The cache entry point exposes reusable presets and conversion helpers.
import {
cachePolicyToHeaders,
neuralCache,
} from '@neuralangular/angular-ssr/cache';
const swrHeaders = cachePolicyToHeaders('public-swr');
const privateHeaders = neuralCache.withHeaders({
visibility: 'private',
noStore: true,
});Built-in presets:
| Preset | Intended use |
| -------------------- | ------------------------------------------------------------------ |
| public-static | Mostly static public pages. |
| public-short | Public dynamic content that can be cached briefly. |
| public-swr | Public SSR content that should respond quickly while revalidating. |
| private-revalidate | Private user content that must revalidate. |
| private-no-store | Sensitive private data that should not be cached. |
| immutable-asset | Fingerprinted static assets. |
immutable-asset is retained in the shared preset union for compatibility, but it is intended for versioned assets, not HTML routes. Page route examples should use public-static, public-short, public-swr, private-revalidate, or private-no-store.
The lower-level API is also available:
import { serializeCacheControl } from '@neuralangular/angular-ssr/cache';
const header = serializeCacheControl({
visibility: 'public',
maxAge: 60,
sharedMaxAge: 300,
staleWhileRevalidate: 900,
});Contradictory policies fail early, including public no-store, private s-maxage, positive max-age with no-store, must-revalidate with immutable, and public Vary: Cookie without explicit opt-in.
ETag helpers are deterministic and body-based:
import { createNeuralEtag } from '@neuralangular/angular-ssr/cache';
const etag = await createNeuralEtag(html, {
strength: 'weak',
});cachePolicyToHeaders() serializes provided etag and lastModified values, but it does not invent them. Last-Modified should come from real resource metadata.
Request Context
Request context utilities turn a Fetch Request into an Angular-injectable value.
import {
NEURAL_REQUEST_CONTEXT,
createNeuralRequestContext,
injectNeuralRequestContext,
injectRequiredNeuralRequestContext,
provideNeuralRequestContext,
} from '@neuralangular/angular-ssr';
const context = createNeuralRequestContext({
request,
allowedHosts: ['example.com'],
canonicalOrigin: 'https://example.com',
trustProxyHeaders: true,
});
export const requestProviders = [provideNeuralRequestContext(context)];In universal code, use the nullable helper:
import { injectNeuralRequestContext } from '@neuralangular/angular-ssr';
const requestContext = injectNeuralRequestContext();In server-only code, use the required helper:
import { injectRequiredNeuralRequestContext } from '@neuralangular/angular-ssr';
const requestContext = injectRequiredNeuralRequestContext();The context includes id, url, origin, host, protocol, optional locale, and trustedProxy. In the browser, NEURAL_REQUEST_CONTEXT resolves to null.
Proxy headers are ignored by default. Set trustProxyHeaders only behind a trusted proxy, preferably with an explicit header policy.
Transfer Cache Safety
Transfer Cache header inclusion is allowlisted and denylisted. These headers can never be transferred, regardless of configuration:
authorizationproxy-authorizationcookieset-cookiex-api-keyx-auth-token
Requests with credentials or auth-like request headers are excluded unless authenticated requests are explicitly enabled.
Migration From 0.0.1
Replace this deprecated pattern:
provideNeuralServerRendering({
routes: serverRoutes,
});with:
provideServerRendering(withRoutes(serverRoutes));
provideNeuralServerRuntime({
allowedHosts: ['example.com'],
trustProxyHeaders: false,
canonicalOrigin: 'https://example.com',
});Also prefer route paths without a leading slash. Leading slash input remains supported for compatibility.
Testing
The testing entry point provides small helpers for package and app tests:
import {
createNeuralMockRequest,
createNeuralMockRequestContext,
expectSafePrivateCache,
} from '@neuralangular/angular-ssr/testing';
const request = createNeuralMockRequest('https://example.test/dashboard');
const context = createNeuralMockRequestContext(
'https://example.test/dashboard',
);
expectSafePrivateCache({
visibility: 'private',
noStore: true,
});Workspace Commands
From the repository root:
pnpm nx run @neural/angular-ssr:build
pnpm nx run @neural/angular-ssr:typecheck
pnpm nx run @neural/angular-ssr:test
pnpm nx run @neural/angular-ssr:lint
pnpm nx run @neural/angular-ssr:api-check
pnpm nx run @neural/angular-ssr:external-check
pnpm nx run angular-ssr:build
pnpm nx run angular-ssr:e2eDesign Notes
- The package should stay Angular-native and avoid replacing Angular SSR.
- Browser-only work belongs behind Angular platform checks or client-only lifecycle boundaries.
- Cache helpers should serialize predictable HTTP headers and fail early on unsafe combinations.
- UI must remain independent from SSR.
- Future meta-framework features should build on this package, not be required by it.
