Skip to content

Commit 850d75d

Browse files
committed
Add EU endpoint support and update README
Add optional baseUrl constructor parameter so users can target the EU endpoint (eu-api.ipdata.co) or any custom URL. Export EU_BASE_URL constant. Update README with EU endpoint docs and response fields reference for company, carrier, and threat objects. https://claude.ai/code/session_01UabaLo3R3sXovyxaZo2yCF
1 parent c47867f commit 850d75d

File tree

3 files changed

+47
-4
lines changed

3 files changed

+47
-4
lines changed

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ JavaScript library that can be used in a web browser or Node.js application to g
1010
- [Use](#use)
1111
- [Import Library](#import-library)
1212
- [Create an Instance](#create-an-instance)
13+
- [EU Endpoint](#eu-endpoint)
1314
- [Lookup](#lookup)
1415
- [Bulk Lookup](#bulk-lookup)
16+
- [Response Fields](#response-fields)
1517

1618
## Install
1719

@@ -62,6 +64,22 @@ const cacheConfig = {
6264
const ipdata = new IPData('<apiKey>', cacheConfig);
6365
```
6466

67+
### EU Endpoint
68+
69+
By default requests are routed to the global endpoint (`https://api.ipdata.co`). To ensure end user data stays in the EU, pass the EU endpoint as the third parameter.
70+
71+
```js
72+
import IPData, { EU_BASE_URL } from 'ipdata';
73+
74+
const ipdata = new IPData('<apiKey>', undefined, EU_BASE_URL);
75+
```
76+
77+
You can also pass a custom base URL if needed.
78+
79+
```js
80+
const ipdata = new IPData('<apiKey>', undefined, 'https://eu-api.ipdata.co/');
81+
```
82+
6583
### Lookup
6684

6785
The library will lookup the ip address of the host computer if no ip address is provided.
@@ -131,3 +149,15 @@ ipdata.bulkLookup(ips, fields)
131149
// ...
132150
});
133151
```
152+
153+
## Response Fields
154+
155+
The following fields are available for use with `selectField` and `fields` parameters:
156+
157+
`ip`, `is_eu`, `city`, `region`, `region_code`, `country_name`, `country_code`, `continent_name`, `continent_code`, `latitude`, `longitude`, `asn`, `company`, `postal`, `calling_code`, `flag`, `emoji_flag`, `emoji_unicode`, `carrier`, `languages`, `currency`, `time_zone`, `threat`, `count`
158+
159+
The `company` field returns an object with `name`, `domain`, `network`, and `type` properties.
160+
161+
The `carrier` field returns an object with `name`, `mcc`, and `mnc` properties.
162+
163+
The `threat` field returns an object with `is_tor`, `is_icloud_relay`, `is_proxy`, `is_datacenter`, `is_anonymous`, `is_known_attacker`, `is_known_abuser`, `is_threat`, `is_bogon`, and `blocklists` properties.

src/ipdata.test.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import keyBy from 'lodash/keyBy';
2-
import IPData from './ipdata';
2+
import IPData, { EU_BASE_URL } from './ipdata';
33

44
const TEST_IP = '1.1.1.1';
55
const DEFAULT_IP_KEY = 'DEFAULT_IP';
@@ -29,6 +29,16 @@ describe('constructor()', () => {
2929
expect(ipdata.cache.max).toEqual(max);
3030
expect(ipdata.cache.maxAge).toEqual(maxAge);
3131
});
32+
33+
it('should use the default base URL', () => {
34+
const ipdata = new IPData(process.env.IPDATA_API_KEY as string);
35+
expect(ipdata.baseUrl).toEqual('https://api.ipdata.co/');
36+
});
37+
38+
it('should accept a custom base URL', () => {
39+
const ipdata = new IPData(process.env.IPDATA_API_KEY as string, undefined, EU_BASE_URL);
40+
expect(ipdata.baseUrl).toEqual('https://eu-api.ipdata.co/');
41+
});
3242
});
3343

3444
describe('lookup()', () => {

src/ipdata.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const VALID_FIELDS = [
3737
'status',
3838
];
3939
const BASE_URL = 'https://api.ipdata.co/';
40+
export const EU_BASE_URL = 'https://eu-api.ipdata.co/';
4041

4142
function isValidIP(ip: string): boolean {
4243
return ip === DEFAULT_IP || isIP(ip);
@@ -146,20 +147,22 @@ interface IPDataParams {
146147

147148
export default class IPData {
148149
apiKey: string;
150+
baseUrl: string;
149151
cache: LRU<string, LookupResponse>;
150152

151-
constructor(apiKey: string, cacheConfig?: CacheConfig) {
153+
constructor(apiKey: string, cacheConfig?: CacheConfig, baseUrl?: string) {
152154
if (!isString(apiKey)) {
153155
throw new Error('An API key is required.');
154156
}
155157

156158
this.apiKey = apiKey;
159+
this.baseUrl = baseUrl || BASE_URL;
157160
this.cache = new LRU<string, LookupResponse>({ max: CACHE_MAX, maxAge: CACHE_MAX_AGE, ...cacheConfig });
158161
}
159162

160163
async lookup(ip?: string, selectField?: string, fields?: string[]): Promise<LookupResponse> {
161164
const params: IPDataParams = { 'api-key': this.apiKey };
162-
let url = ip ? urljoin(BASE_URL, ip) : BASE_URL;
165+
let url = ip ? urljoin(this.baseUrl, ip) : this.baseUrl;
163166

164167
if (ip && !isValidIP(ip)) {
165168
throw new Error(`${ip} is an invalid IP address.`);
@@ -227,7 +230,7 @@ export default class IPData {
227230

228231
try {
229232
if (bulk.length > 0) {
230-
const response = await axios.post(urljoin(BASE_URL, 'bulk'), bulk, { params });
233+
const response = await axios.post(urljoin(this.baseUrl, 'bulk'), bulk, { params });
231234
response.data.forEach(info => {
232235
this.cache.set(info.ip, { ...info, status: response.status });
233236
responses.push(this.cache.get(info.ip));

0 commit comments

Comments
 (0)