|
| 1 | +# Region Endpoint Integration Specification |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +Contentstack services are deployed across multiple cloud providers and geographic regions. SDKs must resolve service endpoints dynamically using the Contentstack Regions Registry rather than relying on hardcoded URLs. |
| 6 | + |
| 7 | +This ensures: |
| 8 | + |
| 9 | +- Consistent endpoint resolution across all SDKs |
| 10 | +- Automatic support for newly introduced regions |
| 11 | +- Automatic support for newly introduced services |
| 12 | +- Single source of truth for endpoint configuration |
| 13 | +- Elimination of region-specific host logic inside SDKs |
| 14 | + |
| 15 | +--- |
| 16 | + |
| 17 | +## Regions Registry |
| 18 | + |
| 19 | +All endpoint information is maintained in the Contentstack Regions Registry. |
| 20 | + |
| 21 | +### Registry URL |
| 22 | + |
| 23 | +```text |
| 24 | +https://artifacts.contentstack.com/regions.json |
| 25 | +``` |
| 26 | + |
| 27 | +The registry contains: |
| 28 | + |
| 29 | +- Region identifiers |
| 30 | +- Region aliases |
| 31 | +- Default region information |
| 32 | +- Service endpoint mappings |
| 33 | + |
| 34 | +### Example |
| 35 | + |
| 36 | +```json |
| 37 | +{ |
| 38 | + "regions": [ |
| 39 | + { |
| 40 | + "id": "na", |
| 41 | + "alias": ["us", "aws-na"], |
| 42 | + "isDefault": true, |
| 43 | + "endpoints": { |
| 44 | + "contentDelivery": "https://cdn.contentstack.io", |
| 45 | + "contentManagement": "https://api.contentstack.io" |
| 46 | + } |
| 47 | + } |
| 48 | + ] |
| 49 | +} |
| 50 | +``` |
| 51 | + |
| 52 | +--- |
| 53 | + |
| 54 | +## Endpoint Resolution Contract |
| 55 | + |
| 56 | +All SDKs must expose a public endpoint resolution API. |
| 57 | + |
| 58 | +```text |
| 59 | +getContentstackEndpoint( |
| 60 | + region, |
| 61 | + service, |
| 62 | + omitProtocol = false |
| 63 | +) |
| 64 | +``` |
| 65 | + |
| 66 | +### Parameters |
| 67 | + |
| 68 | +| Parameter | Description | |
| 69 | +|-----------|-------------| |
| 70 | +| `region` | Region identifier or alias | |
| 71 | +| `service` | Service name | |
| 72 | +| `omitProtocol` | Removes protocol prefix from returned URL | |
| 73 | + |
| 74 | +### Returns |
| 75 | + |
| 76 | +- Service URL when a service is specified |
| 77 | +- Complete endpoint map when service is omitted |
| 78 | + |
| 79 | +--- |
| 80 | + |
| 81 | +## Region Resolution Rules |
| 82 | + |
| 83 | +Region matching must: |
| 84 | + |
| 85 | +- Ignore case |
| 86 | +- Trim whitespace |
| 87 | +- Support aliases |
| 88 | +- Support both dash (`-`) and underscore (`_`) variants where defined |
| 89 | + |
| 90 | +### Examples |
| 91 | + |
| 92 | +| Input | Resolved Region | |
| 93 | +|--------|----------------| |
| 94 | +| `na` | `na` | |
| 95 | +| `us` | `na` | |
| 96 | +| `aws-na` | `na` | |
| 97 | +| `AWS_NA` | `na` | |
| 98 | +| `eu` | `eu` | |
| 99 | +| `azure-na` | `azure-na` | |
| 100 | +| `gcp-eu` | `gcp-eu` | |
| 101 | + |
| 102 | +If no region is found: |
| 103 | + |
| 104 | +```text |
| 105 | +Invalid region |
| 106 | +``` |
| 107 | + |
| 108 | +--- |
| 109 | + |
| 110 | +## Service Resolution Rules |
| 111 | + |
| 112 | +SDKs must: |
| 113 | + |
| 114 | +1. Locate the resolved region. |
| 115 | +2. Locate the service name within the region endpoints. |
| 116 | +3. Return the endpoint URL. |
| 117 | + |
| 118 | +### Example |
| 119 | + |
| 120 | +```text |
| 121 | +Region: eu |
| 122 | +Service: contentDelivery |
| 123 | +
|
| 124 | +Result: |
| 125 | +https://eu-cdn.contentstack.com |
| 126 | +``` |
| 127 | + |
| 128 | +If the service is unavailable: |
| 129 | + |
| 130 | +```text |
| 131 | +Service not found |
| 132 | +``` |
| 133 | + |
| 134 | +--- |
| 135 | + |
| 136 | +## Supported Service Names |
| 137 | + |
| 138 | +- `contentDelivery` |
| 139 | +- `contentManagement` |
| 140 | +- `graphqlDelivery` |
| 141 | +- `graphqlPreview` |
| 142 | +- `preview` |
| 143 | +- `auth` |
| 144 | +- `application` |
| 145 | +- `images` |
| 146 | +- `assets` |
| 147 | +- `automate` |
| 148 | +- `launch` |
| 149 | +- `developerHub` |
| 150 | +- `brandKit` |
| 151 | +- `genAI` |
| 152 | +- `personalizeManagement` |
| 153 | +- `personalizeEdge` |
| 154 | +- `composableStudio` |
| 155 | +- `assetManagement` |
| 156 | + |
| 157 | +SDKs must not hardcode this list. The registry remains the source of truth. |
| 158 | + |
| 159 | +--- |
| 160 | + |
| 161 | +## Registry Loading Requirements |
| 162 | + |
| 163 | +Recommended priority: |
| 164 | + |
| 165 | +1. In-memory cache |
| 166 | +2. Local registry file |
| 167 | +3. Registry download fallback |
| 168 | + |
| 169 | +Examples: |
| 170 | + |
| 171 | +- JavaScript SDK: Build-time download |
| 172 | +- PHP SDK: Install-time download with runtime fallback |
| 173 | +- Java SDK: Build-time download via Maven (`generate-resources` phase) with runtime fallback |
| 174 | + |
| 175 | +--- |
| 176 | + |
| 177 | +## SDK Integration Requirements |
| 178 | + |
| 179 | +```text |
| 180 | +Resolve Region |
| 181 | + ↓ |
| 182 | +Resolve contentDelivery Endpoint |
| 183 | + ↓ |
| 184 | +Configure SDK Host |
| 185 | + ↓ |
| 186 | +Execute API Requests |
| 187 | +``` |
| 188 | + |
| 189 | +The SDK host must be configured using the resolved endpoint rather than a hardcoded hostname. |
| 190 | + |
| 191 | +--- |
| 192 | + |
| 193 | +## Error Handling |
| 194 | + |
| 195 | +| Scenario | Error | |
| 196 | +|-----------|--------| |
| 197 | +| Empty Region | Empty region provided | |
| 198 | +| Invalid Region | Invalid region | |
| 199 | +| Invalid Service | Service not found | |
| 200 | +| Registry Unavailable | Unable to load regions registry | |
| 201 | + |
| 202 | +--- |
| 203 | + |
| 204 | +## Caching Requirements |
| 205 | + |
| 206 | +Goals: |
| 207 | + |
| 208 | +- Avoid repeated disk reads |
| 209 | +- Avoid repeated network requests |
| 210 | +- Improve endpoint lookup performance |
| 211 | + |
| 212 | +Cache implementation is SDK-specific. |
| 213 | + |
| 214 | +--- |
| 215 | + |
| 216 | +## Future Compatibility |
| 217 | + |
| 218 | +SDK implementations must not: |
| 219 | + |
| 220 | +- Hardcode endpoint URLs |
| 221 | +- Hardcode region mappings |
| 222 | +- Hardcode service name mappings |
| 223 | + |
| 224 | +All endpoint information must originate from the Regions Registry. |
| 225 | + |
| 226 | +--- |
| 227 | + |
| 228 | +## SDK Examples |
| 229 | + |
| 230 | +### Java |
| 231 | + |
| 232 | +```java |
| 233 | +import com.contentstack.utils.Endpoint; |
| 234 | + |
| 235 | +// Get a specific service URL |
| 236 | +String cdaUrl = Endpoint.getContentstackEndpoint("eu", "contentDelivery"); |
| 237 | +// → "https://eu-cdn.contentstack.com" |
| 238 | + |
| 239 | +// Get the host without the https:// scheme |
| 240 | +String host = Endpoint.getContentstackEndpoint("eu", "contentDelivery", true); |
| 241 | +// → "eu-cdn.contentstack.com" |
| 242 | + |
| 243 | +// Get all endpoints for a region |
| 244 | +Map<String, String> all = Endpoint.getContentstackEndpoint("eu"); |
| 245 | +// → { "contentDelivery": "https://eu-cdn.contentstack.com", ... } |
| 246 | + |
| 247 | +// Get all endpoints without the scheme |
| 248 | +Map<String, String> hosts = Endpoint.getContentstackEndpoint("eu", true); |
| 249 | +``` |
| 250 | + |
| 251 | +#### Region aliases |
| 252 | + |
| 253 | +```java |
| 254 | +// All of the following resolve to the same NA region |
| 255 | +Endpoint.getContentstackEndpoint("na", "contentDelivery"); // → https://cdn.contentstack.io |
| 256 | +Endpoint.getContentstackEndpoint("us", "contentDelivery"); // → https://cdn.contentstack.io |
| 257 | +Endpoint.getContentstackEndpoint("aws-na", "contentDelivery"); // → https://cdn.contentstack.io |
| 258 | +Endpoint.getContentstackEndpoint("AWS_NA", "contentDelivery"); // → https://cdn.contentstack.io |
| 259 | +``` |
| 260 | + |
| 261 | +#### Available via `Utils` (proxy) |
| 262 | + |
| 263 | +```java |
| 264 | +import com.contentstack.utils.Utils; |
| 265 | + |
| 266 | +// Identical result to Endpoint.getContentstackEndpoint() |
| 267 | +String url = Utils.getContentstackEndpoint("eu", "contentDelivery"); |
| 268 | +String host = Utils.getContentstackEndpoint("eu", "contentDelivery", true); |
| 269 | +Map<String, String> all = Utils.getContentstackEndpoint("eu"); |
| 270 | +``` |
| 271 | + |
| 272 | +#### Error handling |
| 273 | + |
| 274 | +```java |
| 275 | +try { |
| 276 | + Endpoint.getContentstackEndpoint("", "contentDelivery"); |
| 277 | +} catch (IllegalArgumentException e) { |
| 278 | + // "Empty region provided. Please provide a valid region." |
| 279 | +} |
| 280 | + |
| 281 | +try { |
| 282 | + Endpoint.getContentstackEndpoint("invalid", "contentDelivery"); |
| 283 | +} catch (IllegalArgumentException e) { |
| 284 | + // "Invalid region: invalid" |
| 285 | +} |
| 286 | + |
| 287 | +try { |
| 288 | + Endpoint.getContentstackEndpoint("na", "unknownService"); |
| 289 | +} catch (IllegalArgumentException e) { |
| 290 | + // "Service \"unknownService\" not found for region \"na\"" |
| 291 | +} |
| 292 | +``` |
| 293 | + |
| 294 | +#### Integration with Delivery SDK |
| 295 | + |
| 296 | +```java |
| 297 | +import com.contentstack.sdk.Config; |
| 298 | +import com.contentstack.sdk.Contentstack; |
| 299 | +import com.contentstack.sdk.Query; |
| 300 | +import com.contentstack.sdk.QueryResult; |
| 301 | +import com.contentstack.sdk.QueryResultsCallBack; |
| 302 | +import com.contentstack.sdk.ResponseType; |
| 303 | +import com.contentstack.sdk.Stack; |
| 304 | +import com.contentstack.utils.Endpoint; |
| 305 | + |
| 306 | +// 1. Resolve the host for the chosen region (omit https:// for setHost) |
| 307 | +String host = Endpoint.getContentstackEndpoint("eu", "contentDelivery", true); |
| 308 | +// → "eu-cdn.contentstack.com" |
| 309 | + |
| 310 | +// 2. Wire it into a Config and create the Stack |
| 311 | +Config config = new Config(); |
| 312 | +config.setHost(host); |
| 313 | + |
| 314 | +Stack stack = Contentstack.stack("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>", config); |
| 315 | + |
| 316 | +// 3. Fetch entries — all requests now go to the EU CDN |
| 317 | +Query query = stack.contentType("blog").query(); |
| 318 | +query.find(new QueryResultsCallBack() { |
| 319 | + @Override |
| 320 | + public void onCompletion(ResponseType responseType, QueryResult queryResult, Error error) { |
| 321 | + if (error != null) { |
| 322 | + System.err.println(error.getErrorMessage()); |
| 323 | + return; |
| 324 | + } |
| 325 | + queryResult.getResultObjects().forEach(entry -> |
| 326 | + System.out.println(entry.getTitle())); |
| 327 | + } |
| 328 | +}); |
| 329 | +``` |
| 330 | + |
| 331 | +Change one string to switch regions — everything else stays the same: |
| 332 | + |
| 333 | +```java |
| 334 | +// NA |
| 335 | +String host = Endpoint.getContentstackEndpoint("na", "contentDelivery", true); |
| 336 | +// → "cdn.contentstack.io" |
| 337 | + |
| 338 | +// EU |
| 339 | +String host = Endpoint.getContentstackEndpoint("eu", "contentDelivery", true); |
| 340 | +// → "eu-cdn.contentstack.com" |
| 341 | + |
| 342 | +// Azure NA |
| 343 | +String host = Endpoint.getContentstackEndpoint("azure-na", "contentDelivery", true); |
| 344 | +// → "azure-na-cdn.contentstack.com" |
| 345 | + |
| 346 | +// GCP EU |
| 347 | +String host = Endpoint.getContentstackEndpoint("gcp-eu", "contentDelivery", true); |
| 348 | +// → "gcp-eu-cdn.contentstack.com" |
| 349 | +``` |
| 350 | + |
| 351 | +Read region from environment variable (recommended for production): |
| 352 | + |
| 353 | +```java |
| 354 | +String region = System.getenv().getOrDefault("CONTENTSTACK_REGION", "na"); |
| 355 | + |
| 356 | +Config config = new Config(); |
| 357 | +config.setHost(Endpoint.getContentstackEndpoint(region, "contentDelivery", true)); |
| 358 | + |
| 359 | +Stack stack = Contentstack.stack( |
| 360 | + System.getenv("CONTENTSTACK_API_KEY"), |
| 361 | + System.getenv("CONTENTSTACK_DELIVERY_TOKEN"), |
| 362 | + System.getenv("CONTENTSTACK_ENVIRONMENT"), |
| 363 | + config |
| 364 | +); |
| 365 | +``` |
| 366 | + |
| 367 | +#### Refreshing `regions.json` |
| 368 | + |
| 369 | +```bash |
| 370 | +# Runs automatically on every Maven build (generate-resources phase) |
| 371 | +mvn generate-resources |
| 372 | + |
| 373 | +# Or refresh manually |
| 374 | +bash scripts/download-regions.sh |
| 375 | +``` |
0 commit comments