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: trueflags — HTTP status codes already exist - Small enough to understand in one sitting
Core response rules
Success
{
"data": ...,
"meta": { ... }
}
datacontains the business payload (object, list, scalar, ornull)metacontains 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
metais 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:
fastapistarlette
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b0e901695619a8327d05834e306808fddc187bef2e9465f5169b7166f02024ec
|
|
| MD5 |
88e5e77935f4efa78f7885c777e75ec6
|
|
| BLAKE2b-256 |
d851c267eb7f615e27bd969132b0ff1482854c103e79e8be6df9debcff5c1c25
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9668c8687f1b6a9d19c492e6651b91ca628cb9baf65e0495470e384f20bb2e0
|
|
| MD5 |
bc8fc6cb444398a88c6ef026231b1827
|
|
| BLAKE2b-256 |
29f9d6a4799b846b58b8e83c09e51e8576201a546562c37ca6cf40431ebf8b49
|