🌉 ApexRestpy
Oracle APEX RESTful API Client — Python
A Python Adaptation of the ApexBridge C++ Arduino Library
📋 Table of Contents
- Executive Summary
- Key Features
- Architecture & Flow
- Tech Stack
- Prerequisites
- Installation
- Quick Start
- Usage & API Reference
- Environment Variables
- Testing
- Contributing
- Security Policy
- License & Contact
🎯 Executive Summary
ApexRestpy is a lightweight, minimal-dependency (only requests) client library that enables Python applications to communicate swiftly with Oracle APEX RESTful services. It is a direct Python adaptation of the ApexBridge library written in C++ for microcontrollers (ESP32/ESP8266); you can use the same logical API on servers, Raspberry Pi, automation scripts, and CI/CD pipelines.
The library abstracts Oracle APEX's standard /<base_path>/<schema>/<module>/<resource> URL structure and provides Bearer token authentication, query parameter management, and all HTTP methods (GET/POST/PUT/PATCH/DELETE) through a unified, easy-to-test interface. HTTPS connections are established with zero configuration; SSL certificate management is handled entirely and automatically by requests + certifi.
Target audience: Python developers using Oracle APEX as a backend, IoT integration engineers, and embedded systems developers migrating from the Arduino ecosystem to Python.
✨ Key Features
| Feature | Description |
|---|---|
| 🔗 Seamless APEX Communication | Automatic endpoint construction with the schema/module/resource URL pattern |
| 🔒 Automatic HTTPS | Zero-configuration SSL via requests + certifi — no manual certificates |
| 🔑 Bearer Token Authentication | One-line OAuth/JWT integration with set_token() |
| 🛠️ Full HTTP Support | GET · POST · PUT · PATCH · DELETE |
| 🧩 Fluent URL Management | Chainable API: prepare_url() + add_parameter() + add_path() |
| 🐍 Pythonic Design | snake_case, @dataclass, type hints, logging module |
| ⚡ Lightweight | Single external dependency: requests>=2.28 |
| 🧪 High Test Coverage | 38 unit tests, mocked HTTP — no real network required |
| 🔄 Session Management | Connection reuse via requests.Session |
| 📦 Installable | Standard PyPI installation with pip install apex-restpy |
🏗️ Architecture & Flow
ApexRestpy acts as a thin abstraction layer between your application and the Oracle APEX server:
flowchart LR
subgraph Application["Your Application"]
direction TB
A["bridge = ApexBridge(schema)"]
B["bridge.set_token(token)"]
C["bridge.prepare_url(module, resource)"]
D["bridge.add_parameter(k, v)"]
E["bridge.send_request('GET')"]
end
subgraph ApexRestpy["apex_restpy Layer"]
direction TB
AB["ApexBridge"]
APP["ApexApp @dataclass"]
EXC["ApexConnectionError\nApexResponseError"]
AB --> APP
AB --> EXC
end
subgraph HTTP["HTTP Layer"]
REQ["requests.Session\n(HTTPS + certifi)"]
end
subgraph APEX["Oracle APEX"]
ORDS["APEX REST Services\nhttps://apex.oracle.com/pls/apex/\n{schema}/{module}/{resource}"]
end
Application --> ApexRestpy
ApexRestpy --> HTTP
HTTP -->|"HTTPS GET/POST/PUT/PATCH/DELETE"| APEX
APEX -->|"JSON Response"| HTTP
HTTP -->|"dict"| ApexRestpy
ApexRestpy -->|"dict"| Application
URL Construction Process
ApexApp.base_path + schema + module + resource [?param=val&...] [/path]
/pls/apex / myschema / sensor / data ?id=42&type=temp /details
→ https://apex.oracle.com/pls/apex/myschema/sensor/data/details?id=42&type=temp
Package Structure
apex_restpy/
├── __init__.py # Public API: ApexBridge, ApexApp, exceptions
├── apex_bridge.py # Core client class
├── apex_app.py # Configuration dataclass
└── exceptions.py # ApexConnectionError, ApexResponseError
tests/
└── test_apex_bridge.py # 38 unit tests (mocked HTTP)
examples/
├── basic_get.py # GET example
└── basic_post.py # POST example
🛠️ Tech Stack
| Layer | Technology | Version |
|---|---|---|
| Language | Python | ≥ 3.9 |
| HTTP Client | requests | ≥ 2.28.0 |
| SSL | certifi (requests dependency) | Automatic |
| Packaging | setuptools + pyproject.toml | PEP 517/518 |
| Lint | Ruff | ≥ 0.4.0 |
| Type Checking | mypy | ≥ 1.8.0 |
| Testing | pytest + pytest-cov | ≥ 7.0 / ≥ 4.0 |
| CI/CD | GitHub Actions | — |
| Release Management | Release Please | v4 |
| Dependency Updates | Dependabot | — |
✅ Prerequisites
The following tools must be installed in your development environment:
| Tool | Minimum Version | Check |
|---|---|---|
| Python | 3.9 | python --version |
| pip | 21.0 | pip --version |
| Git | 2.30 | git --version |
🚀 Installation
From PyPI (Recommended)
pip install apex-restpy
Development Setup
# 1. Clone the repository
git clone https://github.com/AtaCanYmc/ApexRestpy.git
cd ApexRestpy
# 2. Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # Linux / macOS
# .venv\Scripts\activate.bat # Windows CMD
# .venv\Scripts\Activate.ps1 # Windows PowerShell
# 3. Install the package with development dependencies
pip install -e ".[dev]"
Verify Installation
python -c "from apex_restpy import ApexBridge; print('✅ apex-restpy is ready!')"
⚡ Quick Start
from apex_restpy import ApexBridge
# 1. Initialise the bridge
bridge = ApexBridge(schema="myschema")
# 2. (Optional) Set the authentication token
bridge.set_token("your-jwt-bearer-token")
# 3. Prepare the endpoint and send a request
bridge.prepare_url("time", "now")
response = bridge.send_request() # default: GET
# 4. Use the response
print(response["full_timestamp"]) # → "2025-03-02T14:30:00"
print(response["year"]) # → 2025
📖 Usage & API Reference
ApexBridge(schema, base_path, host, timeout, debug, session)
The core client class. All APEX communication is handled through this object.
bridge = ApexBridge(
schema="myschema", # APEX workspace schema name (required)
base_path="/pls/apex", # URL prefix (default: "/pls/apex")
host="apex.oracle.com", # APEX server address (default)
timeout=10.0, # Request timeout in seconds (default: 10)
debug=False, # If True, enables DEBUG log level
)
set_token(token: str)
Enables Bearer token authentication. The Authorization: Bearer <token> header is automatically added to all subsequent requests.
bridge.set_token("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...")
prepare_url(module, resource, schema=None) → str
Constructs the APEX REST endpoint path and stores it in the internal _last_endpoint.
url = bridge.prepare_url("sensor", "data")
# → "/pls/apex/myschema/sensor/data"
# With a different schema
url = bridge.prepare_url("time", "now", schema="otherschema")
# → "/pls/apex/otherschema/time/now"
add_parameter(param, value, url=None) → str
Appends a query parameter to the active endpoint (or a given URL).
bridge.prepare_url("sensor", "data")
bridge.add_parameter("id", "42") # → ...?id=42
bridge.add_parameter("type", "temp") # → ...?id=42&type=temp
# On a custom URL (the stored endpoint is not modified)
result = bridge.add_parameter("limit", "10", url="/pls/apex/myschema/items/list")
# → "/pls/apex/myschema/items/list?limit=10"
add_path(path, url=None) → str
Appends an additional path segment to the active endpoint.
bridge.prepare_url("items", "list")
bridge.add_path("active")
# _last_endpoint → "/pls/apex/myschema/items/list/active"
send_request(method="GET", payload=None, url=None) → dict
Sends an HTTP request and returns the JSON response as a dict.
# GET
response = bridge.send_request()
# POST — with a JSON body
response = bridge.send_request(
method="POST",
payload={"sensor_id": "42", "value": 23.5},
)
# PUT, PATCH, DELETE
bridge.send_request("PUT", payload={"name": "updated"})
bridge.send_request("PATCH", payload={"status": "active"})
bridge.send_request("DELETE")
# Request to a different URL (the stored endpoint is not modified)
response = bridge.send_request("GET", url="/pls/apex/myschema/custom/path")
Supported methods: GET · POST · PUT · PATCH · DELETE
Exceptions
| Exception | When raised |
|---|---|
ApexConnectionError |
Network connection cannot be established or times out |
ApexResponseError |
Response body is not valid JSON |
ValueError |
An unsupported HTTP method is provided |
from apex_restpy import ApexBridge, ApexConnectionError, ApexResponseError
try:
response = bridge.send_request()
except ApexConnectionError as e:
print(f"Connection error: {e}")
except ApexResponseError as e:
print(f"Invalid response: {e}")
Full Scenario Example
from apex_restpy import ApexBridge
bridge = ApexBridge(schema="iot_prod", timeout=15.0, debug=True)
bridge.set_token("your-jwt-token")
# Fetching data with pagination and filtering
bridge.prepare_url("sensors", "readings")
bridge.add_parameter("device_id", "esp32-01")
bridge.add_parameter("limit", "100")
bridge.add_path("latest")
# → /pls/apex/iot_prod/sensors/readings/latest?device_id=esp32-01&limit=100
response = bridge.send_request("GET")
for reading in response.get("items", []):
print(f"{reading['ts']} → {reading['value']} {reading['unit']}")
🔐 Environment Variables
ApexRestpy is a library, not a CLI tool or service, so it does not ship its own .env file. For applications that use credentials, we recommend the following pattern:
.env.example — Reference this file in your project:
# Oracle APEX Configuration
APEX_SCHEMA=your_schema_name
APEX_BASE_PATH=/pls/apex
APEX_HOST=apex.oracle.com
APEX_TIMEOUT=10
# Authentication
APEX_BEARER_TOKEN=your_jwt_or_oauth_token
Usage:
import os
from dotenv import load_dotenv
from apex_restpy import ApexBridge
load_dotenv()
bridge = ApexBridge(
schema=os.environ["APEX_SCHEMA"],
host=os.environ.get("APEX_HOST", "apex.oracle.com"),
timeout=float(os.environ.get("APEX_TIMEOUT", "10")),
)
bridge.set_token(os.environ["APEX_BEARER_TOKEN"])
🧪 Testing
Running the Tests
# Run all tests
pytest tests/ -v
# With a coverage report
pytest tests/ -v --cov=apex_restpy --cov-report=term-missing
# HTML coverage report
pytest tests/ --cov=apex_restpy --cov-report=html
open htmlcov/index.html
Test Matrix
tests/
└── test_apex_bridge.py 38 tests
├── TestPrepareUrl 4 tests — URL construction logic
├── TestAddParameter 5 tests — Query string management
├── TestAddPath 3 tests — Path appending
├── TestSetToken 4 tests — Token and header management
├── TestSendRequestGet 5 tests — GET requests
├── TestSendRequestPost 3 tests — POST requests and payload
├── TestSendRequestOtherMethods 4 tests — PUT / PATCH / DELETE
├── TestErrorHandling 3 tests — Error handling
├── TestBuildFullUrl 3 tests — URL normalisation
└── TestProperties 4 tests — Read-only properties
Lint & Type Checking
# Lint
ruff check .
# Format check
ruff format --check .
# Auto-fix formatting
ruff format .
# Type checking
mypy apex_restpy --ignore-missing-imports
🤝 Contributing
We warmly welcome contributions! See CONTRIBUTING.md for the detailed guide.
Quick Start
# 1. Fork and clone
git clone https://github.com/<your-username>/ApexRestpy.git
cd ApexRestpy
# 2. Set up the development environment
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
# 3. Create a feature branch
git checkout -b feat/my-feature
# 4. Make your changes and run the tests
pytest tests/ -v
# 5. Lint and format check
ruff check . && ruff format --check . && mypy apex_restpy
# 6. Write your commit message in Conventional Commits format
git commit -m "feat: add retry mechanism for connection failures"
# 7. Open a PR
git push origin feat/my-feature
Commit Message Conventions
This project uses the Conventional Commits standard:
| Prefix | Usage |
|---|---|
feat: |
New feature |
fix: |
Bug fix |
docs: |
Documentation only |
test: |
Adding or fixing tests |
refactor: |
Code restructuring |
chore: |
Dependency updates, CI |
BREAKING CHANGE: |
Backwards-incompatible change |
Release Please analyses these messages to automatically generate a CHANGELOG and assign version numbers.
Code Standards
- Formatter: Ruff (
line-length = 100) - Linter: Ruff (rules
E, W, F, I, B, UP) - Type checking: mypy (
warn_return_any = true) - Test coverage: Unit tests are mandatory for new features
- Docstrings: All public methods must have Google-style docstrings
🔒 Security Policy
For security vulnerability reports, please read SECURITY.md.
In brief: Report vulnerabilities directly to atacanymc@gmail.com, not via GitHub Issues. A fix is committed within 90 days.
📜 License & Contact
This project is distributed under the Apache License. See the LICENSE file for details.
Ata Can Yaymacı
C++ original: ApexBridge — Arduino library for ESP32/ESP8266
⭐ If you find this useful, don't forget to star it on GitHub!
Metadata
Release files for apex-restpy 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| apex_restpy-0.1.0.tar.gz | 28.8 kB | With provenance | Details for apex_restpy-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| apex_restpy-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for apex_restpy-0.1.0-py3-none-any.whl |
Total release size: 49.3 kB
Release files / apex_restpy-0.1.0.tar.gz
| Download URL | apex_restpy-0.1.0.tar.gz |
|---|---|
| Size | 28.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
50b9da6978fd68ea3290efc0e71fbcc7a18c9a5c1b91ccbf86eecf7faece1d2c
|
|
BLAKE2b-256 checksum How to use checksums |
8b4117138ee6c17ba2b77899a0646123bb1450e87eb1473dd326ebfb0d85044c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 19, 2026.
Transparency logRelease files / apex_restpy-0.1.0-py3-none-any.whl
| Download URL | apex_restpy-0.1.0-py3-none-any.whl |
|---|---|
| Size | 20.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ced5eee25b68339ffdd48ea004c806ddd175ddbdde385a77bbc47a4b13f11aa7
|
|
BLAKE2b-256 checksum How to use checksums |
19fd39877b40f65a50984b3c0101879fcd9959b3cc0e0ac9e08325dccb46b364
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 19, 2026.
Transparency logSecurity for apex-restpy 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for apex_restpy-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 19, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.