Skip to content
Docs Try Aspire
Docs Try

Node.js Express + Redis + Vite Frontend

Aspire sample
TypeScript AppHost

Clone, run, and explore this sample

Visit counter with Express backend, Redis caching, and React TypeScript frontend using YARP.

DockerJavaScriptNode.jsRedisTypeScript
AppHost

The entry point that composes every resource and dependency in this sample's distributed application.

View on GitHub
apphost.mts
import { createBuilder } from "./.aspire/modules/aspire.mjs";
const builder = await createBuilder();
const executionContext = await builder.executionContext();
const dc = await builder.addDockerComposeEnvironment("dc");
const redis = await builder.addRedis("redis")
.withRedisInsight()
.withComputeEnvironment(dc);
const api = await builder.addNodeApp("api", "./api", "index.js")
.withHttpEndpoint({ env: "PORT" })
.withHttpHealthCheck({ path: "/health" })
.withComputeEnvironment(dc)
.waitFor(redis)
.withReference(redis);
const frontend = await builder.addViteApp("frontend", "./frontend")
.withReference(api);
await builder.addYarp("app")
.withConfiguration(async (yarp) =>
{
const apiCluster = await yarp.addClusterFromResource(api);
await (await yarp.addRoute("api/{**catch-all}", apiCluster))
.withTransformPathRemovePrefix("/api");
if (await executionContext.isRunMode())
{
const frontendCluster = await yarp.addClusterFromResource(frontend);
await yarp.addRoute("{**catch-all}", frontendCluster);
}
})
.withExternalHttpEndpoints()
.withBrowserLogs()
.publishWithStaticFiles(frontend)
.withComputeEnvironment(dc)
.withExplicitStart();
await builder.build().run();

Visit counter with Express backend, Redis caching, and React TypeScript frontend using YARP.

Run Mode:

flowchart LR
    Browser --> YARP
    YARP -->|/api/*| Express[Express API]
    YARP --> Vite[Vite Dev Server<br/>HMR enabled]
    Express --> Redis

Publish Mode:

flowchart LR
    Browser --> YARP[YARP serving<br/>Vite build output<br/>'npm run build']
    YARP -->|/api/*| Express[Express API]
    Express --> Redis
  • addNodeApp: Express backend with Redis integration
  • addViteApp: React + TypeScript frontend with Vite
  • addYarp: Single endpoint for frontend and API with path transforms
  • addRedis: In-memory data store with automatic connection injection
  • publishWithStaticFiles: Frontend embedded in YARP for publish mode
  • Dual-Mode Operation: Vite HMR in run mode, Vite build output in publish mode
Terminal window
aspire run

This sample keeps the Express endpoints public and unauthenticated so the visit counter is easy to run and inspect locally. It does not add CSRF protection, rate limiting, authentication, or authorization, so treat it as a demo pattern rather than production-ready API security.

The API accepts visits only for the sample's built-in pages and uses bounded Redis operations for stats and reset. Production services still need deliberate cache and key management to avoid unbounded key cardinality, accidental deletion of unrelated keys, or expensive enumeration against shared Redis instances.

For production applications, add real authN/authZ, request rate limiting, CSRF protection where browser credentials are used, bounded request bodies, and Redis security controls appropriate for your deployment. See the Node.js security best practices, Express body parser limits documentation, Redis security documentation, and OWASP API Security Top 10.

Terminal window
aspire run # Run locally
aspire deploy # Deploy to Docker Compose
aspire do docker-compose-down-dc # Teardown deployment

YARP Routing - Single endpoint with path-based routing:

await builder.addYarp("app")
.withConfiguration(async (yarp) =>
{
const apiCluster = await yarp.addClusterFromResource(api);
await (await yarp.addRoute("api/{**catch-all}", apiCluster))
.withTransformPathRemovePrefix("/api");
if (await executionContext.isRunMode.get())
{
const frontendCluster = await yarp.addClusterFromResource(frontend);
await yarp.addRoute("{**catch-all}", frontendCluster);
}
})
.publishWithStaticFiles(frontend);

Redis Connection - Automatic connection string injection via REDIS_URI environment variable

WaitFor - Ensures Redis starts before API

Preview

Sample screenshots

Select the image to zoom in.

Screenshot of the visit counter UI for the Node.js Express + Redis + Vite sample
Screenshot of the visit counter UI for the Node.js Express + Redis + Vite sample