@kodall/kodall-client
v0.1.24
Published
A custom REST client for interacting with a Kodall-based instance, supporting authentication, entity operations, workflow execution, and file upload, all with session management and automatic cookie handling.
Readme
KodallClient
A custom REST client for interacting with a Kodall-based instance, supporting authentication, entity operations, workflow execution, and file upload, all with session management and automatic cookie handling.
Table of Contents
Features
- 🔐 Supports multiple authentication methods:
- Basic Auth
- OpenID Connect
- API Key
- 📦 Full support for CRUD on entities
- 🔄 Session refresh & logout
- 📂 File uploads with CSRF token support
- ⚙️ Flexible workflow and fetch query execution
- 🧪 Built-in type guards for runtime type safety
- 🍪 Handles cookies automatically in the browser
Installation
npm i @kodall/kodall-clientUsage
Initialize the client
You can initialize the client with an API key for authentication. If you are using the client in a browser environment with an existing session, you do not need to provide an API key.
import { KodallClient } from "@kodall/kodall-client"
import * as KodallTypes from "@kodall/kodall-client/dist/types"
// No API key needed in a browser with an existing session
var client = new KodallClient()
// or with an API key
var client = new KodallClient({ apiKey: "your-api-key" })Authentication
Authentication is supported on 2 methods:
- with username and password
({ user, password })
try {
var result = await client.auth( { user: "user", password: "pass" } )
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}- with openID token
({ access_token })
try {
var result = await client.auth( { accessToken: "access-token-example" } )
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}Session & Profiles
Gathering session information
try {
var result = await client.session();
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}Refreshing session
try {
var result = await client.refreshSession();
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}Logout session
try {
var result = await client.logout();
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}Gathering user profiles information
try {
var result = await client.getUserProfiles();
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}Gathering user roles information
try {
var result = await client.getUserRoles();
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}Changing user profile
try {
var result = await client.changeUserProfile('admin')
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}Changing user password
try {
var result = await client.changePassword({
"current-password": changePasswordCurrent.value,
"set-password": changePasswordNew.value,
"confirm-password": changePasswordConfirm.value,
})
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}CRUD Operations
Get entity by key
try {
var result = await client.get('invoice', 123);
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}Create entity
try {
var result = await client.create({ entity_name: 'invoice', properties: { name: 'Test' } });
//Validation Guard clause
if (isValidation(result)) {
// ...processing...
// return something
}
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}Update entity
try {
var result = await client.update(entity);
//Validation Guard clause
if (isValidation(result)) {
// ...processing...
// return something
}
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}Delete entity
try {
var result = await client.delete('invoice', 123);
//Validation Guard clause
if (isValidation(result)) {
// ...processing...
// return something
}
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}Fetch Queries
try {
var result = await client.fetch<MyEntity>(`FETCH invoice (key, total_value) FILTER AND (status == "open")`)
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}Workflows
try {
var result = await client.workflow<MyResult>('myWorkflow', 'myMethod', {
demo_value: 'demo',
other_demo_value: {
id: 123
}
});
//Validation Guard clause
if (isValidation(result)) {
// ...processing...
// return something
}
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}File Upload // File version upload
try {
var result = await client.uploadFile(myFile);
//Validation Guard clause
if (isValidation(result)) {
// ...processing...
// return something
}
//Problem Guard clause
if (isProblem(result)) {
// ...processing...
// return something
}
//...processing...
// return
} catch (e) {
// ...processing error...
}Type Guards
These utilities help you work with uncertain responses:
import {
isProblem,
isValidation,
isOperation,
isStorageResponse
} from '@kodall/kodall-client';| Guard | Description |
| ------------------------ | ------------------------------------------------ |
| isProblem(obj) | Is the response a Problem (e.g. server error)? |
| isValidation(obj) | Is the response a Validation error? |
| isOperation(obj) | Is the response a successful Operation? |
| isStorageResponse(obj) | Is the result from a file upload valid? |
Example:
try {
const response = await client.create(entity);
if (isValidation(response)) {
// Handle form errors
// return
}
if (isProblem(response)) {
// Handle API failure
// return
}
// Success!
} catch (e) {
// Handle Error
}Types
You can find detailed type definitions in types.ts, including:
SessionUserProfileEntity,Operation,Validation,ProblemOpenIdProvider,OidcTokens,UserPasswordKodallClientOptions,StorageResponse
Proxy Configuration
When using KodallClient in a development environment with a frontend framework, you'll need to configure a proxy to forward requests from your frontend application to the Kodall backend. This avoids CORS issues and simplifies API calls.
Vite
For a standard Vite project, you can configure the proxy in your vite.config.ts file. Here is an example configuration that proxies the /rest and /auth endpoints to the production Kodall instance:
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
'/rest': {
target: 'https://developer.oneerp.ro/',
changeOrigin: true,
secure: true,
},
'/storage': {
target: 'https://developer.oneerp.ro/',
changeOrigin: true,
secure: true,
},
'/auth': {
target: 'https://developer.oneerp.ro/',
changeOrigin: true,
secure: true,
}
}
}
})Nuxt.js
For Nuxt.js applications, you can configure the proxy in your nuxt.config.ts file. Here is an example configuration that proxies the /rest, /storage, and /auth endpoints to a local Kodall instance running on https://developer.oneerp.ro:
// nuxt.config.ts
export default defineNuxtConfig({
// ... other config
nitro: {
devProxy: {
"/rest": {
target: "https://developer.oneerp.ro/rest",
changeOrigin: true,
secure: true,
},
"/storage": {
target: "https://developer.oneerp.ro/storage",
changeOrigin: true,
secure: true,
},
"/auth": {
target: "https://developer.oneerp.ro/auth",
changeOrigin: true,
secure: true,
},
},
},
// ... other config
})Make sure to adjust the target URL to match the address of your Kodall backend.
