This guide covers building modern Angular (v17+) standalone applications, configuring the Angular development proxy, injecting @kodall/kodall-client, and deploying with @kodall/kodall-deploy.
Deployed on Kodall instance at /angular
Currently running in shared demo mode with the public API key. Create a private 24-hour sandbox with 1-click to test creating, editing, and deleting records without interference.
Install @kodall/kodall-client and @kodall/kodall-deploy in your Angular project:
npm install @kodall/kodall-client
npm install -D @kodall/kodall-deploy
pnpm add @kodall/kodall-client
pnpm add -D @kodall/kodall-deploy
yarn add @kodall/kodall-client
yarn add -D @kodall/kodall-deploy
bun add @kodall/kodall-client
bun add -d @kodall/kodall-deploy
proxy.conf.js (Recommended)Create proxy.conf.js using @kodall/kodall-deploy's getAngularProxy() helper to automatically resolve target instances from kodall-webapp.config.json:
const { getAngularProxy } = require('@kodall/kodall-deploy')
// Automatically resolves instance from default_env
module.exports = getAngularProxy()
Reference the proxy in angular.json:
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"proxyConfig": "proxy.conf.js"
}
}
proxy.conf.json (Without Helper Package)Create a static proxy.conf.json configuration:
{
"/auth": { "target": "https://dev.kodall.yourcompany.com", "secure": true, "changeOrigin": true },
"/rest": { "target": "https://dev.kodall.yourcompany.com", "secure": true, "changeOrigin": true },
"/storage": { "target": "https://dev.kodall.yourcompany.com", "secure": true, "changeOrigin": true }
}
Reference proxy.conf.json in angular.json:
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"proxyConfig": "proxy.conf.json"
}
}
getAngularProxy() automatically configures proxying for /auth, /rest, /storage, and custom proxy_paths from default_env in kodall-webapp.config.json. To target a specific environment, pass { env: 'staging' } or explore the Local Dev Proxy Suite.Create an injectable Angular service to provide a shared instance of KodallClient:
import { Injectable } from '@angular/core';
import { KodallClient } from '@kodall/kodall-client';
@Injectable({
providedIn: 'root'
})
export class KodallService {
public client = new KodallClient();
}
new KodallClient() requires no hardcoded baseUrl or credentials.Angular 17+ compiles standalone application bundles to ./dist/<project-name>/browser:
{
"web_app_name": "Angular Dashboard",
"web_app_path": "/",
"dist_path": "./dist/my-app/browser",
"default_env": "dev",
"environments": {
"dev": {
"type": "dev",
"instance": "https://dev.kodall.yourcompany.com",
"api_key": "dev-api-key-here"
},
"prod": {
"type": "prod",
"instance": "https://kodall.yourcompany.com",
"api_key": "prod-api-key-here"
}
}
}
https://docs-demo.kodall.io. Enter the base URL of your own Kodall instance deployed from the Kodall Cloud Console.Support both API Key (stateless bearer token) and Username/Password (cookie session) using Angular Signals:
import { Component, OnInit, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { KodallClient, isProblem } from '@kodall/kodall-client';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule],
templateUrl: './app.html',
})
export class App implements OnInit {
public isInitializing = signal<boolean>(true);
public isAuthenticated = signal<boolean>(false);
public apiKey = signal<string>('');
public username = signal<string>('');
public password = signal<string>('');
public getClient(customKey?: string): KodallClient {
return new KodallClient({ apiKey: customKey || this.apiKey() || undefined });
}
// Connect with API Key
public async handleConnectApiKey(keyInput?: string) {
const key = keyInput || this.apiKey();
if (!key.trim()) return;
const client = this.getClient(key);
const result = await client.fetch('FETCH todo (key) LIMIT 1');
if (!isProblem(result)) {
this.isAuthenticated.set(true);
localStorage.setItem('kodall_is_logged_in', 'true');
localStorage.setItem('kodall_auth_mode', 'apikey');
localStorage.setItem('kodall_api_key', key);
}
}
// Login with Username & Password
public async handleLoginBasic(uInput?: string, pInput?: string) {
const u = uInput || this.username();
const p = pInput || this.password();
if (!u || !p) return;
const client = this.getClient('');
const result = await client.auth({ user: u, password: p }, { roles: true });
if (!isProblem(result)) {
this.isAuthenticated.set(true);
localStorage.setItem('kodall_is_logged_in', 'true');
localStorage.setItem('kodall_auth_mode', 'basic');
}
}
// Restore session on mount (anti-flicker loading splash)
ngOnInit() {
const init = async () => {
try {
const params = new URLSearchParams(window.location.search);
const urlApiKey = params.get('apiKey');
if (urlApiKey) this.apiKey.set(urlApiKey);
if (localStorage.getItem('kodall_is_logged_in') === 'true') {
const mode = localStorage.getItem('kodall_auth_mode');
if (mode === 'basic') await this.handleLoginBasic();
else await this.handleConnectApiKey();
}
} finally {
this.isInitializing.set(false);
}
};
init();
}
}
<!-- Anti-flicker loading splash -->
@if (isInitializing()) {
<div class="loading-splash">
<p>Connecting to Kodall...</p>
</div>
} @else if (!isAuthenticated()) {
<!-- Auth Screen -->
<section class="auth-card">
<button (click)="handleConnectApiKey()">Connect with API Key</button>
<button (click)="handleLoginBasic()">Sign In with Password</button>
</section>
} @else {
<!-- Main Dashboard -->
<div class="dashboard">...</div>
}
Fetch hierarchical parent-child relationships and create nested graphs atomically:
// 1. Fetch joined rows
const result = await client.fetch<any>(`
FETCH todo (key, title, is_completed) {
todo_item TO id_todo (key AS item_key, description, is_completed AS item_completed)
}
ORDER BY key DESC
LIMIT 100
`);
// 2. Group flat rows into tree
function groupJoinedTodos(rows: any[]) {
const map = new Map<number, any>();
for (const row of rows) {
if (!map.has(row.key)) {
map.set(row.key, {
key: row.key,
title: row.title,
is_completed: row.is_completed,
children: { todo_todo_item: [] }
});
}
if (row.item_key != null) {
map.get(row.key).children.todo_todo_item.push({
key: row.item_key,
description: row.description,
is_completed: row.item_completed
});
}
}
return Array.from(map.values());
}
// 3. Create parent entity and child items
await client.create({
entity_name: 'todo',
properties: {
title: 'Deploy to Kodall',
is_completed: 0
},
children: {
todo_todo_item: [
{ entity_name: 'todo_item', properties: { description: 'Configure proxy.conf.json', is_completed: 1 } },
{ entity_name: 'todo_item', properties: { description: 'Deploy application', is_completed: 0 } }
]
}
});
Upload and download files directly using Kodall's storage endpoints:
// Upload a file (Blob or File instance from <input type="file">)
const response = await fetch('/storage', {
method: 'POST',
headers: {
'Content-Type': selectedFile.type || 'application/octet-stream',
'X-File-Name': encodeURIComponent(selectedFile.name)
},
body: selectedFile
});
const storageRecord = await response.json();
console.log('Stored File ID:', storageRecord.id);
Add build and deployment scripts to package.json:
{
"scripts": {
"start": "ng serve",
"build": "ng build",
"deploy": "ng build && kodall-deploy -e dev",
"deploy:prod": "ng build && kodall-deploy -e prod"
}
}
Execute the deployment command for your target environment:
# Deploy to development environment
npm run deploy
# Or deploy to production
npm run deploy:prod
# Or run interactively
npx kodall-deploy
kodall-deploy will validate your build directory, package the application bundle, upload the archive to Kodall storage, update the web_app entity, and execute a live HTTP health check ping.
If your app is deployed to a subpath (e.g. "web_app_path": "/portal"), build with --base-href:
{
"scripts": {
"deploy": "ng build --base-href /portal/ && kodall-deploy -e dev",
"deploy:prod": "ng build --base-href /portal/ && kodall-deploy -e prod"
}
}
Every deployment is tracked in Kodall storage. You can inspect previous versions and instantly roll back to any prior build:
# Inspect live status across instances
npx kodall-deploy --status
# View deployment history
npx kodall-deploy --history -e prod
# Roll back to the previous deployment
npx kodall-deploy --rollback -e prod
Missing index.html in ./dist/<app>/browserdist/<app>/browser.dist_path in kodall-webapp.config.json points to ./dist/<project-name>/browser.Proxy configuration not loading in ng serveproxyConfig is missing from angular.json."proxyConfig": "proxy.conf.json" is configured under architect.serve.options.