Skip to main content

ContentGrid Extension Helpers

Reusable helpers for building ContentGrid extensions in Python with FastAPI.

Features

  • Authentication: OIDC/OAuth token verification and a ContentGridUserDependency for FastAPI routes.
  • ContentGrid clients: factories for clients authenticated via the extension flow (on behalf of the user) or a service account.
  • Database: SQLModel session factories for PostgreSQL and SQLite, plus a base repository.
  • Responses: HAL / HAL-Forms response models and RFC 7807 problem responses.
  • Middleware & logging: exception middleware and JSON logging (setup_json_logging).

Installation

pip install contentgrid-extension-helpers

Configuration

Settings are read from environment variables (case-insensitive). .env and .env.secret files in the working directory are also loaded. Values passed directly to a constructor take precedence over the environment.

Authentication (ContentGridUserDependency)

Variable Default Description
EXTENSION_NAME required Extension name as registered in ContentGrid (without the contentgrid:extension: prefix). Used to build the token audience.
OAUTH_ISSUER required Issuer of the extension tokens. Its .well-known/oauth-authorization-server config is used to fetch the JWKS.
EXTENSION_AUTH_URL required Token endpoint of the extensions realm.
ALGORITHM RS256 Accepted JWT signing algorithm when none is passed explicitly.
OIDC_ISSUER / AUDIENCE – Fallbacks used by decode_and_verify_token when no issuer/audience is passed.

Extension flow client (ContentGridExtensionFlowClientFactory)

Variable Default Description
EXTENSION_CLIENT_NAME required Client ID of the extension.
EXTENSION_CLIENT_SECRET required Client secret of the extension.
EXTENSION_AUTH_URL https://auth.sandbox.contentgrid.cloud/realms/extensions/protocol/openid-connect/token Token endpoint of the extensions realm.
SYSTEM_EXCHANGE_URI https://extensions.sandbox.contentgrid.cloud/authentication/system/token System token exchange endpoint.
DELEGATED_EXCHANGE_URI https://extensions.sandbox.contentgrid.cloud/authentication/delegated/token Delegated (on-behalf-of user) token exchange endpoint.

Service account client (ContentGridServiceAccountFactory)

Variable Default Description
CG_AUTH_URL required Auth realm URL of the ContentGrid application.
CG_CLIENT_NAME required Service account client ID.
CG_CLIENT_SECRET required Service account client secret.
CG_APP_URL – Default application endpoint, used when no origin is given.
CG_ALLOWED_DOMAINS – JSON list of domains the client may connect to, e.g. ["app.contentgrid.cloud"].

Database

Variable Default Description
DEBUG false Echo SQL statements.
PG_HOST, PG_PORT, PG_USER, PG_PASSWD, PG_DBNAME required for Postgres PostgreSQL connection settings.
DB_POOL_SIZE 10 Connection pool size.
DB_MAX_OVERFLOW 20 Extra connections allowed beyond the pool size.
DB_POOL_RECYCLE 3600 Seconds after which connections are recycled.
DB_POOL_PRE_PING true Test connections before use.
SQLITE_FILE_NAME database.db SQLite database file.

Server (extension_config)

Variable Default Description
SERVER_URL – Public base URL of the extension.
SERVER_HOST "" Host to bind to.
SERVER_PORT – Port to bind to.
WEB_CONCURRENCY – Number of worker processes.
CORS_ORIGINS [] JSON list of allowed CORS origins (applied when PRODUCTION is true).
PRODUCTION false Run in production mode.
CI false Indicates a CI environment.

Examples

Authentication

from fastapi import FastAPI, Depends
from contentgrid_extension_helpers.dependencies.authentication.user import ContentGridUserDependency
from contentgrid_extension_helpers.authentication.user import ContentGridUser

app = FastAPI()
current_user = ContentGridUserDependency()  # reads EXTENSION_NAME, OAUTH_ISSUER, ...

@app.get("/me")
def me(user: ContentGridUser = Depends(current_user)):
    return user

HAL responses

Subclass FastAPIHALResponse for your resources and reference other endpoints by function name with HALLinkFor / HALTemplateFor. They are resolved to URLs from the app's routes during serialization. HAL-Forms templates get their properties from the endpoint's request body model.

