Skip to main content

Real Life Example

A complete example showing how to use all Alt-stack packages together in a production-like setup.

Overview

This example demonstrates a task management system with:

  • 2 Backend Services (Hono): Authentication and business logic
  • Background Workers (WarpStream): Notifications and report generation
  • NextJS Frontend: Consumes both services via generated SDKs
  • Type-safe SDKs: Generated from OpenAPI/AsyncAPI specs

Architecture

┌─────────────────────────────────────────────────────────────────┐
│ NextJS Web App │
│ (uses http-client-ky + generated SDKs) │
└───────────────────────────────┬─────────────────────────────────┘

┌───────────┴───────────┐
│ │
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ backend-auth │ │ backend-logic │
│ (Hono server) │ │ (Hono server) │
│ │ │ │
│ - POST /signup │◄──│ validates tokens │
│ - POST /login │ │ │
│ - POST /logout │ │ - GET/POST /tasks │
│ - GET /me │ │ - PUT/DELETE /{id}│
│ - GET /validate │ │ │
└───────────────────┘ └─────────┬─────────┘

│ triggers jobs

┌───────────────────┐
│ WarpStream │
│ Workers │
│ │
│ - send-notification│
│ - generate-report │
└───────────────────┘

Key Takeaways

1. End-to-End Type Safety

Types flow from Zod schemas through OpenAPI/AsyncAPI specs to generated SDKs:

// Define once in backend-logic
const TaskSchema = z.object({
id: z.string(),
title: z.string(),
status: z.enum(["pending", "in_progress", "completed"]),
});

// Automatically available in frontend via SDK
import type { TaskSchema } from "@real-life/backend-logic-sdk";
type Task = z.infer<typeof TaskSchema>;

2. Service-to-Service Auth

The logic service validates tokens by calling the auth service's internal endpoint:

// backend-logic calls backend-auth
async function validateToken(token: string): Promise<string | null> {
const res = await ky.get(`${AUTH_SERVICE_URL}/api/validate`, {
headers: { authorization: token },
}).json<{ valid: boolean; userId?: string }>();
return res.valid ? res.userId ?? null : null;
}

3. Protected Procedures with Middleware

Create reusable authenticated procedures using the Result pattern:

import { err, TaggedError } from "@alt-stack/server-hono";

class UnauthorizedError extends TaggedError {
readonly _tag = "UnauthorizedError" as const;
constructor(public readonly message: string = "Authentication required") {
super(message);
}
}

const UnauthorizedErrorSchema = z.object({
_tag: z.literal("UnauthorizedError"),
message: z.string(),
});

const protectedProc = factory.procedure
.errors({ 401: UnauthorizedErrorSchema })
.use(async ({ ctx, next }) => {
if (!ctx.userId) return err(new UnauthorizedError());
return next({ ctx: { userId: ctx.userId } }); // narrow type
});

4. Type-Safe Background Jobs

Trigger workers with full type inference from the SDK:

import { Topics } from "@real-life/workers-sdk";

const client = await createWarpStreamClient({ jobs: Topics, ... });

// TypeScript knows the exact payload shape
await client.trigger("send-notification", {
type: "task_created", // must be valid enum
userId: ctx.userId,
taskId: id,
taskTitle: input.body.title,
});

5. Built-in Telemetry

Enable OpenTelemetry tracing with a single flag:

const app = createServer(
{ api: taskRouter, docs: docsRouter },
{
createContext,
telemetry: {
enabled: env.NODE_ENV === "production",
serviceName: "backend-logic",
ignoreRoutes: ["/docs"],
},
},
);

6. SDK-First Frontend

The frontend uses generated SDKs with http-client-ky for fully typed API calls:

import { createApiClient } from "@alt-stack/http-client-ky";
import { Request, Response } from "@real-life/backend-logic-sdk";

const client = createApiClient({ baseUrl, Request, Response });

// Full autocomplete for paths, params, body, and response
const result = await client.get("/api/{id}", {
params: { id: "..." },
headers: { authorization: `Bearer ${token}` },
});
if (result.success) {
console.log(result.body.title); // TypeScript knows this exists
}

What You'll Learn

TopicDescription
Project StructureMonorepo layout and package organization
Auth ServiceBuilding the authentication service
Logic ServiceBusiness logic with auth integration
WorkersBackground job processing with WarpStream
FrontendNextJS app with SDK consumption
SDK GenerationGenerating and using type-safe SDKs

Quick Start

# Clone and navigate to example
cd examples/real-life

# Install dependencies
pnpm install

# Generate SDKs
pnpm generate:all

# Start all services (in separate terminals)
pnpm --filter @real-life/backend-auth dev # Port 3001
pnpm --filter @real-life/backend-logic dev # Port 3002
pnpm --filter @real-life/workers dev
pnpm --filter @real-life/web dev # Port 3000

Technologies Used

PackageTechnologyPurpose
@alt-stack/server-honoHonoType-safe HTTP servers with telemetry
@alt-stack/workers-warpstreamWarpStream/KafkaBackground job processing
@alt-stack/workers-client-warpstreamWarpStream/KafkaJob triggering
@alt-stack/zod-openapiOpenAPIREST API SDK generation
@alt-stack/zod-asyncapiAsyncAPIWorker SDK generation
@alt-stack/http-client-kykyType-safe HTTP client with SDK integration
@opentelemetry/apiOpenTelemetryDistributed tracing (optional)
Next.jsReactFrontend framework