@kodes.agency/gbp-client
v0.1.0
Published
TypeScript client library for Google Business Profile APIs with discovery-driven code generation
Maintainers
Readme
@kodes.agency/gbp-client
A TypeScript client library for Google Business Profile APIs with OAuth 2.0 authentication, discovery-driven API methods, automatic retry, and comprehensive error handling.
Features
- 🔐 OAuth 2.0 Authentication - Service account and OAuth2 client credentials support
- 🔍 Discovery-driven APIs - Dynamic API methods based on Google's discovery documents
- 🔄 Automatic Retry - Exponential backoff with configurable retry policies
- ✅ Request/Response Validation - Zod schemas for type-safe API calls
- 📝 Configurable Logging - Built-in logger with adjustable log levels
- 🎯 Convenience APIs - High-level methods for common operations
Installation
npm install @kodes.agency/gbp-client
# or
pnpm add @kodes.agency/gbp-clientPrerequisites
- Node.js 20.0.0 or higher
- Google Cloud project with Business Profile API enabled
- Service account credentials or OAuth2 client credentials
Quick Start
1. Set up credentials
Create a .env file in your project root:
GOOGLE_CLIENT_EMAIL=your-service-account@project.iam.gserviceaccount.com
GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"2. Initialize the client
import { createClient, isOk, isErr } from '@kodes.agency/gbp-client';
const result = await createClient({
credentials: {
type: 'service_account',
clientEmail: process.env.GOOGLE_CLIENT_EMAIL!,
privateKey: process.env.GOOGLE_PRIVATE_KEY!,
},
})();
if (isOk(result)) {
const client = result.right;
// List your accounts
const accounts = await client.api.accounts.list();
if (isOk(accounts)) {
console.log('Accounts:', accounts.right.accounts);
}
} else {
console.error('Failed to initialize client:', result.left.message);
}3. Use the convenience APIs
// List locations for an account
const locations = await client.api.locations.list('accounts/{accountId}');
// List reviews for a location
const reviews = await client.api.reviews.list('accounts/{accountId}/locations/{locationId}');
// Reply to a review
const reply = await client.api.reviews.reply(
'accounts/{accountId}/locations/{locationId}/reviews/{reviewId}',
'Thank you for your feedback!'
);Authentication
The library supports two authentication methods:
Service Account (Recommended for server-side applications)
import { createClient, isOk } from '@kodes.agency/gbp-client';
const result = await createClient({
credentials: {
type: 'service_account',
clientEmail: process.env.GOOGLE_CLIENT_EMAIL!,
privateKey: process.env.GOOGLE_PRIVATE_KEY!,
},
})();Setup:
- Create a service account in Google Cloud Console
- Download the JSON key file
- Enable domain-wide delegation if accessing user data
- Grant the service account access to your Business Profile locations
OAuth2 Client Credentials
import { createClient, isOk } from '@kodes.agency/gbp-client';
const result = await createClient({
credentials: {
type: 'oauth2_client',
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
refreshToken: process.env.GOOGLE_REFRESH_TOKEN!,
},
})();Setup:
- Create OAuth2 credentials in Google Cloud Console
- Obtain a refresh token through the OAuth2 authorization flow
- The library automatically refreshes access tokens
Configuration
import { createClient } from '@kodes.agency/gbp-client';
const result = await createClient({
credentials: {
type: 'service_account',
clientEmail: process.env.GOOGLE_CLIENT_EMAIL!,
privateKey: process.env.GOOGLE_PRIVATE_KEY!,
},
// Optional configuration
scopes: ['https://www.googleapis.com/auth/business.manage'],
timeout: 30000, // 30 seconds
})();Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| credentials | Credentials | required | Authentication credentials |
| scopes | string[] | ['https://www.googleapis.com/auth/business.manage'] | OAuth scopes |
| timeout | number | 30000 | Request timeout in milliseconds |
API Overview
Client Interface
The client provides access to all Business Profile APIs:
interface Client {
// Token management
getTokenManager(): TokenManager;
getAccessToken(): TaskEither<ApiError, string>;
// Discovery-based API access
loadDiscovery(apiName: string): TaskEither<ApiError, ParsedDiscoveryDocument>;
getSchemaRegistry(): SchemaRegistry;
checkVersion(apiName: string, minVersion?: string): TaskEither<ApiError, VersionInfo>;
// Convenience APIs
api: ConvenienceApis;
// Lifecycle
close(): Promise<void>;
}Convenience APIs
High-level methods for common operations:
Accounts API
// List all accessible accounts
const accounts = await client.api.accounts.list();
// Get a specific account
const account = await client.api.accounts.get('accounts/{accountId}');Locations API
// List locations for an account
const locations = await client.api.locations.list('accounts/{accountId}', {
pageSize: 50,
filter: 'location.state=ACTIVE',
});
// Get a specific location
const location = await client.api.locations.get('accounts/{accountId}/locations/{locationId}');
// Update a location
const updated = await client.api.locations.update(
'accounts/{accountId}/locations/{locationId}',
{ title: 'New Business Name' },
'title' // updateMask
);Reviews API
// List reviews for a location
const reviews = await client.api.reviews.list('accounts/{accountId}/locations/{locationId}');
// Get a specific review
const review = await client.api.reviews.get('accounts/{accountId}/locations/{locationId}/reviews/{reviewId}');
// Reply to a review
const reply = await client.api.reviews.reply(
'accounts/{accountId}/locations/{locationId}/reviews/{reviewId}',
'Thank you for your feedback!'
);Error Handling
The library uses a Result type pattern for explicit error handling. All async operations return TaskEither<ApiError, T> from fp-ts.
Using isOk/isErr
import { createClient, isOk, isErr } from '@kodes.agency/gbp-client';
const result = await createClient({ credentials })();
if (isOk(result)) {
const client = result.right;
// Success - use the client
} else {
const error = result.left;
console.error(`Error [${error.code}]: ${error.message}`);
if (error.recovery) {
console.log(`Suggested action: ${error.recovery.action}`);
}
}Using fp-ts pipe
import { pipe } from 'fp-ts/function';
import * as TE from 'fp-ts/TaskEither';
import { createClient, isOk } from '@kodes.agency/gbp-client';
const program = pipe(
createClient({ credentials }),
TE.chain((client) => client.api.accounts.list()),
TE.map((response) => response.accounts),
TE.mapLeft((error) => `Failed: ${error.message}`)
);
const result = await program();Error Codes Reference
| Code | Description | Recovery Action |
|------|-------------|-----------------|
| AUTHENTICATION_FAILED | Failed to authenticate with Google | Verify credentials are correct and have required scopes |
| AUTHORIZATION_FAILED | Authenticated but not authorized for resource | Check account permissions and API access |
| INVALID_CREDENTIALS | Credentials format is invalid | Verify credentials JSON structure matches expected format |
| TOKEN_EXPIRED | Access token has expired | Token refresh is automatic; if persistent, check system clock |
| DISCOVERY_FAILED | Failed to fetch API discovery document | Check network connectivity; API may be temporarily unavailable |
| VALIDATION_ERROR | Request parameters failed validation | Check parameter types and required fields |
| RATE_LIMITED | API rate limit exceeded | Implement exponential backoff; reduce request frequency |
| SERVER_ERROR | Server returned 5xx error | Retry with backoff; check Google Cloud status |
| NETWORK_ERROR | Network request failed | Check internet connectivity and DNS resolution |
| TIMEOUT | Request exceeded timeout limit | Increase timeout in config or reduce payload size |
| PARSE_ERROR | Failed to parse API response | Response may be malformed; check API version compatibility |
| RETRY_EXHAUSTED | All retry attempts failed | Check underlying error; may need manual intervention |
| UNKNOWN_ERROR | Unexpected error occurred | Check error details for more information |
Examples
For more detailed examples, see USAGE.md.
- List Accounts
- List Locations with Pagination
- Get/Update Location
- List Reviews
- Reply to Reviews
- Error Handling Patterns
License
MIT
