Skip to main content

DynamicAPIClient: Django-like dynamic ORM models generated from OpenAPI 2/3 or GraphQL schemas

Project description

DynamicAPIClient

CI codecov

Generate Django-like model classes from an OpenAPI 2 or OpenAPI 3 document, or a GraphQL schema (SDL or introspection JSON), as a URL or local file. OpenAPI schemas under definitions (v2) or components.schemas (v3) become models with .objects wired to REST paths. GraphQL object types become models whose .objects issues query / mutation operations against a single HTTP endpoint (default POST /graphql).

Install

The PyPI distribution is dynamicapiclient (pip install dynamicapiclient). The Python import package remains dynamicapiclient (import dynamicapiclient). This project is referred to as DynamicAPIClient in documentation.

pip install -e .
# or, with dev dependencies:
pip install -e ".[dev]"
# GraphQL support (graphql-core):
pip install -e ".[graphql]"

Requires Python 3.10+. GraphQL requires the optional graphql extra (graphql-core).

GitHub: badges and links in this README use the repository name dynamicapiclient. After you rename the repository on GitHub to match, update PyPI → Publishing → trusted publisher with the new repository path if you use OIDC publishing.

Tests, coverage, and pre-commit

CI-style checks use ≥90% coverage on dynamicapiclient (see pyproject.toml). Run:

pytest -q --cov=dynamicapiclient --cov-fail-under=90

GitHub Actions runs the same suite on Python 3.10–3.13 and uploads coverage to Codecov via OIDC (no CODECOV_TOKEN needed on the main repo). Add the project in Codecov once so the badge and graphs populate. Forks or private mirrors may need a CODECOV_TOKEN secret—see Codecov’s docs.

With a Git checkout, install pre-commit (pip install pre-commit or use the dev extra) and run pre-commit install so commits run the same pytest command via .pre-commit-config.yaml.

Quick start (fixture spec)

This repo includes a sample OpenAPI 3 spec at tests/fixtures/library_oas3.yaml. It describes a small “library” API with Author and Book schemas and paths under https://api.example.com/v1.

Load the spec from disk and build the API object:

from pathlib import Path

from dynamicapiclient import api_make  # or: from dynamicapiclient import apiMake

spec_path = Path("tests/fixtures/library_oas3.yaml")
# Or an absolute path on your machine.

MyAPI = api_make(spec_path)

Discover generated models (works well in a REPL):

dir(MyAPI.models)          # ['Author', 'Book']
list(MyAPI.models)         # model classes
MyAPI.models.model_names() # ('Author', 'Book')

The spec’s servers[0].url is used as the HTTP base URL unless you override it:

MyAPI = api_make(spec_path, base_url="http://localhost:8000/v1")

Creating and reading resources

The fixture marks name and email as required on Author, and title and author_id on Book. Your server must actually implement the described paths (POST /authors, GET /authors/{author_id}, POST /books, etc.); the YAML file is only the contract.

author = MyAPI.models.Author.objects.create(
    name="J.R.R. Tolkien",
    email="tolkien@example.com",
)

book = MyAPI.models.Book.objects.create(
    title="The Hobbit",
    author_id=author.pk,
)

Note: In library_oas3.yaml, Book only defines id, title, and author_id. Pass only fields that appear in the schema (extra fields raise validation errors). This fixture uses an integer author_id, not a nested author= relation object.

Other useful calls:

same = MyAPI.models.Author.objects.get(pk=author.pk)
for a in MyAPI.models.Author.objects.filter(name="J.R.R. Tolkien"):
    print(a.pk, a._data["email"])

MyAPI.models.Author.objects.update(same, email="tolkien@tolkien.estate")
MyAPI.models.Author.objects.delete(same)

Close the underlying HTTP client when you are done (optional but good practice):

MyAPI.close()
# or: with api_make(...) as MyAPI: ...

Headers (e.g. auth)

MyAPI = api_make(
    spec_path,
    headers={"Authorization": "Bearer YOUR_TOKEN"},
)

Loading from a URL

MyAPI = api_make("https://example.com/openapi.yaml")

Swagger 2 example

tests/fixtures/swagger2_library.json defines a Widget model. Load it the same way; Swagger 2 uses host + basePath + schemes for the base URL, or pass base_url=... explicitly.

GraphQL schema (SDL or introspection JSON)

Install graphql-core (pip install "dynamicapiclient[graphql]"). Point api_make at a .graphql / .gql file, a JSON introspection export (data.__schema or bare __schema), or a URL whose body looks like GraphQL SDL or introspection. You must pass base_url= to the HTTP server root; SDL does not carry a server URL.

from pathlib import Path

from dynamicapiclient import api_make

schema_path = Path("tests/fixtures/library.graphql")
GQL = api_make(
    schema_path,
    base_url="https://api.example.com",
    graphql_path="/graphql",  # default; POST JSON { "query", "variables" }
)
author = GQL.models.Author.objects.create(name="Ada", email="ada@example.com")

DynamicAPIClient infers operations using common patterns:

  • List: a Query field whose return type is a list of the object type (e.g. authors: [Author!]!), optional filter() args match declared GraphQL arguments on that field.
  • Get: a Query field returning the type with an id: ID! (or authorId-style) argument.
  • Create / update / delete: Mutation fields whose names start with create / add, update / edit, or delete / remove, with input arguments for writes and ID arguments where needed.

If your API uses different names, DynamicAPIClient may not find an operation; you will get a clear DynamicAPIClientModelError.

How it works (short)

  • Schemas become Python types on api.models.<Name>.
  • OpenAPI: CRUD routes are inferred from paths whose bodies or responses reference that schema.
  • GraphQL: CRUD maps to query / mutation documents sent to graphql_path, using the heuristics described above.
  • If the spec does not expose a clear operation for a model, calling the missing operation raises a clear DynamicAPIClientModelError.

For full behavior and edge cases, see the test suite under tests/.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dynamicapiclient-0.2.0.tar.gz (35.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dynamicapiclient-0.2.0-py3-none-any.whl (24.4 kB view details)

Uploaded Python 3

File details

Details for the file dynamicapiclient-0.2.0.tar.gz.

File metadata

  • Download URL: dynamicapiclient-0.2.0.tar.gz
  • Upload date:
  • Size: 35.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dynamicapiclient-0.2.0.tar.gz
Algorithm Hash digest
SHA256 f5de5807c4e1d36879699f85f9c4205759eeea062fbf97800bdeb03fbdeec5dc
MD5 2f32b1e780a6dc55c03a9561cd7a2f50
BLAKE2b-256 282cf7ec768480aabbea7a18d20beadae2a71926c69c13a5f174bd27ed19ea54

See more details on using hashes here.

Provenance

The following attestation bundles were made for dynamicapiclient-0.2.0.tar.gz:

Publisher: publish-pypi.yml on stuart23/dynamicapiclient

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dynamicapiclient-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for dynamicapiclient-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 71e105ff4d5f50bf14d5d1965ae7f290cd349437f261a53fbd1736c7bda5eda6
MD5 793dacfc49b959d909daf86af03a2506
BLAKE2b-256 be20de0e068a6a9e0cdb49f149801565d8fe25389cc9b8f57a546764a0b54087

See more details on using hashes here.

Provenance

The following attestation bundles were made for dynamicapiclient-0.2.0-py3-none-any.whl:

Publisher: publish-pypi.yml on stuart23/dynamicapiclient

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page