Skip to content

Commit ac84f48

Browse files
committed
feat: Upgrade to Astrology API TypeScript SDK v1.0.0
- Migrated from RapidAPI to direct API access at `https://api.astrology-api.io` - Changed authentication method to use `Authorization: Bearer <token>` header - Renamed environment variable from `RAPIDAPI_KEY` to `ASTROLOGY_API_KEY` - Updated default base URL and removed unnecessary RapidAPI configurations - Adjusted documentation and examples to reflect new API structure and environment variables This release includes breaking changes; please refer to the migration guide for details.
1 parent 02d7a0d commit ac84f48

9 files changed

Lines changed: 62 additions & 106 deletions

File tree

.cursorrules

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
- Keep code DRY, KISS, and single-responsibility. Extract helpers if logic repeats.
2020
- Comments, docs, and logs MUST be written in English.
2121
- When adding HTTP endpoints ensure:
22-
- URL assembled via RapidAPI base URL (default from `DEFAULT_RAPIDAPI_HOST`)
23-
- RapidAPI headers are set (`x-rapidapi-key`, `x-rapidapi-host`)
24-
- Environment variables: `RAPIDAPI_KEY`, `RAPIDAPI_HOST`, `ASTROLOGY_API_BASE_URL`
22+
- URL assembled via default base URL (`https://api.astrology-api.io`)
23+
- Authorization header is set (`Authorization: Bearer <token>`)
24+
- Environment variables: `ASTROLOGY_API_KEY`, `ASTROLOGY_API_BASE_URL`
2525
- Payload validated via `validators.ts`.
2626
- For additions touching API schema, consult `docs-openapi.json` first.
2727
- Update or add Vitest suites and maintain 100% coverage. Use axios-mock-adapter for HTTP mocks.

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.0.0] - 2026-01-21
9+
10+
### Changed
11+
12+
- **BREAKING**: Migrated from RapidAPI to direct API access at `https://api.astrology-api.io`
13+
- **BREAKING**: Authentication changed from `x-rapidapi-key` header to `Authorization: Bearer <token>`
14+
- **BREAKING**: Environment variable renamed from `RAPIDAPI_KEY` to `ASTROLOGY_API_KEY`
15+
- Removed `rapidApiHost` configuration option (no longer needed)
16+
- Removed `RAPIDAPI_HOST` environment variable support
17+
- Updated default base URL to `https://api.astrology-api.io`
18+
19+
### Migration Guide
20+
21+
To upgrade from 0.1.0 to 1.0.0:
22+
23+
1. Rename environment variable `RAPIDAPI_KEY` to `ASTROLOGY_API_KEY`
24+
2. Remove any `rapidApiHost` configuration option
25+
3. If using custom `baseUrl`, update it to point to the new API server
26+
827
## [0.1.0] - 2024-01-01
928

1029
### Added

CLAUDE.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## Project Overview
66

7-
TypeScript SDK (`@procoders/astrology-api-client`) for the Astrology API v3.0.0. The OpenAPI spec is cached locally in `docs-openapi.json`.
7+
TypeScript SDK (`@procoders/astrology-api-client`) for the Astrology API v3.2.10. The OpenAPI spec is cached locally in `docs-openapi.json`.
88

99
- **Runtime**: Node.js 22+ (ES modules)
1010
- **HTTP layer**: Axios with request/response interceptors, automatic retry with backoff
@@ -37,7 +37,7 @@ npx vitest run -t "should retry"
3737
### Client Structure
3838

3939
`AstrologyClient` ([src/client.ts](src/client.ts)) is the entry point. It creates an Axios instance with interceptors for:
40-
- API key injection (`X-API-Key` header)
40+
- Bearer token authentication (`Authorization: Bearer <token>` header)
4141
- Debug logging
4242
- Automatic retry on 429/500/502/503/504 and network errors
4343
- Response unwrapping (extracts `data` or `result` from response payloads)
@@ -102,7 +102,6 @@ Each API family has a dedicated client in [src/categories/](src/categories/) tha
102102

