Skip to content
Back to skills

Bamboohr Local Dev Loop

ASecurity

'Configure BambooHR local development with hot reload, mocking, and testing.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 11, 2026
testingtypescriptphpbashnodetestinggitapidocumentation

Works with

  • claude code
  • cli
  • api

Security analysis

A100/100

Scanned November 11, 2026

npx -y skills add pauldx/claudifying --skill bamboohr-local-dev-loop --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Bamboohr Local Dev Loop?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Bamboohr Local Dev Loop
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/pauldx-bamboohr-local-dev-loop/badge)](https://www.skillsdirectory.com/skills/pauldx-bamboohr-local-dev-loop)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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".

  '
allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(pnpm:*), Grep
version: 1.0.0
license: MIT
tags:
- saas
- hr
- bamboohr
- development
compatibility: Designed for Claude Code
---
# 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

```typescript
// 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

```json
{
  "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

```typescript
// 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

```typescript
// 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

```typescript
// 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`

## 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

- [MSW Documentation](https://mswjs.io/)
- [Vitest Documentation](https://vitest.dev/)
- [BambooHR API Technical Overview](https://documentation.bamboohr.com/docs/api-details)

## Next Steps

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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in testing →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.