← Back to DevBytes

Building Web APIs with JAX: A Comprehensive Guide

Building Web APIs with JAX: A Comprehensive Guide

What is JAX?

JAX is a high-performance numerical computing library for Python that brings NumPy-like syntax together with automatic differentiation, just-in-time (JIT) compilation via XLA, and seamless execution on CPUs, GPUs, and TPUs. It is widely used in machine learning research and production for training and inference, especially when fine-grained control over performance is required.

Why Use JAX for Web APIs?

When building web APIs that need to run complex mathematical operations – such as model inference, optimization, or simulation – JAX offers significant advantages:

Prerequisites

Setting Up the Environment

Create a new directory and install the required packages:

mkdir jax-api-tutorial
cd jax-api-tutorial
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
pip install jax jaxlib fastapi uvicorn pydantic

A Simple JAX Function as an API Endpoint

Let’s start by creating a minimal FastAPI application that exposes a JAX function. The function will compute a linear transformation: y = W * x + b.

# main.py
import jax.numpy as jnp
from jax import random, jit
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI(title="JAX API Demo")

# Predefine parameters (in production, load from a file)
key = random.PRNGKey(0)
W = random.normal(key, (3, 3))
b = random.normal(key, (3,))

@jit
def predict(x: jnp.ndarray) -> jnp.ndarray:
    return jnp.dot(W, x) + b

class InputVector(BaseModel):
    values: list[float]

class OutputVector(BaseModel):
    result: list[float]

@app.post("/predict", response_model=OutputVector)
async def predict_endpoint(input_vec: InputVector):
    x = jnp.array(input_vec.values)
    y = predict(x)
    return {"result": y.tolist()}

Run the API with Uvicorn:

uvicorn main:app --reload --host 0.0.0.0 --port 8000

Test with curl:

curl -X POST "http://localhost:8000/predict" \
  -H "Content-Type: application/json" \
  -d '{"values": [1.0, 2.0, 3.0]}'

Serving a Pre-trained JAX Model

In practice you will load a pre-trained model (e.g., from Flax, Haiku, or a saved checkpoint). Below is an example using a simple neural network defined with Flax. We assume a saved checkpoint exists.

# model_inference.py
import jax.numpy as jnp
import jax
from flax import linen as nn
from flax.training import checkpoints

class SimpleMLP(nn.Module):
    hidden_dim: int = 128
    num_classes: int = 10

    @nn.compact
    def __call__(self, x):
        x = nn.Dense(self.hidden_dim)(x)
        x = nn.relu(x)
        x = nn.Dense(self.num_classes)(x)
        return x

# Global model instance and parameters (loaded once at startup)
model = SimpleMLP()
dummy_input = jnp.ones((1, 784))  # example input shape
params = model.init(jax.random.PRNGKey(0), dummy_input)

# Load weights from a checkpoint (adjust path)
params = checkpoints.restore_checkpoint(ckpt_dir="./checkpoints", target=params)

@jax.jit
def inference(params, x):
    return model.apply(params, x)

def predict_batch(inputs: jnp.ndarray) -> jnp.ndarray:
    return inference(params, inputs)

Integrate into FastAPI:

# api.py (continues from above)
from fastapi import FastAPI
from pydantic import BaseModel
import jax.numpy as jnp

app = FastAPI()

class BatchInput(BaseModel):
    data: list[list[float]]  # batch of flattened images

class BatchOutput(BaseModel):
    predictions: list[list[float]]

@app.post("/infer", response_model=BatchOutput)
async def infer_batch(input_data: BatchInput):
    batch = jnp.array(input_data.data)
    logits = predict_batch(batch)
    return {"predictions": logits.tolist()}

Asynchronous and Batch Processing

For high-throughput APIs, batch incoming requests together or process them asynchronously. FastAPI’s async endpoints work well with JAX because JAX operations are inherently blocking but fast. For batch processing, you can accept a list of inputs in one request:

@app.post("/batch_predict", response_model=BatchOutput)
async def batch_predict(inputs: BatchInput):
    # Input validation and conversion
    batch_array = jnp.array(inputs.data)
    # JIT-compiled function handles batched computation efficiently
    results = predict_batch(batch_array)
    return {"predictions": results.tolist()}

To avoid blocking the event loop during heavy computation, consider using run_in_executor with a thread pool, though JAX on CPU/GPU already releases the GIL.

Handling JIT Compilation and Caching

JAX’s JIT compilation is key to performance. To avoid recompiling on every request, define your compiled functions at module level and reuse them. For dynamic shapes, use jax.jit with static_argnums or shape-polymorphic compilation (JAX 0.4+).

import functools
from jax import jit

# Cache compiled versions based on input shape? Use jit with static_argnums if needed.
@functools.lru_cache(maxsize=128)
def get_compiled_fn(shape):
    @jit
    def compiled_fn(x):
        # Some computation that depends on shape
        return jnp.sum(x, axis=0)
    return compiled_fn

@app.post("/sum")
async def sum_endpoint(data: list[float]):
    x = jnp.array(data)
    fn = get_compiled_fn(x.shape)
    result = fn(x)
    return {"sum": result.tolist()}

Better yet, avoid shape-dependent logic in the compiled function and pad inputs to a fixed maximum shape if possible.

Best Practices

Conclusion

Building web APIs with JAX unlocks the power of high-performance, GPU-accelerated numerical computation directly behind a modern web interface. By combining JAX’s JIT compilation and automatic differentiation with a lightweight framework like FastAPI, you can create low-latency inference servers, optimization endpoints, or even simulation APIs. This guide covered the fundamentals – from a simple linear function to a pre-trained model, batch processing, and JIT caching – along with best practices to ensure production readiness. As you explore further, consider integrating JAX with other libraries (Flax, Haiku, Optax) and deploying your API using containers, Kubernetes, or serverless platforms. The combination of JAX and web APIs offers a powerful toolset for building scalable, data-intensive services.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles