Metadata-Version: 2.3
Name: jsonframe
Version: 0.3.1
Summary: A small, typed JSON frame (envelope) for API responses and messages.
Keywords: json,api,envelope,frame,schema,pydantic
Author: Rinat Advaer
Author-email: Rinat Advaer <rinat.advaer@gmail.com>
License: MIT License
         
         Copyright (c) 2026 Rinat Advaer
         
         Permission is hereby granted, free of charge, to any person obtaining a copy
         of this software and associated documentation files (the "Software"), to deal
         in the Software without restriction, including without limitation the rights
         to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         copies of the Software, and to permit persons to whom the Software is
         furnished to do so, subject to the following conditions:
         
         The above copyright notice and this permission notice shall be included in all
         copies or substantial portions of the Software.
         
         THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         SOFTWARE.
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Dist: pydantic>=2.12.5
Requires-Python: >=3.10
Project-URL: Homepage, https://github.com/advaer/jsonframe
Project-URL: Repository, https://github.com/advaer/jsonframe
Project-URL: Issues, https://github.com/advaer/jsonframe/issues
Description-Content-Type: text/markdown

# jsonframe

A tiny, opinionated library 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
```json
{
  "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
```json
{
  "detail": "Invalid request payload"
}
```

Or structured:
```json
{
  "detail": {
    "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
- `code` is always present in structured form (value can be `null`)
- `meta` is optional and always an object when present

---

## Examples

#### Example of success payload and framed result
Given the user object and request_id:
```python
from jsonframe import ok

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

result = ok(data=user, meta={"request_id": "req_123"})
```

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

#### Example error response (string)
```python
from jsonframe import error

result = error(message="User not found")
```

```json
{
  "detail": "User not found"
}
```

#### Example error response (structured)
```python
from jsonframe import error

result = error(
    code="not_found",
    message="User not found",
    meta={"request_id": "req_123"},
)
```

```json
{
  "detail": {
    "code": "not_found",
    "message": "User not found",
    "meta": {
      "request_id": "req_123"
    }
  }
}
```

---

## Installation

```bash
uv add jsonframe
```

Core dependency:
- `pydantic >= 2.0` (used for lightweight validation and serialization)

---

## Usage

### Success response
```python
from jsonframe import ok

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

### Empty success
```python
from jsonframe import ok

return ok()
```

### List response
```python
from jsonframe import ok

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

### Paginated list
```python
from jsonframe import ok

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

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

---

### Error response
```python
from jsonframe import error

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

---

### Using ErrorDetail with FastAPI
`ErrorDetail` produces the right shape for FastAPI's `HTTPException.detail`:
```python
from fastapi import HTTPException
from jsonframe import ErrorDetail

raise HTTPException(
    status_code=404,
    detail=ErrorDetail(
        message="User not found",
        code="not_found",
        meta={"user_id": 42},
    ).to_dict(),
)
```

FastAPI will return:
```json
{
  "detail": {
    "code": "not_found",
    "message": "User not found",
    "meta": {
      "user_id": 42
    }
  }
}
```

For simple string errors:
```python
raise HTTPException(
    status_code=400,
    detail=ErrorDetail(message="Bad request").to_dict(),
)
```

Returns:
```json
{
  "detail": "Bad request"
}
```

---

## 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
