orjson can make your FastAPI API blaze by replacing Python’s built-in json module with a much faster, Rust-based alternative for JSON serialization.
Let’s see it in action. Imagine a simple FastAPI endpoint that returns a list of dictionaries.
from fastapi import FastAPI
import uvicorn
from typing import List, Dict
import time
import json
import orjson
app = FastAPI()
def generate_data(num_items: int) -> List[Dict]:
data = []
for i in range(num_items):
data.append({
"id": i,
"name": f"Item {i}",
"description": "This is a sample description for item number " + str(i) * 5,
"value": i * 1.23,
"active": i % 2 == 0
})
return data
@app.get("/json")
async def get_json_standard():
data = generate_data(1000)
return data
@app.get("/orjson")
async def get_orjson_standard():
data = generate_data(1000)
return data
if __name__ == "__main__":
# To test, run this script and then hit /json and /orjson endpoints
# with a tool like wrk or k6 to measure response times.
# Example with wrk: wrk -t4 -c100 -d10s http://127.0.0.1:8000/json
# wrk -t4 -c100 -d10s http://127.0.0.1:8000/orjson
uvicorn.run(app, host="127.0.0.1", port=8000)
If you were to test the /json endpoint using wrk (e.g., wrk -t4 -c100 -d10s http://127.0.0.1:8000/json), you’d get a certain throughput. Now, if you switch to the /orjson endpoint, you’d likely see a significant jump in requests per second. This isn’t magic; it’s orjson’s optimized C/Rust implementation doing the heavy lifting.
FastAPI, by default, uses Python’s standard json library for serializing Pydantic models and other Python objects into JSON responses. While json is perfectly functional, it’s written in pure Python and can become a bottleneck for applications that frequently serialize large or complex data structures. orjson is designed specifically for speed, leveraging Rust for its core serialization logic, which is inherently faster and more memory-efficient than Python’s GIL-bound operations.
To integrate orjson into FastAPI, you don’t typically need to change your endpoint code directly. Instead, you configure FastAPI to use orjson for its response serialization. This is achieved by setting the json_dumps and json_loads parameters when creating your FastAPI application instance. However, a more idiomatic and cleaner way is to leverage FastAPI’s ability to automatically detect and use orjson if it’s installed and you configure the application correctly.
Here’s how you’d typically set it up for maximum benefit:
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
import uvicorn
app = FastAPI(default_response_class=ORJSONResponse)
# ... your endpoint definitions as before ...
@app.get("/items")
async def read_items():
# Example data that would be serialized by ORJSONResponse
return [{"id": 1, "name": "Foo"}, {"id": 2, "name": "Bar"}]
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)
By setting default_response_class=ORJSONResponse, you’re telling FastAPI to use ORJSONResponse for all responses unless a specific endpoint explicitly returns a different response type. ORJSONResponse is a custom response class provided by FastAPI that internally uses orjson.dumps for serialization. This means all your existing endpoints that return standard Python dictionaries, lists, or Pydantic models will automatically benefit from orjson’s speed.
The core advantage of orjson lies in its strict adherence to JSON standards and its performance optimizations. It’s significantly faster than json for most common use cases, especially when dealing with large datasets or high request volumes. It also handles various Python types directly, including datetime objects, UUIDs, and bytes, without needing custom encoders, further simplifying your code and boosting performance.
What most people miss is that orjson is not just about speed; it’s also about correctness and robustness. While Python’s json module can sometimes be lenient, orjson is more strict, ensuring that the output is always valid JSON. For instance, it correctly handles non-ASCII characters and various edge cases that might trip up the standard library. This strictness, combined with its speed, makes it a superior choice for production APIs where reliability and performance are paramount.
The next step is to explore how orjson handles custom data types and potential performance tuning for extremely complex serialization scenarios.