What You’ll Build
Create a color manager model that automatically fetches a baseline color from a device API endpoint when the app loads, then displays it in the UI. This tutorial demonstrates the simplest form of API integration: lifecycle-based service requests that run automatically during model initialization.
Prerequisites
- Completed the “Getting Started with KOS Models” tutorial
- Development server running:
npm run dev(recommended)- OR
kosui dev(CLI shorthand)
- Chrome DevTools Console tab open
Step 1: Generate Service Types from OpenAPI
Before making API calls, generate TypeScript types and service wrappers from your device’s OpenAPI specification:
kosui api:generate --project my-first-kos-app-models[Screenshot: Terminal showing kosui api:generate command output with list of generated service files]
This command:
- Connects to your device (default: http://127.0.0.1:8081)
- Downloads the OpenAPI specification
- Generates TypeScript types for all endpoints
- Creates service wrapper functions with error-first tuple pattern
- Organizes files by app namespace (kos, dispense, etc.)
Generated files:
utils/services/kos/v1/openapi.d.ts– Type definitionsutils/services/kos/v1/service.ts– Service functionsutils/services-index.ts– Convenient exports
[Screenshot: VS Code file explorer showing the utils/services folder structure with the generated files]
Why this matters: These generated files give you full TypeScript IntelliSense for every device endpoint, eliminating manual interface creation and ensuring type safety.
Step 2: Create the Color Manager Model
Generate a singleton model to manage color state:
kosui model --name color-manager --project my-first-kos-app-models --singleton[Screenshot: Terminal showing the model generation output]
Why singleton? We only need one color manager instance for the entire app.
Step 3: Add Observable Property
Open libs/my-first-kos-app-models/src/models/color-manager/color-manager-model.ts.
Add a color property to hold the baseline color:
@kosModel({ modelTypeId: MODEL_TYPE, singleton: true })
@kosLoggerAware()
export class ColorManagerModelImpl implements IKosDataModel {
// Observable property - UI automatically updates when this changes
currentColor: string = '#000000';
async init(): Promise<void> {}
async load(): Promise<void> {}
}[Screenshot: VS Code showing the ColorManagerModelImpl class with currentColor property added]
Why: This observable property will update when the service request completes, automatically triggering UI re-renders.
Step 4: Import Service Functions
Add imports for the generated service functions at the top of the file:
import {
kosModel,
IKosDataModel,
kosLoggerAware,
KosLoggerAware,
kosServiceRequest,
DependencyLifecycle,
} from "@kosdev-code/kos-ui-sdk";
import type { ColorManagerOptions } from "./types";
import { KOS_APP } from "../../../utils/services-index";
// Extract the service request decorator configured for your app
const { kosServiceRequest: appServiceRequest } = KOS_APP;[Screenshot: VS Code showing the complete import section with all necessary imports highlighted]
Why KOS_APP.kosServiceRequest? The decorator is app-specific and provides full TypeScript IntelliSense for your device’s OpenAPI specification.
Step 5: Add Service Request Method
Add a method decorated with @kosServiceRequest to fetch baseline color data:
@kosModel({ modelTypeId: MODEL_TYPE, singleton: true })
@kosLoggerAware()
export class ColorManagerModelImpl implements IKosDataModel {
currentColor: string = '#000000';
@appServiceRequest({
path: '/api/kos/colors/baseline',
lifecycle: DependencyLifecycle.READY
})
async handleBaselineColorLoaded(data: { color: string }): Promise<void> {
this.logger.info('Baseline color received:', data);
// Update the observable property
this.currentColor = data.color;
this.logger.info(`Color updated to: ${this.currentColor}`);
}
async init(): Promise<void> {
this.logger.info('Color manager initialized');
}
async load(): Promise<void> {
this.logger.info('Color manager loaded');
}
}[Screenshot: VS Code showing the complete ColorManagerModelImpl class with the decorated method highlighted]
What this does:
The @kosServiceRequest decorator tells KOS:
- When to execute: When the model reaches the
READYlifecycle phase (afterinit()andload()complete) - What endpoint: Send a GET request to
/api/kos/colors/baseline - What to do with response: Call
handleBaselineColorLoadedwith the response data - Automatic handling: Error management, retries, and logging happen automatically
Why lifecycle-based? This is the simplest pattern—the request happens automatically when the model is ready, just like loading initial data.
Step 6: Register Your Model
Open apps/my-first-kos-app-ui/src/app/registration.ts.
Add the import:
import { ColorManager } from "@my-first-kos-app/models";Add the registration:
KosModelRegistry.dispense.models().model(ColorManager).preload(ColorManager.type);[Screenshot: VS Code showing registration.ts with the ColorManager import and registration line highlighted]
Step 7: Create Color Display Component
Create apps/my-first-kos-app-ui/src/app/components/color-display.tsx:
import { kosComponent, useKosModel } from '@kosdev-code/kos-ui-sdk';
export const ColorDisplay = kosComponent(() => {
const { model: colorManager, ready } = useKosModel({
modelType: 'color-manager-model'
});
if (!ready) {
return <div>Loading color manager...</div>;
}
return (
<div style={{ padding: '20px', fontFamily: 'Arial' }}>
<h2>Baseline Color</h2>
<div style={{
width: '200px',
height: '200px',
backgroundColor: colorManager.currentColor,
border: '2px solid #333',
borderRadius: '8px',
marginBottom: '20px'
}} />
<div style={{ fontSize: '18px', fontWeight: 'bold' }}>
Color Value: {colorManager.currentColor}
</div>
<div style={{ fontSize: '14px', color: '#666', marginTop: '10px' }}>
This color was loaded automatically from the device API
</div>
</div>
);
});[Screenshot: VS Code showing the complete ColorDisplay component]
Why kosComponent? This wrapper makes the component observe the model. When currentColor changes, the component automatically re-renders with the new background color.
Step 8: Add Component to Your App
Open apps/my-first-kos-app-ui/src/app/app.tsx:
import { CounterDisplay } from './components/counter-display';
import { ColorDisplay } from './components/color-display';
export function App() {
return (
<div>
<h1>My First KOS App</h1>
<CounterDisplay />
<ColorDisplay />
</div>
);
}[Screenshot: VS Code showing app.tsx with both components imported and used]
Note: Both components are now visible on screen. The counter continues to auto-increment every 2 seconds (from tutorial 02), and the color display shows the baseline color. Each model operates independently, demonstrating how KOS models compose naturally without interfering with each other.
Step 9: See It Work
Save all files. The browser automatically refreshes.
What happens:
- App starts – Color manager model is created (registration)
- Model initializes –
init()andload()run - Model becomes READY – Service request fires automatically
- Request completes –
handleBaselineColorLoadedis called with response data - Property updates –
currentColorchanges from ‘#000000’ to the API response color - UI reacts – Component re-renders with new background color
[Screenshot: Browser showing both the counter (from tutorial 02) auto-incrementing at the top, and the color display with an orange box below it, demonstrating independent model updates]
Open the Console (F12) and you’ll see:
Color manager initialized
Color manager loaded
Baseline color received: { color: "#FF5733" }
Color updated to: #FF5733[Screenshot: Chrome DevTools Console showing the model lifecycle logs and the service request logs]
This is the key insight: You wrote no code to call the API, handle the response, or update the UI. The @kosServiceRequest decorator handled everything based on the model lifecycle.
What Just Happened
You built an API-integrated reactive system:
- Generated Types – TypeScript types from OpenAPI for full IntelliSense
- Service Request Decorator – Automatic API call tied to model lifecycle
- Observable Property –
currentColorupdates trigger UI re-renders - Lifecycle Integration – Request fires automatically when model is ready
- Error-First Pattern – Service functions return
[error, data]tuples for safe error handling - Zero Boilerplate – No manual fetch calls, no useEffect, no state synchronization
The decorator pattern eliminates the typical React pattern of “fetch in useEffect, store in useState, handle loading/error states”—KOS handles all of that for you.
Key Concepts Demonstrated
Declarative Service Requests: The @kosServiceRequest decorator declares what should happen (fetch baseline color when ready) without specifying how to make the request, handle errors, or update the UI.
Lifecycle-Based Execution: By specifying lifecycle: DependencyLifecycle.READY, the request runs automatically at the right time in the model lifecycle—no manual triggering needed.
Type Safety: The generated service types ensure you can’t call endpoints that don’t exist or use wrong parameter types.
Separation of Concerns: The model handles API integration and business logic. The component just renders. The decorator handles the HTTP details.
Observable Integration: When currentColor updates, any component using that property automatically re-renders—no manual state management.
Model Composability: Notice how the counter model and color manager model coexist without any coordination code. Each model manages its own state, lifecycle, and UI updates independently. This is a core KOS architectural principle—models compose naturally.
Understanding the Service Response
The fictional /api/kos/colors/baseline endpoint returns:
{
"color": "#FF5733"
}In a real device, you’d discover actual endpoints using:
- Device OpenAPI documentation (auto-generated by
kosui api:generate) - MCP tools for endpoint discovery
- Device backend team’s API documentation
The service generation handles the response typing automatically, so you get IntelliSense for response properties.
Try This
Extend the color manager:
- Add a
lastUpdated: Dateproperty - Update it in
handleBaselineColorLoaded - Display the timestamp in the component
- Add error handling to show when the request fails
You’ll see that adding model features requires no changes to the component’s reactive logic—properties automatically propagate to the UI.
Next Steps
This tutorial showed the simplest service request pattern: automatic lifecycle-based requests. The next tutorial builds on this foundation by adding event-driven updates.
In the next tutorial, you’ll learn how to use @kosTopicHandler to listen for device events that change the color in real-time. You’ll keep this color manager model and add a topic handler that receives color-changed events from the device, demonstrating how lifecycle requests (baseline data) and topic handlers (real-time updates) work together.
Coming up:
- Subscribe to device topics with
@kosTopicHandler - Handle real-time color change events
- Combine baseline data (service requests) with live updates (topic handlers)
- See the color box change dynamically as device events arrive
Next: Learn how to receive real-time device events with topic handlers.
