Skip to main content

New App Template

This guide covers initialization patterns and templates for creating new Farseer applications.

Initialization Patterns

When running inside a Farseer environment, the client auto-configures from environment variables:

import { FarseerClient } from 'farseer-client';

const client = new FarseerClient();

Required environment variables:

VariableDescription
TENANT_IDYour Farseer tenant identifier
FARSEER_API_KEYAPI authentication key
FARSEER_URLBase URL (e.g., https://app.farseer.io)

Manual Configuration (Development)

const client = new FarseerClient({
basePath: 'https://app.farseer.io/api/v3',
headers: {
'X-TENANT-ID': 'my-tenant-id',
'X-API-KEY': 'my-api-key',
},
});

tip

Always implement local initialization for testing before deploying to production. This prevents accidental data modifications during development.

import * as farseer from 'farseer-client';

// Environment detection
const CFG_DEV_ENV =
process.env.FARSEER_URL === undefined &&
process.env.FARSEER_API_KEY === undefined;

// Development credentials
const DEV_TENANT_ID = 'your-tenant-dev';
const DEV_API_KEY = 'your_dev_api_key_here';
const DEV_FARSEER_URL = 'https://your-tenant-dev.farseer.io/api/v3';

async function main() {
let client: farseer.FarseerClient;

if (CFG_DEV_ENV) {
console.log('Running in DEVELOPMENT mode');
console.log(` Tenant: ${DEV_TENANT_ID}`);
console.log(` URL: ${DEV_FARSEER_URL}`);
client = new farseer.FarseerClient(DEV_TENANT_ID, DEV_API_KEY, DEV_FARSEER_URL);
} else {
console.log('Running in PRODUCTION mode (using environment variables)');
client = new farseer.FarseerClient();
}

// Your application logic here...
}

main().catch(farseer.handleUnknownError);

Why this pattern?

  1. Safety - Test all logic against a development instance first
  2. Visibility - Console logs make it obvious which environment you're targeting
  3. Easy switching - Just set environment variables for production
  4. No code changes - Same code runs in both environments

Project Structure

your-app/
├── index.ts # Entry point with command routing
├── operations/
│ ├── syncSales.ts # Operation implementation
│ └── syncProducts.ts # Operation implementation
├── tagIds.ts # Hardcoded Farseer IDs (optional)
├── util.ts # Helper functions (optional)
└── credentials.ts # External service credentials (optional)

Entry Point with Command Routing

import * as farseer from 'farseer-client';

enum SelectArgument {
SYNC_SALES = 'sync-sales',
SYNC_PRODUCTS = 'sync-products',
}

async function main() {
const args = process.argv.slice(2);
const command = args[0] as SelectArgument;

const client = CFG_DEV_ENV
? new farseer.FarseerClient(DEV_TENANT_ID, DEV_API_KEY, DEV_URL)
: new farseer.FarseerClient();

switch (command) {
case SelectArgument.SYNC_SALES:
await syncSales(client);
break;
case SelectArgument.SYNC_PRODUCTS:
await syncProducts(client);
break;
default:
console.log(`Unknown command: ${command}`);
console.log(`Available: ${Object.values(SelectArgument).join(', ')}`);
}
}

main().catch(farseer.handleUnknownError);

Tag IDs File (Optional)

For apps that need hardcoded IDs:

export const TAG_IDS = {
DIMENSION_TABLES: {
PRODUCTS: 123,
YEARS: 124,
MONTHS: 125,
},
VARIABLES: {
REVENUE: 200,
COSTS: 201,
},
VERSIONS: {
ACTUAL: 26,
PLAN: 27,
},
};

Error Handling

Standard pattern:

main().catch(farseer.handleUnknownError);

Custom error handling:

main().catch(async (error: any) => {
if (error && typeof error.text === 'function') {
console.error(await error.text());
} else {
console.error(error);
}
process.exit(1);
});

Checklist for New Apps

  1. Create project folder
  2. Set up index.ts with dev/prod initialization pattern
  3. Create tagIds.ts with Farseer dimension/variable IDs (if needed)
  4. Implement operations in separate files
  5. Test in development mode first
  6. Verify imports work correctly before deploying