rest-api-patterns
skill✓Guide for implementing REST API wrapper functions. Use this when adding new API wrappers or troubleshooting API calls.
apm::install
apm install @microsoft/rest-api-patternsapm::skill.md
---
name: rest-api-patterns
description: Guide for implementing REST API wrapper functions. Use this when adding new API wrappers or troubleshooting API calls.
---
# REST API Patterns
This skill covers the patterns and utilities for implementing REST API wrapper functions in Semantic Link Labs.
## When to Use This Skill
Use this skill when you need to:
- Implement new REST API wrappers
- Understand the `_base_api` helper function
- Handle pagination, long-running operations, or errors
- Debug API-related issues
---
## Finding API Documentation
Before implementing a wrapper, use the API search tool to find the relevant documentation:
### Using `search_public_api_doc.py`
```bash
# Navigate to the scripts directory
cd .claude/skills/rest-api-patterns/scripts
# Search both Fabric and Power BI APIs
python search_public_api_doc.py "dataset refresh"
# Search Fabric APIs only
python search_public_api_doc.py "create item" --source fabric
# Search Power BI APIs only
python search_public_api_doc.py "gateway" --source powerbi
# Limit results
python search_public_api_doc.py "workspace" --limit 10
```
### Example Output
```
🔍 Searching for: 'dataset refresh' in Fabric + Power BI
================================================================================
📥 Fetching Microsoft Fabric TOC...
✅ Loaded 15 top-level categories from Microsoft Fabric
📥 Fetching Power BI TOC...
✅ Loaded 20 top-level categories from Power BI
Found 5 results:
1. [POWERBI] Datasets - Refresh Dataset In Group (score: 95.0)
URL: https://learn.microsoft.com/en-us/rest/api/power-bi/datasets/refresh-dataset-in-group
Path: Datasets > Refresh Dataset In Group
2. [POWERBI] Datasets - Get Refresh History In Group (score: 90.0)
URL: https://learn.microsoft.com/en-us/rest/api/power-bi/datasets/get-refresh-history-in-group
Path: Datasets > Get Refresh History In Group
```
### Requirements
The script requires `rapidfuzz` and `requests`:
```bash
pip install rapidfuzz requests
```
### API Documentation References
| API | Base URL | Documentation |
|-----|----------|---------------|
| Fabric REST API | `https://api.fabric.microsoft.com/v1/` | [Fabric REST API](https://learn.microsoft.com/rest/api/fabric/) |
| Power BI REST API | `https://api.powerbi.com/v1.0/myorg/` | [Power BI REST API](https://learn.microsoft.com/rest/api/power-bi/) |
---
## API Client Architecture
Semantic Link Labs uses `sempy.fabric.FabricRestClient` as the underlying HTTP client, wrapped by the `_base_api` helper function.
### Key Components
| Component | Purpose |
|-----------|---------|
| `_base_api` | Main helper for all API calls |
| `FabricRestClient` | HTTP client from sempy |
| `pagination` | Handles paginated responses |
| `lro` | Handles long-running operations |
---
## The `_base_api` Function
Located in `src/sempy_labs/_helper_functions.py`, this is the standard way to make API calls.
### Function Signature
```python
def _base_api(
request: str, # API endpoint path
client: str = "fabric", # Client type
method: str = "get", # HTTP method
payload: Optional[str] = None, # Request body
status_codes: Optional[int] = 200, # Expected status codes
uses_pagination: bool = False, # Enable pagination
lro_return_json: bool = False, # Wait for LRO, return JSON
lro_return_status_code: bool = False, # Wait for LRO, return status
lro_return_df: bool = False, # Wait for LRO, return DataFrame
):
```
### Client Types
| Client | Use Case | Authentication |
|--------|----------|----------------|
| `fabric` | Standard Fabric API | Default notebook credentials |
| `fabric_sp` | Fabric API with SP support | Service Principal or default |
| `azure` | Azure Resource Manager | Service Principal |
| `graph` | Microsoft Graph | Service Principal |
| `onelake` | OneLake storage | Storage token |
### Return Types
**The `_base_api` function returns different types depending on the parameters used:**
| Parameters | Return Type | How to Access Data |
|------------|-------------|-------------------|
| Default (no special flags) | `Response` object | Call `.json()` to get dict |
| `uses_pagination=True` | `list[dict]` | Iterate over list, each item has `.get("value", [])` |
| `lro_return_json=True` | `dict` | Access directly, already parsed JSON |
| `lro_return_status_code=True` | `int` | HTTP status code |
| `lro_return_df=True` | `DataFrame` | Use directly |
**⚠️ COMMON MISTAKE:** Forgetting to call `.json()` on the response for simple GET requests.
```python
# ❌ WRONG - response is a Response object, not a dict
response = _base_api(request=f"/v1/workspaces/{workspace_id}/items/{item_id}")
name = response.get("displayName") # AttributeError: 'Response' object has no attribute 'get'
# ✅ CORRECT - call .json() to get the dict
response = _base_api(request=f"/v1/workspaces/{workspace_id}/items/{item_id}").json()
name = response.get("displayName") # Works!
```
---
## Common API Patterns
### Simple GET Request
```python
from sempy_labs._helper_functions import _base_api
response = _base_api(
request=f"/v1/workspaces/{workspace_id}/items",
client="fabric_sp",
)
data = response.json()
```
### POST Request with Payload
```python
payload = {
"displayName": name,
"description": description,
}
response = _base_api(
request=f"/v1/workspaces/{workspace_id}/items",
method="post",
payload=payload,
status_codes=[201, 202],
client="fabric_sp",
)
```
### DELETE Request
```python
_base_api(
request=f"/v1/workspaces/{workspace_id}/items/{item_id}",
method="delete",
client="fabric_sp",
)
```
### PATCH Request
```python
payload = {
"displayName": new_name,
}
_base_api(
request=f"/v1/workspaces/{workspace_id}/items/{item_id}",
method="patch",
payload=payload,
client="fabric_sp",
)
```
---
## Handling Pagination
For APIs that return paginated results:
```python
from sempy_labs._helper_functions import _base_api, _create_dataframe
columns = {
"Id": "string",
"Name": "string",
}
df = _create_dataframe(columns=columns)
# Get all pages
responses = _base_api(
request=f"/v1/workspaces/{workspace_id}/items",
uses_pagination=True,
client="fabric_sp",
)
# Process all responses
rows = []
for r in responses:
for item in r.get("value", []):
rows.append({
"Id": item.get("id"),
"Name": item.get("displayName"),
})
if rows:
df = pd.DataFrame(rows)
return df
```
---
## Handling Long-Running Operations (LRO)
Some APIs return 202 Accepted and require polling for completion.
### Return JSON When Complete
```python
result = _base_api(
request=f"/v1/workspaces/{workspace_id}/items/{item_id}/getDefinition",
method="post",
lro_return_json=True,
client="fabric_sp",
)
# Result contains the final JSON response
definition = result.get("definition")
```
### Return Status Code
```python
status = _base_api(
request=f"/v1/workspaces/{workspace_id}/items",
method="post",
payload=payload,
lro_return_status_code=True,
client="fabric_sp",
)
# status is the final HTTP status code
if status == 200:
print("Operation completed successfully")
```
---
## Error Handling
### Expected Status Codes
Specify expected status codes to avoid exceptions:
```python
# Accept 200, 201, or 202 as success
response = _base_api(
request=url,
method="post",
payload=payload,
status_codes=[200, 201, 202],
client="fabric_sp",
)
```
### FabricHTTPException
When status code doesn't match, `FabricHTTPException` is raised:
```python
from sempy.fabric.exceptions import FabricHTTPException
try:
response = _base_api(
request=f"/v1/workspaces/{workspace_id}/items/{item_id}",
client="fabric_sp",
)
except FabricHTTPException as e:
if e.response.status_code == 404:
print(f"Item not found")
else:
raise
```
---
## Building URLs with Parameters
Use the `_build_url` helper for query parameters:
```python
from sempy_labs._helper_functions import _build_url
url = "/v1/admin/workspaces"
params = {
"capacityId": capacity_id,
"state": "Active",
}
url = _build_url(url, params)
# Result: "/v1/admin/workspaces?capacityId=xxx&state=Active"
responses = _base_api(
request=url,
uses_pagination=True,
client="fabric_sp",
)
```
---
## API Endpoint Patterns
### Fabric Core API
```python
# List items in workspace
f"/v1/workspaces/{workspace_id}/items"
# Get specific item
f"/v1/workspaces/{workspace_id}/items/{item_id}"
# Item operations
f"/v1/workspaces/{workspace_id}/items/{item_id}/getDefinition"
f"/v1/workspaces/{workspace_id}/items/{item_id}/updateDefinition"
```
### Fabric Admin API
```python
# Admin workspaces
"/v1/admin/workspaces"
# Admin items
"/v1/admin/items"
# Capacities
"/v1/admin/capacities"
```
### Power BI REST API
```python
# Groups (workspaces)
f"/v1.0/myorg/groups/{workspace_id}/..."
# Datasets
f"/v1.0/myorg/groups/{workspace_id}/datasets/{dataset_id}/..."
# Reports
f"/v1.0/myorg/groups/{workspace_id}/reports/{report_id}/..."
```
### Azure Resource Manager
```python
# Fabric capacities
f"https://management.azure.com/subscriptions/{subscription_id}/providers/Microsoft.Fabric/capacities"
# Resource groups
f"https://management.azure.com/subscriptions/{subscription_id}/resourceGroups/{resource_group}"
```
---
## Authentication Headers
For non-Fabric clients (Azure, Graph), use `_get_headers`:
```python
from sempy_labs._authentication import _get_headers
import sempy_labs._authentication as auth
headers = _get_headers(auth.token_provider.get(), audience="azure")
response = requests.get(
url,
headers=headers,
)
```
---
## Creating Result DataFrames
Use `_create_dataframe` for consistent empty DataFrames:
```python
from sempy_labs._helper_functions import _create_dataframe
columns = {
"Id": "string",
"Name": "string",
"Type": "string",
"Created Date": "datetime",
"Size": "int",
}
df = _create_dataframe(columns=columns)
```
### Updating DataFrame Types
```python
from sempy_labs._helper_functions import _update_dataframe_datatypes
column_map = {
"Created Date": "datetime",
"Size": "int",
"Is Active": "bool",
}
_update_dataframe_datatypes(df, column_map)
```
---
## Complete Example
```python
from sempy._utils._log import log
from sempy_labs._helper_functions import (
resolve_workspace_name_and_id,
_base_api,
_create_dataframe,
_build_url,
)
import sempy_labs._icons as icons
from typing import Optional
from uuid import UUID
import pandas as pd
@log
def list_my_items(
item_type: Optional[str] = None,
workspace: Optional[str | UUID] = None,
) -> pd.DataFrame:
"""
Lists items in a workspace.
This is a wrapper function for the following API: `Items - List Items <https://learn.microsoft.com/rest/api/fabric/core/items/list-items>`_.
Service Principal Authentication is supported.
Parameters
----------
item_type : str, default=None
Filter by item type.
workspace : str | uuid.UUID, default=None
The Fabric workspace name or ID.
Defaults to None which resolves to the workspace of the attached lakehouse.
Returns
-------
pandas.DataFrame
A pandas dataframe showing items in the workspace.
"""
(workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
columns = {
"Id": "string",
"Name": "string",
"Type": "string",
}
df = _create_dataframe(columns=columns)
url = f"/v1/workspaces/{workspace_id}/items"
params = {}
if item_type:
params["type"] = item_type
if params:
url = _build_url(url, params)
responses = _base_api(
request=url,
uses_pagination=True,
client="fabric_sp",
)
rows = []
for r in responses:
for item in r.get("value", []):
rows.append({
"Id": item.get("id"),
"Name": item.get("displayName"),
"Type": item.get("type"),
})
if rows:
df = pd.DataFrame(rows)
return df
```
---
## Debugging API Calls
### Print Response Details
```python
response = _base_api(
request=url,
client="fabric_sp",
)
print(f"Status: {response.status_code}")
print(f"Headers: {response.headers}")
print(f"Body: {response.json()}")
```
### Check Request Being Made
Add temporary debug prints:
```python
print(f"Making request to: {url}")
print(f"Payload: {payload}")
```
---
## API Documentation References
| API | Documentation |
|-----|---------------|
| Fabric Core | [https://learn.microsoft.com/rest/api/fabric/core/](https://learn.microsoft.com/rest/api/fabric/core/) |
| Fabric Admin | [https://learn.microsoft.com/rest/api/fabric/admin/](https://learn.microsoft.com/rest/api/fabric/admin/) |
| Power BI | [https://learn.microsoft.com/rest/api/power-bi/](https://learn.microsoft.com/rest/api/power-bi/) |
| Azure Fabric | [https://learn.microsoft.com/rest/api/microsoftfabric/](https://learn.microsoft.com/rest/api/microsoftfabric/) |
| Graph | [https://learn.microsoft.com/graph/api/overview](https://learn.microsoft.com/graph/api/overview) |