Skip to content
OpenSmartRoute
Skillv1.0.0

bamboohr-local-dev-loop

Configure BambooHR local development with hot reload, mocking, and testing. Use when setting up a development environment, configuring test workflows, or establishing a fast iteration cycle with Bambo

by jeremylongshore(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from jeremylongshore/tons-of-skills-marketplace (skills/.curated/bamboohr-local-dev-loop/SKILL.md). Install upstream with npx skills add jeremylongshore/tons-of-skills-marketplace --skill bamboohr-local-dev-loop. Copyright stays with the author (MIT).

BambooHR Local Dev Loop

Overview

Set up a fast, reproducible local development workflow for BambooHR integrations with request mocking, hot reload, and integration testing against the real API.

Prerequisites

  • Completed bamboohr-install-auth setup
  • Node.js 18+ with npm or pnpm
  • BAMBOOHR_API_KEY and BAMBOOHR_COMPANY_DOMAIN set in .env

Instructions

Step 1: Project Structure

my-bamboohr-project/
├── src/
│   ├── bamboohr/
│   │   ├── client.ts       # Reusable API client
│   │   ├── types.ts         # BambooHR response types
│   │   └── employees.ts     # Employee operations
│   └── index.ts
├── tests/
│   ├── mocks/
│   │   └── bamboohr.ts      # API response fixtures
│   ├── unit/
│   │   └── employees.test.ts
│   └── integration/
│       └── bamboohr.test.ts
├── .env.local               # Real API key (git-ignored)
├── .env.example              # Template for team
├── .env.test                 # Test config (sandbox key)
└── package.json

Step 2: Create Reusable API Client

// src/bamboohr/client.ts
import 'dotenv/config';

export class BambooHRClient {
  private baseUrl: string;
  private authHeader: string;

  constructor(companyDomain?: string, apiKey?: string) {
    const domain = companyDomain || process.env.BAMBOOHR_COMPANY_DOMAIN!;
    const key = apiKey || process.env.BAMBOOHR_API_KEY!;
    this.baseUrl = `https://api.bamboohr.com/api/gateway.php/${domain}/v1`;
    this.authHeader = `Basic ${Buffer.from(`${key}:x`).toString('base64')}`;
  }

  async request<T>(path: string, options: RequestInit = {}): Promise<T> {
    const res = await fetch(`${this.baseUrl}${path}`, {
      ...options,
      headers: {
        Authorization: this.authHeader,
        Accept: 'application/json',
        'Content-Type': 'application/json',
        ...options.headers,
      },
    });

    if (!res.ok) {
      const errMsg = res.headers.get('X-BambooHR-Error-Message') || res.statusText;
      throw new BambooHRError(res.status, errMsg, path);
    }

    return res.json() as Promise<T>;
  }

  async getEmployee(id: number | string, fields: string[]): Promise<Record<string, string>> {
    return this.request(`/employees/${id}/?fields=${fields.join(',')}`);
  }

  async getDirectory(): Promise<{ employees: BambooEmployee[] }> {
    return this.request('/employees/directory');
  }
}

export class BambooHRError extends Error {
  constructor(public status: number, message: string, public path: string) {
    super(`BambooHR ${status}: ${message} [${path}]`);
    this.name = 'BambooHRError';
  }
}

Step 3: Setup Hot Reload and Scripts

{
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "test": "vitest run",
    "test:watch": "vitest --watch",
    "test:integration": "DOTENV_CONFIG_PATH=.env.test vitest run tests/integration/",
    "typecheck": "tsc --noEmit"
  },
  "devDependencies": {
    "tsx": "^4.0.0",
    "vitest": "^2.0.0",
    "typescript": "^5.5.0",
    "msw": "^2.0.0"
  }
}

Step 4: Mock BambooHR API with MSW

// tests/mocks/bamboohr.ts
import { http, HttpResponse } from 'msw';

const MOCK_COMPANY = 'testcompany';
const BASE = `https://api.bamboohr.com/api/gateway.php/${MOCK_COMPANY}/v1`;

export const bamboohrHandlers = [
  // Employee directory
  http.get(`${BASE}/employees/directory`, () => {
    return HttpResponse.json({
      fields: [{ id: 'displayName', type: 'text', name: 'Display Name' }],
      employees: [
        {
          id: '1', displayName: 'Jane Smith', firstName: 'Jane',
          lastName: 'Smith', jobTitle: 'Engineer', department: 'Engineering',
          workEmail: 'jane@test.com', location: 'Remote',
        },
        {
          id: '2', displayName: 'Bob Jones', firstName: 'Bob',
          lastName: 'Jones', jobTitle: 'Designer', department: 'Design',
          workEmail: 'bob@test.com', location: 'NYC',
        },
      ],
    });
  }),

  // Single employee
  http.get(`${BASE}/employees/:id/`, ({ params }) => {
    return HttpResponse.json({
      id: params.id, firstName: 'Jane', lastName: 'Smith',
      jobTitle: 'Engineer', department: 'Engineering',
      hireDate: '2023-01-15', workEmail: 'jane@test.com',
      status: 'Active',
    });
  }),

  // Custom report
  http.post(`${BASE}/reports/custom`, () => {
    return HttpResponse.json({
      title: 'Test Report', employees: [
        { firstName: 'Jane', lastName: 'Smith', department: 'Engineering' },
      ],
    });
  }),
];

Step 5: Write Unit Tests

