Skip to main content

A small, typed JSON frame (envelope) for API responses and messages.

Project description

jsonframe

A tiny, opinionated helper for consistent JSON API response frames.

jsonframe standardizes how APIs return successful responses, collections, pagination metadata, and errors — without dragging in heavy specs or forcing a framework.


Design goals

  • Responses are always JSON objects (never top-level arrays)
  • Predictable structure across services
  • Minimal cognitive load for newcomers
  • No success: true flags — HTTP status codes already exist
  • Small enough to understand in one sitting

Core response rules

Success

{
  "data": ...,
  "meta": { ... }
}
  • data contains the business payload (object, list, scalar, or null)
  • meta contains non-business metadata (optional, always an object). Typical examples include pagination info, request IDs, timing data, or feature flags — never domain data.

Error

{
  "detail": "Invalid request payload"
}

Or structured:

{
  "detail": {
    "error": {
      "code": "validation_error",
      "message": "Invalid request payload"
    },
    "meta": { ... }
  }
}
  • Errors are represented by a single error object (no arrays, no partial failures)
  • HTTP status code communicates severity
  • meta is optional and always an object when present
  • Additional fields may be included for diagnostics (e.g. context)

Examples

Example of success payload and framed result

Given the user object and request_id:

from jsonframe import ok

user = {
  "id": 42, 
  "name": "Ada Lovelace", 
  "email": "ada@example.com", 
  "role": "admin"
}
meta={"request_id": "req_123"}

return ok(data=user, meta=meta)

Result:

{
  "data": {
    "id": 42,
    "name": "Ada Lovelace",
    "email": "ada@example.com",
    "role": "admin"
  },
  "meta": {
    "request_id": "req_123"
  }
}

Example error response (string)

from jsonframe import error

return error(message="User not found")
{
  "detail": "User not found"
}

Example error response (structured)

from jsonframe import error

return error(
    code="not_found",
    message="User not found",
    context={"user_id": 42},
    meta={"request_id": "req_123"},
)
{
  "detail": {
    "error": {
      "code": "not_found",
      "message": "User not found",
      "context": {
        "user_id": 42
      }
    },
    "meta": {
      "request_id": "req_123"
    }
  }
}

Installation

Core package

uv add jsonframe

Core dependency:

  • pydantic >= 2.0 (used for lightweight validation and serialization)

Optional FastAPI integration

FastAPI helpers are optional and not installed by default. FastAPI wraps error payloads under detail; http_error() applies this automatically without changing the core error shape.

uv add "jsonframe[fastapi]"

This installs:

  • fastapi
  • starlette

Usage

Success response

from jsonframe import ok

return ok(data={"id": 1, "name": "Ada"})

Empty success

from jsonframe import ok

return ok()

List response

from jsonframe import ok

return ok(data=[{"id": 1}, {"id": 2}])

Paginated list

from jsonframe import ok_paged

return ok_paged(
    data=[{"id": 1}, {"id": 2}],
    total=120,
    limit=20,
    offset=40,
)

Result:

{
  "data": [...],
  "meta": {
    "page": {
      "total": 120,
      "limit": 20,
      "offset": 40
    }
  }
}

Error response

from jsonframe import error

return error(
    code="validation_error",
    message="Invalid input",
    context={"field": "email"},
)

FastAPI helpers (optional)

Typical imports:

from jsonframe.fastapi import ok, http_error
# or
from jsonframe.fastapi import ok, ok_paged, http_error

Returning framed JSON

ok() and ok_paged() return plain JSON-serializable dict values that you can return directly.

from jsonframe.fastapi import ok, ok_paged

return ok(data={"id": 1})
from jsonframe.fastapi import ok_paged

return ok_paged(
    data=[{"id": 1}, {"id": 2}],
    total=120,
    limit=20,
    offset=40,
)

Raising framed HTTP errors

from jsonframe.fastapi import http_error

raise http_error(
    404,
    code="not_found",
    message="User not found",
    context={"user_id": 42},
)

What jsonframe is not

  • Not a full JSON:API implementation
  • Not a validation framework
  • Not a transport abstraction
  • Not a replacement for OpenAPI or HTTP semantics

When to use jsonframe

  • Internal APIs
  • BFFs
  • Microservices
  • AI / LLM-backed services
  • Teams that want consistency without ceremony

Philosophy

jsonframe is intentionally small.

It standardizes structure, not business logic.
If you can’t explain your API responses by pointing to this README, the library is doing too much.


License

MIT

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

jsonframe-0.1.2.tar.gz (5.1 kB view details)

Uploaded Source

Built Distribution

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

jsonframe-0.1.2-py3-none-any.whl (6.9 kB view details)

Uploaded Python 3

File details

Details for the file jsonframe-0.1.2.tar.gz.

File metadata

  • Download URL: jsonframe-0.1.2.tar.gz
  • Upload date:
  • Size: 5.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for jsonframe-0.1.2.tar.gz
Algorithm Hash digest
SHA256 b0e901695619a8327d05834e306808fddc187bef2e9465f5169b7166f02024ec
MD5 88e5e77935f4efa78f7885c777e75ec6
BLAKE2b-256 d851c267eb7f615e27bd969132b0ff1482854c103e79e8be6df9debcff5c1c25

See more details on using hashes here.

File details

Details for the file jsonframe-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: jsonframe-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 6.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for jsonframe-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 b9668c8687f1b6a9d19c492e6651b91ca628cb9baf65e0495470e384f20bb2e0
MD5 bc8fc6cb444398a88c6ef026231b1827
BLAKE2b-256 29f9d6a4799b846b58b8e83c09e51e8576201a546562c37ca6cf40431ebf8b49

See more details on using hashes here.

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