import uuid
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from contentgrid_extension_helpers.responses.hal import (
    FastAPIHALResponse, FastAPIHALCollection, HALLinkFor, HALTemplateFor,
)

app = FastAPI()
FastAPIHALResponse.init_app(app)                            # required: resolve links from this app's routes
FastAPIHALResponse.add_server_url("http://localhost:8000")  # optional: make links absolute

class BookCreate(BaseModel):
    title: str

class Book(FastAPIHALResponse):
    id: uuid.UUID = Field(default_factory=uuid.uuid4)
    title: str

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # path_params as a callable receives the instance at serialization time
        self.links = {
            "self": HALLinkFor(endpoint_function_name="get_book", path_params=lambda book: {"book_id": str(book.id)}),
            "collection": HALLinkFor(endpoint_function_name="list_books"),
        }

books: list[Book] = [Book(title="Dune")]

@app.get("/books", response_model=FastAPIHALCollection, response_model_exclude_none=True)
def list_books():
    return FastAPIHALCollection[Book](
        _links={"self": HALLinkFor(endpoint_function_name="list_books")},
        _embedded={"books": books},
        _templates={"create": HALTemplateFor(endpoint_function_name="create_book")},
    )

@app.get("/books/{book_id}", response_model=Book, response_model_exclude_none=True)
def get_book(book_id: uuid.UUID):
    for book in books:
        if book.id == book_id:
            return book
    raise HTTPException(status_code=404, detail="Book not found")

@app.post("/books", response_model=Book, response_model_exclude_none=True)
def create_book(book: BookCreate):
    new_book = Book(**book.model_dump())
    books.append(new_book)
    return new_book

GET /books returns:

{
  "_links": { "self": { "href": "http://localhost:8000/books", "templated": false } },
  "_templates": {
    "create": {
      "method": "POST",
      "contentType": "application/json",
      "properties": [{ "name": "title", "type": "text", "prompt": "title", "required": true }],
      "target": "http://localhost:8000/books"
    }
  },
  "_embedded": {
    "books": [{
      "_links": {
        "self": { "href": "http://localhost:8000/books/3f2b8c1e-5d4a-4b7e-9c6f-1a2b3c4d5e6f", "templated": false },
        "collection": { "href": "http://localhost:8000/books", "templated": false }
      },
      "id": "3f2b8c1e-5d4a-4b7e-9c6f-1a2b3c4d5e6f",
      "title": "Dune"
    }]
  }
}

path_params and params take either a dict of strings or a callable that receives the resource, as in Book above. Links can also be passed per response with _links=..., as done for the collection. condition (a bool or a callable) leaves a link or template out when it is false. When routers are included more than once, pass tags to choose the right route.

Development

# from the repository root
pip install -r requirements.txt -r requirements-dev.txt
cd contentgrid_extension_helpers && pytest

Metadata

Release files for contentgrid-extension-helpers 0.0.8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for contentgrid-extension-helpers 0.0.8
File Size Uploaded
contentgrid_extension_helpers-0.0.8.tar.gz 63.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for contentgrid-extension-helpers 0.0.8
File Interpreter ABI Platform
contentgrid_extension_helpers-0.0.8-py3-none-any.whl Python 3 none any Details

Total release size: 101.5 kB

Release files / contentgrid_extension_helpers-0.0.8.tar.gz

Download URL contentgrid_extension_helpers-0.0.8.tar.gz
Size 63.1 kB
Tags Source
SHA-256 checksum
How to use checksums
870fcf4126b2e2e2a4fe890751041d4b5b8a7676e5980e3760c7bf1c3f71a1b6
BLAKE2b-256 checksum
How to use checksums
b1ae3d5c0787221b036d0a87df906f31b422a80031050ad66c6364d3f8de516d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.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 Oct 7, 2026.

Transparency log

Release files / contentgrid_extension_helpers-0.0.8-py3-none-any.whl

Download URL contentgrid_extension_helpers-0.0.8-py3-none-any.whl
Size 38.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2c27d1e9ebcf667c1f94e2162f4f76bd93b4b2e3a66c75c3e3c41b8679f16c46
BLAKE2b-256 checksum
How to use checksums
31836c4f93689b5bf37e19dd0552a1f3ebb3400caab73a3c7d9d835804c616bd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.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 Oct 7, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.0.8 This release

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page