Featherweight Development Kit for entrecode APIs.
npm i ec.fdk
There are 2 ways to use ec.fdk:
Start by calling fdk with your environment (stage | live), then method chain your way to success:
import { fdk } from 'ec.fdk';
fdk('stage') // choose stage environment
.dm('<shortID>') // select datamanager via short id
.model('muffin') // select model muffin
.entryList() // load entry list
.then((list) => {
console.log(list);
});
See all functions in the Fdk reference.
The act function converts a single object param into a fetch request:
const muffins = await act({
action: 'entryList',
env: 'stage',
dmShortID: '<shortID>',
model: 'muffin',
});
More in the act reference.
The act function is good to be used with swr or react-query:
import { act } from 'ec.fdk';
import useSWR from 'swr';
export function useFdk(config) {
const key = config ? JSON.stringify(config) : null;
return useSWR([key], () => act(config));
}
Then use the hook:
const config = {
env: 'stage',
dmShortID: '<shortID>',
};
function App() {
const { data: entryList } = useFdk({
...config,
action: 'entryList',
model: 'muffin',
});
/* more stuff */
}
You can replace the built-in HTTP fetcher with a custom function via .set({ fetcher }). This is useful for testing and mock modes — intercept all ec.fdk requests without monkey-patching globalThis.fetch:
import { fdk } from 'ec.fdk';
const mockFetcher = async (url, config, options) => {
// return fixture data based on URL
if (url.includes('/api/myShortID/settings')) {
return {
count: 1, total: 1,
_embedded: { 'myShortID:settings': [{ subdomain: 'test' }] },
};
}
return { count: 0, total: 0, _embedded: {} };
};
const api = fdk('stage')
.token('my-token')
.set({ fetcher: mockFetcher });
// uses mockFetcher instead of real HTTP — works with the full chaining API
const list = await api.dm('myShortID').model('settings').entryList();
The custom fetcher has the same signature as the internal fetcher: (url: string, config?: { token?: string; rawRes?: boolean }, options?: RequestInit) => Promise<any>. It must return parsed JSON (not a Response object) in the same shape as the entrecode API.
The Fetcher type is exported for convenience:
import type { Fetcher } from 'ec.fdk';
ec.fdk also ships a CLI for quick shell-based interaction with entrecode APIs. Output is JSON, so you can pipe it into jq and friends.
npx ec.fdk <command> [options]
Or install globally:
npm i -g ec.fdk
ec.fdk <command> [options]
| Command | Description | Required flags |
|---|---|---|
| Entries | --dm = shortID, --model required |
|
entryList |
List entries | --dm, --model |
getEntry |
Get a single entry | --dm, --model, --id |
createEntry |
Create an entry | --dm, --model, --data |
editEntry |
Edit an entry | --dm, --model, --id, --data |
deleteEntry |
Delete an entry | --dm, --model, --id |
getSchema |
Get model schema | --dm, --model |
| Datamanagers | --id = DM UUID |
|
dmList |
List datamanagers | — |
getDatamanager |
Get a datamanager | --id |
createDatamanager |
Create a datamanager | --data |
editDatamanager |
Edit a datamanager (full PUT) | --id, --data |
deleteDatamanager |
Delete a datamanager | --id |
getStats |
Get datamanager stats | — |
| Models | --id = DM UUID |
|
modelList |
List models | --id |
createModel |
Create a model | --id, --data |
editModel |
Edit a model | --id, --rid, --data |
deleteModel |
Delete a model | --id, --rid |
| Templates | ||
createTemplate |
Create a template | --data |
| Asset Groups | --id = DM UUID |
|
createAssetGroup |
Create an asset group | --id, --data |
editAssetGroup |
Edit an asset group | --id, --rid, --data |
| Assets | --dm = shortID |
|
assetList |
List assets | --dm, --assetgroup |
getAsset |
Get a single asset | --dm, --assetgroup, --rid |
createAsset |
Upload asset(s) from local file | --dm, --assetgroup, --file (repeatable) |
editAsset |
Edit asset metadata | --dm, --assetgroup, --rid, --data |
deleteAsset |
Delete an asset | --dm, --assetgroup, --rid |
| DM Clients | --id = DM UUID |
|
editDmClient |
Edit a DM client | --id, --rid, --data |
| Roles | --id = DM UUID |
|
createRole |
Create a role | --id, --data |
editRole |
Edit a role | --id, --rid, --data |
deleteRole |
Delete a role | --id, --rid |
| DM Accounts | --id = DM UUID |
|
editDmAccount |
Edit a DM account | --id, --account-id, --data |
deleteDmAccount |
Delete a DM account | --id, --account-id |
| Account Clients | ||
createAccountClient |
Create an account client | --data |
editAccountClient |
Edit an account client | --rid, --data |
deleteAccountClient |
Delete an account client | --rid |
| Groups | ||
createGroup |
Create a group | --data |
editGroup |
Edit a group | --rid, --data |
deleteGroup |
Delete a group | --rid |
| Invites | ||
createInvite |
Create an invite | --data |
editInvite |
Edit an invite | --rid, --data |
deleteInvite |
Delete an invite | --rid |
| Accounts | ||
editAccount |
Edit an account | --account-id, --data |
| Tokens | ||
listTokens |
List tokens | --account-id |
createToken |
Create a token | --account-id |
deleteToken |
Delete a token | --account-id, --rid |
| Generic Resources | ||
resourceList |
List any resource type | --resource, -f |
resourceGet |
Get a single resource | --resource, -f |
resourceEdit |
Edit a single resource | --resource, -f, --data |
resourceDelete |
Delete a single resource | --resource, -f |
| Other | ||
login |
Login via browser (OIDC). Use --password for email/password. |
— |
logout |
Logout and remove stored token | — |
whoami |
Show current logged-in user | — |
describe |
Show type definition for a command's return value | <command> |
typegen |
Generate typed entry APIs (.d.ts) | --dm |
getHistory |
Get dm-history entries | -f shortID=<shortID> |
install-skill |
Install Claude Code skill | — |
update |
Self-update ec.fdk | — |
resourceList resource types:
--resource |
--subdomain |
Typical filters |
|---|---|---|
dm-account |
— | -f dataManagerID=<dataManagerID> |
dm-client |
— | -f dataManagerID=<dataManagerID> |
model |
— | -f dataManagerID=<dataManagerID> |
assetgroup |
— | -f dataManagerID=<dataManagerID> |
role |
— | -f dataManagerID=<dataManagerID> |
tag |
— | -f dataManagerID=<dataManagerID> |
template |
— | — |
account |
accounts |
— |
client |
accounts |
— |
group |
accounts |
— |
invite |
accounts |
— |
account/tokens |
accounts |
-f accountID=<accountID> |
| Option | Description |
|---|---|
-e, --env <env> |
Environment: stage | live (default: stage) |
-d, --dm <shortID> |
DataManager short ID |
-m, --model <name> |
Model name |
-i, --id <id> |
Entry ID or DataManager UUID (context-dependent) |
--rid <id> |
Resource ID (model, template, role, client, asset group, invite, etc.) |
--account-id <id> |
Account ID |
--assetgroup <name> |
Asset group name (for asset commands) |
--file <path> |
Local file path for createAsset (repeatable) |
--resource <name> |
Resource name (for resource* commands and describe) |
--subdomain <name> |
Subdomain override (for resource* commands) |
--data <json> |
JSON data (for create/edit, or pipe via stdin) |
-s, --size <n> |
Page size for list |
-p, --page <n> |
Page number for list |
--sort <field> |
Sort field for list |
-f, --filter <k=v> |
Filter for list (repeatable) |
--password |
Use email/password login instead of browser OIDC |
--dir <path> |
Target directory for install-skill (default: ~/.claude) |
--raw |
Include _links and _embedded in output |
--md |
Output entries as readable markdown table |
--short |
Only print the return type, omit referenced types (for describe) |
--models <a,b,c> |
Only generate types for these models (comma-separated, typegen only) |
--out <path> |
Output file path for typegen (default: ./ec.fdk.generated.<shortID>.d.ts) |
-v, --version |
Show version |
-h, --help |
Show help |
All examples use <PLACEHOLDER> values — replace them with your own resource IDs.
| Placeholder | Flag | Resource |
|---|---|---|
<shortID> |
--dm |
Datamanager |
<dataManagerID> |
--id |
Datamanager |
<entryID> |
--id |
Model |
<modelID> |
--rid |
Model |
<assetGroup> |
--rid / --assetgroup |
Asset Group |
<assetID> |
--rid |
Asset |
<clientID> |
--rid |
DM Client / Account Client |
<roleID> |
--rid |
Role |
<accountID> |
--account-id |
DM Account / Account |
<groupID> |
--rid |
Group |
<inviteID> |
--rid |
Invite |
<tokenID> |
--rid |
Token |
<collectionID> |
--data |
Template |
# Login to stage via browser OIDC (stores token in ~/.ec-fdk/auth.json)
ec.fdk login -e stage
# Login via email/password prompt
ec.fdk login -e stage --password
# Logout
ec.fdk logout -e stage
# Show current user
ec.fdk whoami -e stage
# Install Claude Code skill (to ~/.claude/skills/ec-fdk/)
ec.fdk install-skill
# Install to custom directory
ec.fdk install-skill --dir ~/entrecode/.claude
# Self-update ec.fdk
ec.fdk update
# List datamanagers
ec.fdk dmList -e stage
# Get a single datamanager
ec.fdk getDatamanager -e stage --id <dataManagerID>
# List models of a datamanager
ec.fdk modelList -e stage --id <dataManagerID>
# Filter models by title
ec.fdk modelList --id <dataManagerID> -f title~=muffin
# List entries
ec.fdk entryList -d <shortID> -m muffin
# List with pagination
ec.fdk entryList -d <shortID> -m muffin -s 10 -p 2
# Get a single entry
ec.fdk getEntry -d <shortID> -m muffin -i <entryID>
# Create an entry
ec.fdk createEntry -d <shortID> -m muffin --data '{"name":"new muffin","amazement_factor":10}'
# Create via stdin pipe
echo '{"name":"piped muffin","amazement_factor":10}' | ec.fdk createEntry -d <shortID> -m muffin
# Edit an entry
ec.fdk editEntry -d <shortID> -m muffin -i <entryID> --data '{"name":"edited"}'
# Delete an entry
ec.fdk deleteEntry -d <shortID> -m muffin -i <entryID>
# Get model schema
ec.fdk getSchema -d <shortID> -m muffin
# Filter entries (repeatable -f for arbitrary query params)
ec.fdk entryList -d <shortID> -m muffin -f name~=chocolate
ec.fdk entryList -d <shortID> -m muffin -f amazement_factorFrom=5 -f amazement_factorTo=10
ec.fdk entryList -d <shortID> -m muffin -f createdFrom=2024-01-01
# Load multiple entries by ID (comma-separated)
ec.fdk entryList -d <shortID> -m muffin -f "id=abc123,def456,ghi789"
# Filter datamanagers
ec.fdk dmList -f title~=myproject
# Pipe into jq
ec.fdk entryList -d <shortID> -m muffin | jq '.items | length'
# Datamanager stats
ec.fdk getStats
# dm-history (requires shortID filter)
ec.fdk getHistory -f shortID=<shortID> -s 10
# List any resource type
ec.fdk resourceList --resource template -s 5
ec.fdk resourceList --resource client --subdomain accounts
# Generic get / edit / delete (works with any resource type)
ec.fdk resourceGet --resource model -f dataManagerID=<dataManagerID> -f modelID=<modelID>
ec.fdk resourceGet --resource group --subdomain accounts -f groupID=<groupID>
ec.fdk resourceEdit --resource invite --subdomain accounts -f invite=<inviteID> --data '{"email":"user@example.com","permissions":["dm-read"],"groups":[]}'
ec.fdk resourceDelete --resource role -f dataManagerID=<dataManagerID> -f roleID=<roleID>
# Datamanager
ec.fdk createDatamanager --data '{"title":"My DM","config":{}}'
ec.fdk deleteDatamanager --id <dataManagerID>
# editDatamanager is a full PUT — pass the complete resource, not just changed fields.
# Use getDatamanager + jq to build the payload:
ec.fdk getDatamanager --id <dataManagerID> \
| jq '.title = "New Title"' \
| ec.fdk editDatamanager --id <dataManagerID>
# Model
ec.fdk createModel --id <dataManagerID> --data '{"title":"article","locales":[],"fields":[]}'
ec.fdk editModel --id <dataManagerID> --rid <modelID> --data '{"title":"renamed","locales":[],"fields":[]}'
ec.fdk deleteModel --id <dataManagerID> --rid <modelID>
# Template
ec.fdk createTemplate --data '{"name":"My Template","collection":{"id":"<collectionID>","name":"my-collection","order":[],"requests":[]}}'
# Asset group
ec.fdk createAssetGroup --id <dataManagerID> --data '{"assetGroupID":"photos"}'
ec.fdk editAssetGroup --id <dataManagerID> --rid <assetGroup> --data '{"public":true}'
# Assets
ec.fdk assetList --dm <shortID> --assetgroup <assetGroup>
ec.fdk getAsset --dm <shortID> --assetgroup <assetGroup> --rid <assetID>
ec.fdk createAsset --dm <shortID> --assetgroup <assetGroup> --file ./photo.jpg
ec.fdk createAsset --dm <shortID> --assetgroup <assetGroup> --file ./a.jpg --file ./b.png
ec.fdk editAsset --dm <shortID> --assetgroup <assetGroup> --rid <assetID> --data '{"title":"sunset"}'
ec.fdk deleteAsset --dm <shortID> --assetgroup <assetGroup> --rid <assetID>
# DM client
ec.fdk editDmClient --id <dataManagerID> --rid <clientID> --data '{"callbackURL":"https://example.com/cb"}'
# Role
ec.fdk createRole --id <dataManagerID> --data '{"name":"editor"}'
ec.fdk editRole --id <dataManagerID> --rid <roleID> --data '{"name":"admin"}'
ec.fdk deleteRole --id <dataManagerID> --rid <roleID>
# DM account
ec.fdk editDmAccount --id <dataManagerID> --account-id <accountID> --data '{"email":"user@example.com"}'
ec.fdk deleteDmAccount --id <dataManagerID> --account-id <accountID>
# Account client
ec.fdk createAccountClient --data '{"clientID":"my-client","callbackURL":"https://example.com/cb"}'
# editAccountClient is a full PUT — clientID is required in the body.
ec.fdk resourceList --resource client --subdomain accounts -f clientID=my-client \
| jq '.items[0]' \
| jq '.callbackURL = "https://example.com/cb2"' \
| ec.fdk editAccountClient --rid my-client
ec.fdk deleteAccountClient --rid <clientID>
# Group
ec.fdk createGroup --data '{"name":"devs"}'
ec.fdk editGroup --rid <groupID> --data '{"name":"developers"}'
ec.fdk deleteGroup --rid <groupID>
# Invite
ec.fdk createInvite --data '{"email":"user@example.com"}'
# editInvite is a full PUT — pass the complete resource (email, permissions, groups).
ec.fdk editInvite --rid <inviteID> --data '{"email":"user@example.com","permissions":["dm-read"],"groups":[]}'
ec.fdk deleteInvite --rid <inviteID>
# Account
ec.fdk editAccount --account-id <accountID> --data '{"email":"user@example.com"}'
# Tokens
ec.fdk listTokens --account-id <accountID>
ec.fdk createToken --account-id <accountID>
ec.fdk deleteToken --account-id <accountID> --rid <tokenID>
Generate a .d.ts declaration file that gives you type-safe entry APIs with autocomplete for all models in a datamanager:
# Requires login first
ec.fdk login -e stage
# Generate types for all models (output: ./ec.fdk.generated.<shortID>.d.ts)
ec.fdk typegen --dm <shortID> --env stage
# Generate types for specific models only (faster, smaller output)
ec.fdk typegen --dm <shortID> --models site,settings,membership_config
# Custom output path
ec.fdk typegen --dm <shortID> --out ./types/my-dm.d.ts
The generated file includes a // Regenerate: comment with the exact command used, making it easy to re-run later.
Place the generated file in your TypeScript project. After that, model() carries the model name as a type parameter, so all entry methods return typed results:
import { fdk } from 'ec.fdk';
const muffin = await fdk('stage').dm('<shortID>').model('muffin').getEntry('abc');
muffin.name; // string — autocomplete works
muffin.amazement_factor; // number
muffin.bogus; // type error
await fdk('stage').dm('<shortID>').model('muffin').createEntry({
name: 'Blueberry', // required string
amazement_factor: 9, // required number
color: 'blue', // required string
});
await fdk('stage').dm('<shortID>').model('muffin').editEntry('abc', {
name: 'Updated', // partial — only changed fields needed
});
Without a generated file, everything falls back to the default EntryResource type with [key: string]: unknown — fully backwards-compatible.
| Option | Description |
|---|---|
--dm <shortID> |
DataManager short ID (required) |
--env <env> |
stage (default) or live |
--models <a,b,c> |
Only generate types for these models (comma-separated) |
--out <path> |
Output file path (default: ./ec.fdk.generated.<shortID>.d.ts) |
JSON/object fields are generated as unknown since the schema has no structure info. You can refine these types by augmenting ModelOverrides in a separate file (so re-running typegen won't overwrite your changes):
// ec.fdk.overrides.d.ts
export {};
declare module "ec.fdk" {
interface ModelOverrides {
"restaurant": {
cuisine?: { italian: boolean; maxSeats: number } | null;
};
}
}
Only the fields you list are overridden — all other fields keep their generated types.
Show the return type of any command:
# Show the type definition for a command's return value (includes referenced types)
ec.fdk describe getAsset
ec.fdk describe entryList
# Only print the main return type, omit referenced types
ec.fdk describe getAsset --short
# List all describable commands
ec.fdk describe
For entry commands (getEntry, entryList, createEntry, editEntry), pass --dm and --model to generate a concrete type with actual field names and types fetched from the model schema (public API, no auth required):
ec.fdk describe getEntry --dm 83cc6374 --model muffin
# type MuffinEntry = EntryResourceBase & {
# name: string;
# amazement_factor: number;
# test_asset: string | null;
# color: string;
# }
#
# type EntryResourceBase = {
# id: string;
# _created: Date;
# ...
# }
ec.fdk describe entryList --dm 83cc6374 --model muffin
# type EntryList = {
# count: number;
# total: number;
# items: MuffinEntry[];
# }
# ...
Without --dm/--model, the generic EntryResource type with [key: string]: any is shown.
For resource commands (resourceList, resourceGet, resourceEdit), pass --resource to get the specific typed return:
ec.fdk describe resourceList --resource dm-account
# type DmAccountList = {
# count: number;
# total: number;
# items: DmAccountResource[];
# }
# type DmAccountResource = { accountID: string; email: string | null; ... }
ec.fdk describe resourceGet --resource client
# type ClientResource = { clientID: string; clientName: string; ... }
Note: account and client refer to Account Server resources. Use dm-account and dm-client for DM-level resources (different, smaller types).
Without --resource, the generic ResourceList type is shown.
The -f flag maps directly to entrecode filter query params. Common filter suffixes:
| Suffix | Meaning | Example |
|---|---|---|
~ |
Search (contains) | -f name~=chocolate |
From |
Greater than / after | -f createdFrom=2024-01-01 |
To |
Less than / before | -f createdTo=2025-01-01 |
, |
Any of (OR) | -f id=id1,id2,id3 |
! |
Not null | -f active!= |
| (none) | Exact match | -f amazement_factor=10 |
Status/error messages go to stderr, data goes to stdout — so piping always works cleanly.
ec.fdk won't change / decorate data returned from ec APIs. For example, an entry returned from the datamanager will be returned as is. Advantages:
Instead of mutating an EntryResource and calling .save(), we now pass the new value directly:
// this does not exist anymore:
await entry.save(); // <- DONT
// use this to update an entry:
await editEntryObject(entry, value); // <- DO
// alternatively:
await fdk.env(env).dm(dmShortID).model(model).updateEntry(entryID, value);
// or:
await act({ action: 'editEntry', env, dmShortID, model, entryID, value });
Similar to save:
// this does not exist anymore:
await entry.del(); // <- DONT
// use this to delete an entry:
await deleteEntryObject(entry); // <- DO
// alternatively:
await fdk.dm('shortID').model('model').deleteEntry('entryID');
// or:
await act({ action: 'deleteEntry', env, dmShortID, model, entryID });
In ec.fdk, entry asset fields are plain ids:
// assuming "photo" is an asset field:
entry.photo; // <-- this used to be an AssetResource. Now it's a plain id string.
// use this to get the embedded AssetResource:
getEntryAsset('photo', entry); // (no request goes out)
// assuming "lastSeen" is a datetime field:
entry.lastSeen; // <-- this used to be an instance of Date. Now it's a date ISO string
// use this to get a Date instance:
new Date(entry.lastSeen);
// ec.sdk
const api = new PublicAPI(shortID, env, true);
const entryList = await api.entryList(model);
const items = entryList.getAllItems();
const first = entryList.getFirstItem();
// ec.fdk
const api = fdk(env).dm(shortID);
const entryList = await api.entryList(model);
const items = entryList.items; // <------- change
const first = entryList.items[0]; // <------- change
// or in one line:
const entryList = await fdk(env).dm(shortID).entryList(model);
By default, the second param of ec.fdk entryList will just convert the object to url params:
const entryList = await fdk('stage').dm('<shortID>').entryList({ createdTo: '2021-01-18T09:13:47.605Z' });
/*
https://datamanager.cachena.entrecode.de/api/<shortID>/muffin?
_list=true&
createdTo=2021-01-18T09:13:47.605Z&
page=1&
size=50
*/
Read more in the entrecode filtering doc
There is some syntax sugar you can use to get the same behavior as ec.sdk filterOptions:
const entryList = await fdk('stage')
.dm('<shortID>')
.entryList(filterOptions({ created: { to: '2021-01-18T09:13:47.605Z' } }));
// ec.sdk
await api.dmAssetList(group, { assetID: { any: value } });
// ec.fdk
const options = sdkOptions({ assetID: { any: value } } as any);
const assets = await api.assetGroup(group).assetList(options);
// ec.sdk
api.dmAsset(group, img);
// ec.fdk
api.assetGroup(group).getAsset(img);
cd packages/ec.fdk
./publish.sh
The script prompts for a new version, regenerates docs, commits, pushes, and publishes to npm.