Hootsuite Local Dev Loop
Overview
Set up a development workflow for Hootsuite API integrations with mocked API responses, token management, and testing.
Instructions
Step 1: Project Structure
hootsuite-integration/
├── src/
│ ├── hootsuite/
│ │ ├── client.ts # API client with token refresh
│ │ ├── auth.ts # OAuth 2.0 flow
│ │ ├── publishing.ts # Message scheduling
│ │ └── analytics.ts # Metrics retrieval
│ └── index.ts
├── tests/
│ ├── fixtures/ # Mock API responses
│ │ ├── profiles.json
│ │ └── messages.json
│ └── publishing.test.ts
├── .env.local
└── package.json
Step 2: API Client with Auto Token Refresh
// src/hootsuite/client.ts
import 'dotenv/config';
class HootsuiteClient {
private accessToken: string;
private refreshToken: string;
private expiresAt: number;
private base = 'https://platform.hootsuite.com/v1';
constructor() {
this.accessToken = process.env.HOOTSUITE_ACCESS_TOKEN!;
this.refreshToken = process.env.HOOTSUITE_REFRESH_TOKEN!;
this.expiresAt = Date.now() + 3600000;
}
async request(path: string, options: RequestInit = {}) {
if (Date.now() > this.expiresAt - 60000) await this.refresh();
const response = await fetch(`${this.base}${path}`, {
...options,
headers: { 'Authorization': `Bearer ${this.accessToken}`, 'Content-Type': 'application/json', ...options.headers },
});
if (!response.ok) throw new Error(`Hootsuite API ${response.status}: ${await response.text()}`);
return response.json();
}
private async refresh() {
const res = await fetch('https://platform.hootsuite.com/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': `Basic ${Buffer.from(`${process.env.HOOTSUITE_CLIENT_ID}:${process.env.HOOTSUITE_CLIENT_SECRET}`).toString('base64')}` },
body: new URLSearchParams({ grant_type: 'refresh_token', refresh_token: this.refreshToken }),
});
const tokens = await res.json();
this.accessToken = tokens.access_token;
this.refreshToken = tokens.refresh_token;
this.expiresAt = Date.now() + tokens.expires_in * 1000;
}
}
export const hootsuite = new HootsuiteClient();
Step 3: Mocked Tests
// tests/publishing.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
const mockFetch = vi.fn();
vi.stubGlobal('fetch', mockFetch);
describe('Hootsuite Publishing', () => {
beforeEach(() => vi.clearAllMocks());
it('should schedule a message', async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => ({ data: [{ id: 'msg_123', state: 'SCHEDULED' }] }),
});
// Test scheduling logic
});
it('should list social profiles', async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => ({ data: [{ id: 'prof_1', type: 'TWITTER', socialNetworkUsername: 'test' }] }),
});
// Test profile listing
});
});
Output
- API client with automatic token refresh
- Mocked test suite
- Project structure for Hootsuite integrations
Resources
Next Steps
See hootsuite-sdk-patterns for production patterns.