ContentGrid Extension Helpers
Reusable helpers for building ContentGrid extensions in Python with FastAPI.
Features
- Authentication: OIDC/OAuth token verification and a
ContentGridUserDependencyfor 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)
| File | Size | Uploaded | |
|---|---|---|---|
| contentgrid_extension_helpers-0.0.8.tar.gz | 63.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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