// tests/unit/employees.test.ts
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
import { setupServer } from 'msw/node';
import { bamboohrHandlers } from '../mocks/bamboohr';
import { BambooHRClient } from '../../src/bamboohr/client';

const server = setupServer(...bamboohrHandlers);

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

describe('BambooHRClient', () => {
  const client = new BambooHRClient('testcompany', 'fake-key');

  it('fetches the employee directory', async () => {
    const dir = await client.getDirectory();
    expect(dir.employees).toHaveLength(2);
    expect(dir.employees[0].displayName).toBe('Jane Smith');
  });

  it('fetches a single employee', async () => {
    const emp = await client.getEmployee(1, ['firstName', 'lastName', 'jobTitle']);
    expect(emp.firstName).toBe('Jane');
    expect(emp.jobTitle).toBe('Engineer');
  });
});

Step 6: Integration Test Against Real API

// tests/integration/bamboohr.test.ts
import { describe, it, expect } from 'vitest';
import { BambooHRClient } from '../../src/bamboohr/client';

const HAS_KEY = !!process.env.BAMBOOHR_API_KEY;

describe.skipIf(!HAS_KEY)('BambooHR Integration', () => {
  const client = new BambooHRClient();

  it('should fetch the real employee directory', async () => {
    const dir = await client.getDirectory();
    expect(dir.employees.length).toBeGreaterThan(0);
    expect(dir.employees[0]).toHaveProperty('displayName');
  }, 15_000);
});

Output

  • Reusable BambooHRClient with typed methods
  • MSW mocks for offline development
  • Unit tests with mocked API
  • Integration tests gated on BAMBOOHR_API_KEY presence
  • Hot-reload dev server via tsx watch

Examples

Develop against mocked responses and synthetic employee fixtures by default. If a sandbox call is needed, use a dedicated least-privilege credential, request only the named test fields, avoid caching raw responses outside the protected test directory, and delete the artifact when the test completes.

Error Handling

Error Cause Solution
BambooHRError 401 Wrong key in .env.local Re-copy from BambooHR dashboard
MSW onUnhandledRequest Unmocked endpoint hit Add handler to bamboohrHandlers
ECONNREFUSED in tests MSW server not started Ensure beforeAll(() => server.listen())
Slow integration tests Real API latency Increase vitest timeout to 15s

Resources

Next Steps

See bamboohr-sdk-patterns for production-ready code patterns.

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/jeremylongshore-tons-of-skills-marketplace-bamboohr-loca-ca8c2c/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

jeremylongshore-tons-of-skills-marketplace-bamboohr-loca-ca8c2c.ocm.jsonjson
{
  "ocm": "1",
  "id": "jeremylongshore-tons-of-skills-marketplace-bamboohr-loca-ca8c2c",
  "kind": "skill",
  "name": "bamboohr-local-dev-loop",
  "description": "Configure BambooHR local development with hot reload, mocking, and testing. Use when setting up a development environment, configuring test workflows, or establishing a fast iteration cycle with BambooHR API. Trigger with phrases like \"bamboohr dev setup\", \"bamboohr local development\", \"bamboohr dev environment\", \"develop with bamboohr\", \"bamboohr mock\".",
  "publisher": "jeremylongshore",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "coding"
    ],
    "tags": [
      "skill-md",
      "saas",
      "hr",
      "bamboohr",
      "development",
      "skills-sh"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Configure BambooHR local development with hot reload, mocking, and testing. Use when setting up a development environment, configuring test workflows, or establishing a fast iteration cycle with BambooHR API. Trigger with phrases like \"bamboohr dev setup\", \"bamboohr local development\", \"bamboohr dev environment\", \"develop with bamboohr\", \"bamboohr mock\"."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "skills.sh",
      "repository": "https://github.com/jeremylongshore/tons-of-skills-marketplace",
      "path": "skills/.curated/bamboohr-local-dev-loop/SKILL.md",
      "ref": "HEAD",
      "url": "https://github.com/jeremylongshore/tons-of-skills-marketplace/blob/HEAD/skills/.curated/bamboohr-local-dev-loop/SKILL.md",
      "key": "jeremylongshore/tons-of-skills-marketplace/skills/.curated/bamboohr-local-dev-loop/SKILL.md"
    },
    "compatibility": "Designed for Claude Code",
    "allowed_tools": [
      "Read,",
      "Write,",
      "Edit,",
      "Bash(npm:*),",
      "Bash(pnpm:*),",
      "Grep"
    ],
    "license": "MIT"
  },
  "instructions": "# BambooHR Local Dev Loop\n\n## Overview\n\nSet up a fast, reproducible local development workflow for BambooHR integrations with request mocking, hot reload, and integration testing against the real API.\n\n## Prerequisites\n\n- Completed `bamboohr-install-auth` setup\n- Node.js 18+ with npm or pnpm\n- `BAMBOOHR_API_KEY` and `BAMBOOHR_COMPANY_DOMAIN` set in `.env`\n\n## Instructions\n\n### Step 1: Project Structure\n\n```\nmy-bamboohr-project/\n├── src/\n│   ├── bamboohr/\n│   │   ├── client.ts       # Reusable API client\n│   │   ├── types.ts         # BambooHR response types\n│   │   └── employees.ts     # Emplo",
  "cost": {
    "context_tokens": 1840
  }
}

Fetch it by URL: GET /api/v1/registry/jeremylongshore-tons-of-skills-marketplace-bamboohr-loca-ca8c2c/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.