103103
| Variable | Description |
104104
|----------|-------------|
105-
| `RAPIDAPI_KEY` | RapidAPI key (auto-injected if not passed to constructor) |
106-
| `RAPIDAPI_HOST` | Override the default RapidAPI host |
107-
| `ASTROLOGY_API_BASE_URL` | Override the full API base URL |
105+
| `ASTROLOGY_API_KEY` | API key for Bearer token authentication (auto-injected if not passed to constructor) |
106+
| `ASTROLOGY_API_BASE_URL` | Override the full API base URL (default: https://api.astrology-api.io) |
108107
| `ASTROLOGY_DEBUG` | Set to `true` to enable debug logging |

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![npm downloads](https://img.shields.io/npm/dm/@procoders/astrology-api-client.svg)](https://www.npmjs.com/package/@procoders/astrology-api-client)
66
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
77

8-
Type-safe Node.js client for the [Astrology API v3.0.0](https://api.astrology-api.io/rapidoc). The package ships as an ESM build, exposes a modular architecture with dedicated sub-clients per endpoint family, and enforces 100 % test coverage. Publish-ready as `@procoders/astrology-api-client`.
8+
Type-safe Node.js client for the [Astrology API v3.2.10](https://api.astrology-api.io/rapidoc). The package ships as an ESM build, exposes a modular architecture with dedicated sub-clients per endpoint family, and enforces 100 % test coverage. Publish-ready as `@procoders/astrology-api-client`.
99

1010
### Highlights
1111
- Axios-powered HTTP layer with retry/backoff, header normalization, and response unwrapping
@@ -34,9 +34,8 @@ const { AstrologyClient } = require('@procoders/astrology-api-client');
3434
### Environment Variables
3535
| Variable | Required | Description |
3636
| --- | --- | --- |
37-
| `RAPIDAPI_KEY` | **required** | Your RapidAPI key for authentication |
38-
| `RAPIDAPI_HOST` | optional | Override the default RapidAPI host |
39-
| `ASTROLOGY_API_BASE_URL` | optional | Override the full API base URL |
37+
| `ASTROLOGY_API_KEY` | **required** | Your API key for Bearer token authentication |
38+
| `ASTROLOGY_API_BASE_URL` | optional | Override the full API base URL (default: https://api.astrology-api.io) |
4039
| `ASTROLOGY_DEBUG` | optional | Set to `true` to enable debug logging |
4140
| `RUN_ASTROLOGY_EXAMPLE` | optional | Set to `true` to execute `examples/usage.ts` |
4241

@@ -45,7 +44,7 @@ const { AstrologyClient } = require('@procoders/astrology-api-client');
4544
import { AstrologyClient } from '@procoders/astrology-api-client';
4645

4746
const client = new AstrologyClient({
48-
apiKey: 'your-rapidapi-key-here', // or set RAPIDAPI_KEY env var
47+
apiKey: 'your-api-key-here', // or set ASTROLOGY_API_KEY env var
4948
retry: { attempts: 2, delayMs: 250 },
5049
debug: process.env.ASTROLOGY_DEBUG === 'true',
5150
});

examples/usage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import type {
1010

1111
export async function runExample(): Promise<void> {
1212
const client = new AstrologyClient({
13-
apiKey: process.env.RAPIDAPI_KEY,
13+
apiKey: process.env.ASTROLOGY_API_KEY,
1414
retry: { attempts: 2, delayMs: 250 },
1515
debug: process.env.ASTROLOGY_DEBUG === 'true',
1616
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@procoders/astrology-api-client",
3-
"version": "0.1.0",
3+
"version": "1.0.0",
44
"description": "TypeScript SDK for interacting with the Astrology API.",
55
"keywords": [
66
"astrology",

src/client.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
AstrologyClientConfig,
1111
AstrologyLogger,
1212
DEFAULT_BASE_URL,
13-
DEFAULT_RAPIDAPI_HOST,
1413
DEFAULT_RETRY_STATUS_CODES,
1514
DEFAULT_TIMEOUT_MS,
1615
} from './types';
@@ -56,7 +55,6 @@ export class AstrologyClient {
5655
private readonly axiosInstance: AxiosInstance;
5756
private readonly retryConfig: NormalizedRetryConfig;
5857
private readonly apiKey?: string;
59-
private readonly rapidApiHost: string;
6058
private readonly debugEnabled: boolean;
6159
private readonly logger: AstrologyLogger;
6260
private readonly logPrefix = '[AstrologyClient]';
@@ -98,7 +96,6 @@ export class AstrologyClient {
9896
const baseURL = this.resolveBaseUrl(config.baseURL ?? config.baseUrl);
9997
const timeout = typeof config.timeout === 'number' ? config.timeout : DEFAULT_TIMEOUT_MS;
10098
this.apiKey = this.resolveApiKey(config.apiKey);
101-
this.rapidApiHost = this.resolveRapidApiHost(config.rapidApiHost);
10299
this.debugEnabled = this.resolveDebugFlag(config.debug);
103100
this.logger = config.logger ?? console.log;
104101
this.retryConfig = {
@@ -154,12 +151,9 @@ export class AstrologyClient {
154151
set?: (name: string, value: string) => void;
155152
};
156153

157-
// Set RapidAPI host header
158-
this.setHeaderIfMissing(headers, 'x-rapidapi-host', this.rapidApiHost);
159-
160-
// Set RapidAPI key header
154+
// Set Authorization header with Bearer token
161155
if (this.apiKey) {
162-
this.setHeaderIfMissing(headers, 'x-rapidapi-key', this.apiKey);
156+
this.setHeaderIfMissing(headers, 'Authorization', `Bearer ${this.apiKey}`);
163157
}
164158

165159
this.log('Outgoing request', {
@@ -272,19 +266,10 @@ export class AstrologyClient {
272266
if (fromConfig) {
273267
return fromConfig;
274268
}
275-
const fromEnv = process.env.RAPIDAPI_KEY?.trim();
269+
const fromEnv = process.env.ASTROLOGY_API_KEY?.trim();
276270
return fromEnv || undefined;
277271
}
278272

279-
private resolveRapidApiHost(host?: string): string {
280-
const fromConfig = host?.trim();
281-
if (fromConfig) {
282-
return fromConfig;
283-
}
284-
const fromEnv = process.env.RAPIDAPI_HOST?.trim();
285-
return fromEnv || DEFAULT_RAPIDAPI_HOST;
286-
}
287-
288273
private resolveBaseUrl(baseURL?: string): string {
289274
if (baseURL && baseURL.trim()) {
290275
return baseURL;

src/types/config.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,14 @@ export type AstrologyLogger = (message: string, details?: unknown) => void;
1010

1111
export interface AstrologyClientConfig {
1212
/**
13-
* RapidAPI key for authentication. If omitted, the client will fall back to
14-
* the RAPIDAPI_KEY environment variable.
13+
* API key for Bearer token authentication. If omitted, the client will fall back to
14+
* the ASTROLOGY_API_KEY environment variable.
1515
*/
1616
apiKey?: string;
1717
/**
18-
* RapidAPI host header value. If omitted, defaults to the RapidAPI host.
19-
* Can also be set via RAPIDAPI_HOST environment variable.
20-
*/
21-
rapidApiHost?: string;
22-
/**
23-
* Optional override for the API base URL. When omitted, the client uses the RapidAPI
24-
* endpoint. `baseUrl` is accepted as a camelCase alias.
18+
* Optional override for the API base URL. When omitted, the client uses the default
19+
* endpoint (https://api.astrology-api.io). `baseUrl` is accepted as a camelCase alias.
20+
* Can also be set via ASTROLOGY_API_BASE_URL environment variable.
2521
*/
2622
baseUrl?: string;
2723
baseURL?: string;
@@ -38,9 +34,7 @@ export interface AstrologyClientConfig {
3834
logger?: AstrologyLogger;
3935
}
4036

41-
export const DEFAULT_RAPIDAPI_HOST =
42-
'best-astrology-api-natal-charts-transits-synastry.p.rapidapi.com';
43-
export const DEFAULT_BASE_URL = `https://${DEFAULT_RAPIDAPI_HOST}`;
37+
export const DEFAULT_BASE_URL = 'https://api.astrology-api.io';
4438
export const DEFAULT_TIMEOUT_MS = 10000;
4539
export const DEFAULT_RETRY_STATUS_CODES = [408, 425, 429, 500, 502, 503, 504];
4640

tests/unit/client.test.ts

Lines changed: 21 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
AstrologyClient,
66
AstrologyError,
77
DEFAULT_BASE_URL,
8-
DEFAULT_RAPIDAPI_HOST,
98
DEFAULT_RETRY_STATUS_CODES,
109
} from '../../src';
1110
import {
@@ -68,8 +67,7 @@ const buildAxiosError = (status: number, message: string, config?: AxiosRequestC
6867
);
6968
};
7069

71-
const ORIGINAL_ENV_API_KEY = process.env.RAPIDAPI_KEY;
72-
const ORIGINAL_ENV_HOST = process.env.RAPIDAPI_HOST;
70+
const ORIGINAL_ENV_API_KEY = process.env.ASTROLOGY_API_KEY;
7371
const ORIGINAL_DEBUG_ENV = process.env.ASTROLOGY_DEBUG;
7472

7573
describe('AstrologyClient', () => {
@@ -78,14 +76,9 @@ describe('AstrologyClient', () => {
7876

7977
beforeEach(() => {
8078
if (ORIGINAL_ENV_API_KEY === undefined) {
81-
delete process.env.RAPIDAPI_KEY;
79+
delete process.env.ASTROLOGY_API_KEY;
8280
} else {
83-
process.env.RAPIDAPI_KEY = ORIGINAL_ENV_API_KEY;
84-
}
85-
if (ORIGINAL_ENV_HOST === undefined) {
86-
delete process.env.RAPIDAPI_HOST;
87-
} else {
88-
process.env.RAPIDAPI_HOST = ORIGINAL_ENV_HOST;
81+
process.env.ASTROLOGY_API_KEY = ORIGINAL_ENV_API_KEY;
8982
}
9083
if (ORIGINAL_DEBUG_ENV === undefined) {
9184
delete process.env.ASTROLOGY_DEBUG;
@@ -105,14 +98,9 @@ describe('AstrologyClient', () => {
10598
afterEach(() => {
10699
mock.reset();
107100
if (ORIGINAL_ENV_API_KEY === undefined) {
108-
delete process.env.RAPIDAPI_KEY;
109-
} else {
110-
process.env.RAPIDAPI_KEY = ORIGINAL_ENV_API_KEY;
111-
}
112-
if (ORIGINAL_ENV_HOST === undefined) {
113-
delete process.env.RAPIDAPI_HOST;
101+
delete process.env.ASTROLOGY_API_KEY;
114102
} else {
115-
process.env.RAPIDAPI_HOST = ORIGINAL_ENV_HOST;
103+
process.env.ASTROLOGY_API_KEY = ORIGINAL_ENV_API_KEY;
116104
}
117105
if (ORIGINAL_DEBUG_ENV === undefined) {
118106
delete process.env.ASTROLOGY_DEBUG;
@@ -123,14 +111,9 @@ describe('AstrologyClient', () => {
123111

124112
afterAll(() => {
125113
if (ORIGINAL_ENV_API_KEY === undefined) {
126-
delete process.env.RAPIDAPI_KEY;
114+
delete process.env.ASTROLOGY_API_KEY;
127115
} else {
128-
process.env.RAPIDAPI_KEY = ORIGINAL_ENV_API_KEY;
129-
}
130-
if (ORIGINAL_ENV_HOST === undefined) {
131-
delete process.env.RAPIDAPI_HOST;
132-
} else {
133-
process.env.RAPIDAPI_HOST = ORIGINAL_ENV_HOST;
116+
process.env.ASTROLOGY_API_KEY = ORIGINAL_ENV_API_KEY;
134117
}
135118
if (ORIGINAL_DEBUG_ENV === undefined) {
136119
delete process.env.ASTROLOGY_DEBUG;
@@ -146,10 +129,9 @@ describe('AstrologyClient', () => {
146129
},
147130
});
148131

149-
it('sends RapidAPI headers and returns planetary positions', async () => {
132+
it('sends Authorization Bearer header and returns planetary positions', async () => {
150133
mock.onPost('/api/v3/data/positions').reply((config) => {
151-
expect(config.headers?.['x-rapidapi-key']).toBe('test-key');
152-
expect(config.headers?.['x-rapidapi-host']).toBeDefined();
134+
expect(config.headers?.['Authorization']).toBe('Bearer test-key');
153135
const payload = JSON.parse(config.data) as PlanetaryPositionsRequest;
154136
expect(payload.subject.birth_data.year).toBe(1990);
155137
return [200, mockPlanetaryPositionsResponse];
@@ -400,7 +382,7 @@ describe('AstrologyClient', () => {
400382
},
401383
axiosOptions: {
402384
headers: {
403-
'x-rapidapi-key': 'pre-set',
385+
Authorization: 'Bearer pre-set',
404386
'X-Custom-Header': 'value',
405387
},
406388
},
@@ -410,7 +392,7 @@ describe('AstrologyClient', () => {
410392
customMock.onPost('/api/v3/data/positions').reply((config) => {
411393
expect(config.baseURL).toBe('https://custom.example.com');
412394
expect(config.timeout).toBe(5000);
413-
expect(config.headers?.['x-rapidapi-key']).toBe('pre-set');
395+
expect(config.headers?.['Authorization']).toBe('Bearer pre-set');
414396
expect(config.headers?.['X-Custom-Header']).toBe('value');
415397
return [200, mockPlanetaryPositionsResponse];
416398
});
@@ -419,34 +401,34 @@ describe('AstrologyClient', () => {
419401
});
420402

421403
it('uses environment API key when config omits one', async () => {
422-
const original = process.env.RAPIDAPI_KEY;
423-
process.env.RAPIDAPI_KEY = 'env-key';
404+
const original = process.env.ASTROLOGY_API_KEY;
405+
process.env.ASTROLOGY_API_KEY = 'env-key';
424406
const envClient = new AstrologyClient({
425407
retry: { attempts: 0 },
426408
});
427409
const envMock = new MockAdapter(envClient.httpClient);
428410
envMock.onPost('/api/v3/data/positions').reply((config) => {
429-
expect(config.headers?.['x-rapidapi-key']).toBe('env-key');
411+
expect(config.headers?.['Authorization']).toBe('Bearer env-key');
430412
return [200, mockPlanetaryPositionsResponse];
431413
});
432414

433415
await envClient.data.getPositions(createPositionsRequest());
434416
envMock.reset();
435417
if (original === undefined) {
436-
delete process.env.RAPIDAPI_KEY;
418+
delete process.env.ASTROLOGY_API_KEY;
437419
} else {
438-
process.env.RAPIDAPI_KEY = original;
420+
process.env.ASTROLOGY_API_KEY = original;
439421
}
440422
});
441423

442-
it('does not set API key header when no key provided', async () => {
443-
delete process.env.RAPIDAPI_KEY;
424+
it('does not set Authorization header when no key provided', async () => {
425+
delete process.env.ASTROLOGY_API_KEY;
444426
const anonymousClient = new AstrologyClient({
445427
retry: { attempts: 0 },
446428
});
447429
const anonymousMock = new MockAdapter(anonymousClient.httpClient);
448430
anonymousMock.onPost('/api/v3/data/positions').reply((config) => {
449-
expect(config.headers?.['x-rapidapi-key']).toBeUndefined();
431+
expect(config.headers?.['Authorization']).toBeUndefined();
450432
return [200, mockPlanetaryPositionsResponse];
451433
});
452434

@@ -559,26 +541,6 @@ describe('AstrologyClient', () => {
559541
}
560542
});
561543

562-
it('uses custom rapidApiHost when provided in config', async () => {
563-
const customHostClient = new AstrologyClient({
564-
apiKey: 'test-key',
565-
rapidApiHost: 'custom-host.example.com',
566-
retry: { attempts: 0 },
567-
});
568-
const customHostMock = new MockAdapter(customHostClient.httpClient);
569-
customHostMock.onPost('/api/v3/data/positions').reply((config) => {
570-
expect(config.headers?.['x-rapidapi-host']).toBe('custom-host.example.com');
571-
return [200, mockPlanetaryPositionsResponse];
572-
});
573-
574-
await customHostClient.data.getPositions(createPositionsRequest());
575-
});
576-
577-
it('falls back to default RapidAPI host when not provided', () => {
578-
const host = (client as any).resolveRapidApiHost(undefined);
579-
expect(host).toBe(DEFAULT_RAPIDAPI_HOST);
580-
});
581-
582544
it('clamps retry values to non-negative numbers', () => {
583545
expect((client as any).clampToNonNegative(undefined, 5)).toBe(5);
584546
expect((client as any).clampToNonNegative(NaN, 5)).toBe(5);
@@ -595,17 +557,15 @@ describe('AstrologyClient', () => {
595557
it('initializes missing headers inside request interceptor', () => {
596558
const interceptor = client.httpClient.interceptors.request.handlers[0].fulfilled!;
597559
const result = interceptor({} as any);
598-
expect(result.headers['x-rapidapi-key']).toBe('test-key');
599-
expect(result.headers['x-rapidapi-host']).toBeDefined();
560+
expect(result.headers['Authorization']).toBe('Bearer test-key');
600561
});
601562

602563
it('delegates to headers.set when available', () => {
603564
const set = vi.fn();
604565
const has = vi.fn().mockReturnValue(false);
605566
const interceptor = client.httpClient.interceptors.request.handlers[0].fulfilled!;
606567
interceptor({ headers: { set, has } } as any);
607-
expect(set).toHaveBeenCalledWith('x-rapidapi-host', expect.any(String));
608-
expect(set).toHaveBeenCalledWith('x-rapidapi-key', 'test-key');
568+
expect(set).toHaveBeenCalledWith('Authorization', 'Bearer test-key');
609569
});
610570
});
611571

0 commit comments

Comments
 (0)