https://github.com/eljijuna/argocd-api-client
TypeScript client for the official Argo CD REST API. Fully typed, fetch-based, supports auth tokens, AbortSignal, common Argo CD resources, docs generation, benchmarks, and semantic-release publishing.
https://github.com/eljijuna/argocd-api-client
api-client argo argo-cd argocd cd continuous-delivery devops gitops javascript k8s kubernetes nodejs rest-api sdk typescript
Last synced: 9 days ago
JSON representation
TypeScript client for the official Argo CD REST API. Fully typed, fetch-based, supports auth tokens, AbortSignal, common Argo CD resources, docs generation, benchmarks, and semantic-release publishing.
- Host: GitHub
- URL: https://github.com/eljijuna/argocd-api-client
- Owner: ElJijuna
- License: mit
- Created: 2026-06-07T02:19:49.000Z (16 days ago)
- Default Branch: main
- Last Pushed: 2026-06-09T13:08:22.000Z (13 days ago)
- Last Synced: 2026-06-09T14:21:18.546Z (13 days ago)
- Topics: api-client, argo, argo-cd, argocd, cd, continuous-delivery, devops, gitops, javascript, k8s, kubernetes, nodejs, rest-api, sdk, typescript
- Language: TypeScript
- Homepage: https://eljijuna.github.io/argocd-api-client/
- Size: 408 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Roadmap: ROADMAP.md
Awesome Lists containing this project
README
# argocd-api-client
TypeScript client for the official Argo CD REST API. Works in Node.js 18+ and browsers via `fetch`.
Argo CD exposes Swagger docs at `/swagger-ui` on each Argo CD server. Argo CD authenticates API requests with JWTs; pass the JWT in the `Authorization` header as `Bearer `.
## Install
```bash
npm install argocd-api-client
```
## Quick Start
```typescript
import { ArgoCdClient } from 'argocd-api-client';
const argocd = new ArgoCdClient({
baseUrl: 'https://argocd.example.com',
token: process.env.ARGOCD_TOKEN,
});
const apps = await argocd.applications.list({ project: ['default'] });
console.log(apps.items.map((app) => app.metadata?.name));
```
## Login
```typescript
const argocd = await ArgoCdClient.fromCredentials({
baseUrl: 'https://argocd.example.com',
username: 'admin',
password: 'password',
});
```
The client stores the credentials internally and automatically refreshes the session on 401 responses.
## Resources
```typescript
// Applications
await argocd.applications.list({ project: ['default'] });
await argocd.applications.get('guestbook', { project: 'default' });
await argocd.applications.create({ metadata: { name: 'guestbook' } });
await argocd.applications.update('guestbook', { metadata: { name: 'guestbook' } });
await argocd.applications.patch('guestbook', { spec: {} });
await argocd.applications.sync('guestbook', { revision: 'main' });
await argocd.applications.revisionMetadata('guestbook', 'v2.1.0');
// → ArgoCdRevisionMetadata { author, date, tags, message }
await argocd.applications.terminateSync('guestbook');
await argocd.applications.wait('guestbook', { health: true, timeout: '60s' });
// → ArgoCdApplication returned once the app reaches the desired state
await argocd.applications.deleteResource('guestbook', {
kind: 'Pod',
resourceName: 'api-abc123',
version: 'v1',
namespace: 'default',
force: true,
});
await argocd.applications.rollback('guestbook', { id: 3 });
await argocd.applications.deleteByName('guestbook');
// Application observability
const tree = await argocd.applications.resourceTree('guestbook');
const managed = await argocd.applications.managedResources('guestbook');
const logs = await argocd.applications.logs('guestbook', { container: 'main', tailLines: 100 });
// logs → ArgoCdLogEntry[] (streamed NDJSON, returned as an array)
// Convenience methods
await argocd.applications.images('guestbook');
// → string[] unique container images across all resources
await argocd.applications.pods('guestbook');
// → ArgoCdPod[] live pods with phase, nodeName and container specs/status
await argocd.applications.containers('guestbook');
// → ArgoCdContainer[] all containers flattened from every pod, each with podName
await argocd.applications.nodes('guestbook');
// → ArgoCdNode[] Kubernetes nodes hosting the app's pods (osImage, architecture, kernelVersion…)
await argocd.applications.health('guestbook');
// → ArgoCdApplicationHealth { status: 'Healthy' | 'Degraded' | …, message? }
await argocd.applications.diff('guestbook');
// → ArgoCdManagedResource[] only resources whose live state differs from target
await argocd.applications.events('guestbook');
// → ArgoCdEvent[] Kubernetes events for the application or a specific resource
// ApplicationSets
await argocd.applicationSets.list();
await argocd.applicationSets.get('my-set');
await argocd.applicationSets.create({ metadata: { name: 'my-set' } });
await argocd.applicationSets.update('my-set', { metadata: { name: 'my-set' } });
await argocd.applicationSets.deleteByName('my-set');
// Projects
await argocd.projects.list();
await argocd.projects.get('default');
await argocd.projects.create({ metadata: { name: 'default' } });
await argocd.projects.update('default', { metadata: { name: 'default' } });
await argocd.projects.deleteByName('default');
await argocd.projects.events('default');
// → ArgoCdEvent[] Kubernetes events for the project
await argocd.projects.repositories('default');
// → ArgoCdRepositoryList repositories associated with the project
// Repositories
await argocd.repositories.list();
await argocd.repositories.get('https://github.com/acme/app.git');
await argocd.repositories.create({ repo: 'https://github.com/acme/app.git' });
await argocd.repositories.refs('https://github.com/acme/app.git');
await argocd.repositories.apps('https://github.com/acme/app.git', { revision: 'main' });
// → ArgoCdRepoAppsResponse { items: [{ type, path }] }
await argocd.repositories.deleteByRepo('https://github.com/acme/app.git');
// Clusters
await argocd.clusters.list();
await argocd.clusters.get('https://kubernetes.default.svc');
await argocd.clusters.create({ name: 'prod', server: 'https://prod.k8s.io' });
await argocd.clusters.update('https://prod.k8s.io', { name: 'prod' });
await argocd.clusters.deleteByServer('https://kubernetes.default.svc');
await argocd.clusters.invalidateCache('https://kubernetes.default.svc');
// → ArgoCdCluster cluster with refreshed cache state
// Accounts
await argocd.accounts.list();
await argocd.accounts.get('admin');
await argocd.accounts.canI('applications', 'get', '*');
await argocd.accounts.updatePassword({ name: 'admin', currentPassword: 'old', newPassword: 'new' });
await argocd.accounts.listTokens('admin');
// → ArgoCdAccountTokenList { items: ArgoCdAccountToken[] }
await argocd.accounts.createToken('admin', { expiresIn: '24h', id: 'ci-token' });
// → ArgoCdAccountTokenCreated { token, id, issuedAt, expiresAt }
await argocd.accounts.deleteToken('admin', 'token-id');
// Repository credential templates
await argocd.repoCreds.list();
await argocd.repoCreds.create({ url: 'https://github.com/acme', username: 'bot', sshPrivateKey: '...' });
await argocd.repoCreds.deleteByUrl('https://github.com/acme');
// Repository certificates (TLS/SSH)
await argocd.certificates.list();
await argocd.certificates.create([{ serverName: 'github.com', certType: 'ssh', certSubType: 'ssh-rsa', certData: '...' }]);
await argocd.certificates.delete({ hostNamePattern: 'github.com', certType: 'ssh' });
// GPG keys (commit signature verification)
await argocd.gpgKeys.list();
// → ArgoCdGpgKeyList { items: Record }
await argocd.gpgKeys.create({ keyData: '-----BEGIN PGP PUBLIC KEY BLOCK-----...' });
// → ArgoCdGpgKeyCreateResponse { created: Record, skipped: string[] }
await argocd.gpgKeys.create({ keyData: '...' }, { upsert: true });
await argocd.gpgKeys.deleteByKeyId('A1B2C3D4');
// Server settings (read-only)
await argocd.settings.get();
// → ArgoCdSettings { url, appLabelKey, statusBadgeEnabled, ... }
// Server version
await argocd.version.get();
// → ArgoCdVersion { Version, BuildDate, GitCommit, GitTag, GoVersion, Platform, ... }
// Session
await argocd.userInfo();
// → ArgoCdUserInfo { loggedIn, username, iss, groups }
await argocd.deleteSession();
// Invalidates the current token on the server (logout)
```
## Abort Requests
Every request method accepts an optional `AbortSignal` as its final argument.
```typescript
const controller = new AbortController();
const apps = await argocd.applications.list(
{ project: ['default'] },
controller.signal,
);
controller.abort();
```
## Events
Use `.on('request', callback)` to observe every HTTP request made by the client — useful for logging, metrics, and error tracking.
```typescript
argocd.on('request', (event) => {
console.log(`[${event.method}] ${event.url} → ${event.statusCode} (${event.durationMs}ms)`);
if (event.error) console.error('Request failed:', event.error.message);
});
```
The `RequestEvent` object includes:
| Field | Type | Description |
| --- | --- | --- |
| `url` | `string` | Full URL requested |
| `method` | `'GET' \| 'POST' \| 'PUT' \| 'PATCH' \| 'DELETE'` | HTTP method |
| `startedAt` | `Date` | When the request started |
| `finishedAt` | `Date` | When the request finished |
| `durationMs` | `number` | Duration in milliseconds |
| `statusCode` | `number \| undefined` | HTTP status code (absent on network errors) |
| `error` | `Error \| undefined` | Set on any failed request |
Multiple listeners are supported and called in registration order. `.on()` returns `this` for chaining:
```typescript
argocd
.on('request', (e) => metrics.record(e.durationMs))
.on('request', (e) => { if (e.error) sentry.capture(e.error); });
```
> Token auto-refresh on 401 is transparent: only one event is emitted per logical operation, reflecting the final outcome.
## Express Integration
Install Express and (optionally) its types:
```bash
npm install express
npm install -D @types/express
```
Create a shared client instance and expose Argo CD resources through your own API:
```typescript
// src/argocd.ts
import { ArgoCdClient } from 'argocd-api-client';
export const argocd = new ArgoCdClient({
baseUrl: process.env.ARGOCD_BASE_URL!,
token: process.env.ARGOCD_TOKEN,
});
```
```typescript
// src/routes/apps.ts
import { Router } from 'express';
import { argocd } from '../argocd';
const router = Router();
// GET /apps?project=default
router.get('/', async (req, res, next) => {
try {
const project = req.query.project
? [String(req.query.project)]
: undefined;
const apps = await argocd.applications.list(
{ project },
req.signal, // forward the request's AbortSignal
);
res.json(apps.items?.map((app) => ({
name: app.metadata?.name,
namespace: app.metadata?.namespace,
health: app.status?.health?.status,
sync: app.status?.sync?.status,
})));
} catch (err) {
next(err);
}
});
// POST /apps/:name/sync
router.post('/:name/sync', async (req, res, next) => {
try {
const result = await argocd.applications.sync(req.params.name, {
revision: req.body.revision ?? 'HEAD',
});
res.json(result);
} catch (err) {
next(err);
}
});
export default router;
```
```typescript
// src/index.ts
import express from 'express';
import appsRouter from './routes/apps';
const app = express();
app.use(express.json());
app.use('/apps', appsRouter);
app.listen(3000, () => console.log('Listening on :3000'));
```
### Environment variables
```env
ARGOCD_BASE_URL=https://argocd.example.com
ARGOCD_TOKEN=
```
If you don't have a static token, use `fromCredentials` to authenticate and get a ready-to-use client in one call:
```typescript
import { ArgoCdClient } from 'argocd-api-client';
export const argocd = await ArgoCdClient.fromCredentials({
baseUrl: process.env.ARGOCD_BASE_URL!,
username: process.env.ARGOCD_USER!,
password: process.env.ARGOCD_PASS!,
});
```
### Token refresh
Clients created with `fromCredentials` store the credentials internally and handle token expiry automatically:
```typescript
// Automatic — a 401 response triggers a session refresh and retries the request once
await argocd.applications.list();
// Manual — force a refresh before the token expires
await argocd.refreshSession();
```
Clients created with `new ArgoCdClient({ token })` do not store credentials; calling `refreshSession()` on them throws an error.
### Express + createSession
This pattern exposes a `POST /auth/login` endpoint that exchanges credentials for an Argo CD JWT, then uses that token on every subsequent request via a middleware-created client.
```typescript
// src/routes/auth.ts
import { Router } from 'express';
import { ArgoCdClient } from 'argocd-api-client';
const router = Router();
// POST /auth/login { "username": "admin", "password": "..." }
router.post('/login', async (req, res, next) => {
try {
const { username, password } = req.body as {
username: string;
password: string;
};
if (!username || !password) {
res.status(400).json({ error: 'username and password are required' });
return;
}
const client = new ArgoCdClient({ baseUrl: process.env.ARGOCD_BASE_URL! });
const { token } = await client.createSession({ username, password });
res.json({ token });
} catch (err) {
next(err);
}
});
export default router;
```
```typescript
// src/middleware/argocd.ts
import { RequestHandler } from 'express';
import { ArgoCdClient } from 'argocd-api-client';
declare global {
namespace Express {
interface Request {
argocd: ArgoCdClient;
}
}
}
export const argocdMiddleware: RequestHandler = (req, res, next) => {
const auth = req.headers.authorization;
if (!auth?.startsWith('Bearer ')) {
res.status(401).json({ error: 'Missing or invalid Authorization header' });
return;
}
req.argocd = new ArgoCdClient({
baseUrl: process.env.ARGOCD_BASE_URL!,
token: auth.slice(7),
});
next();
};
```
```typescript
// src/routes/apps.ts
import { Router } from 'express';
import { argocdMiddleware } from '../middleware/argocd';
const router = Router();
router.use(argocdMiddleware);
// GET /apps
router.get('/', async (req, res, next) => {
try {
const apps = await req.argocd.applications.list({}, req.signal);
res.json(apps.items?.map((app) => ({
name: app.metadata?.name,
health: app.status?.health?.status,
sync: app.status?.sync?.status,
})));
} catch (err) {
next(err);
}
});
export default router;
```
```typescript
// src/index.ts
import express from 'express';
import authRouter from './routes/auth';
import appsRouter from './routes/apps';
const app = express();
app.use(express.json());
app.use('/auth', authRouter);
app.use('/apps', appsRouter); // protected by argocdMiddleware
app.listen(3000, () => console.log('Listening on :3000'));
```
**Flow:**
```bash
# 1. Obtain a token
curl -X POST http://localhost:3000/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"secret"}'
# → { "token": "" }
# 2. Use the token in subsequent requests
curl http://localhost:3000/apps \
-H 'Authorization: Bearer '
```
## Benchmarks
```bash
npm run bench
```
The benchmark suite uses mocked `fetch` responses, so it never calls a real Argo CD server. It covers client construction, resource access, GET/POST/DELETE request paths, query params, error handling, `AbortSignal`, and memory pressure.
## API Coverage
| Service | Client property | Methods |
| --- | --- | --- |
| `SessionService` | — | `createSession` · `deleteSession` · `userInfo` |
| `ApplicationService` | `applications` | `list` · `get` · `create` · `update` · `patch` · `sync` · `terminateSync` · `wait` · `rollback` · `deleteByName` · `deleteResource` · `refresh` · `revisionMetadata` · `resourceTree` · `managedResources` · `logs` · `images` · `pods` · `containers` · `nodes` · `health` · `diff` · `events` |
| `ApplicationSetService` | `applicationSets` | `list` · `get` · `create` · `update` · `deleteByName` |
| `ProjectService` | `projects` | `list` · `get` · `create` · `update` · `deleteByName` · `events` · `repositories` |
| `RepositoryService` | `repositories` | `list` · `get` · `create` · `refs` · `apps` · `deleteByRepo` |
| `RepoCredsService` | `repoCreds` | `list` · `create` · `deleteByUrl` |
| `ClusterService` | `clusters` | `list` · `get` · `create` · `update` · `deleteByServer` · `invalidateCache` |
| `AccountService` | `accounts` | `list` · `get` · `canI` · `updatePassword` · `listTokens` · `createToken` · `deleteToken` |
| `CertificateService` | `certificates` | `list` · `create` · `delete` |
| `GPGKeyService` | `gpgKeys` | `list` · `create` · `deleteByKeyId` |
| `SettingsService` | `settings` | `get` |
| `VersionService` | `version` | `get` |
See [ROADMAP.md](ROADMAP.md) for planned additions. Types keep Argo CD/Kubernetes payloads extensible with `Record` for areas where server versions vary.