You can mount your Chainlit app on an existing FastAPI app to create custom endpoints. One good use case for this is to serve an assistant through a rest API.

How it works

To mount your Chainlit app on an existing FastAPI app, you will have to define a subpath for your Chainlit app.

app.py
from fastapi import FastAPI
from chainlit.utils import mount_chainlit

app = FastAPI()

@app.get("/app")
def read_main():
    return {"message": "Hello World from main app"}

mount_chainlit(app=app, target="my_cl_app.py", path="/chainlit")

Now run your FastAPI app and you will be able to access your Chainlit app at /chainlit.

uvicorn app:app --host 0.0.0.0 --port 80

Use Chainlit APIs in your endpoint

To use Chainlit APIs, a Chainlit context is required.

HTTP context

In an HTTP context, Chainlit APIs such as Message.send() will do nothing.

If data persistence is enabled, the Chainlit APIs will still persist data.

from fastapi import FastAPI
from chainlit.utils import mount_chainlit
from chainlit.context import init_http_context
import chainlit as cl


app = FastAPI()

@app.get("/app")
async def read_main():
    init_http_context()
    await cl.Message(content="Hello, I am a chatbot!").send()
    return {"message": "Hello World from main app"}

mount_chainlit(app=app, target="my_cl_app.py", path="/chainlit")

Websocket context

The only use case that requires to use the Websocket context within a custom endpoint is to send data to a websocket client (which you know the session ID of) based on some arbitrary HTTP request.

my_cl_app
import chainlit as cl


@cl.on_chat_start
def main():
    print("Session id:", cl.user_session.get("id"))
main
from fastapi import FastAPI, Request
from chainlit.utils import mount_chainlit
from chainlit.context import init_ws_context
from chainlit.session import WebsocketSession
import chainlit as cl


app = FastAPI()

@app.get("/hello/{session_id}")
async def hello(
    request: Request,
    session_id: str,
):
    ws_session = WebsocketSession.get_by_id(session_id=session_id)
    init_ws_context(ws_session)
    await cl.Message(content="Hello World").send()
    return "Data sent to the websocket client"

mount_chainlit(app=app, target="./my_cl_app.py")

Authentication

You can use any authentication system since the request is accessible. However Chainlit authentication is fully compatible with custom endpoints.

from typing_extensions import Annotated
from fastapi import Request, Depends, FastAPI
from fastapi.responses import (
    HTMLResponse,
)

from chainlit.context import init_http_context
from chainlit.auth import authenticate_user
import chainlit as cl

app = FastAPI()

@app.get("/hello")
async def hello(
    request: Request,
    current_user: Annotated[
        cl.User, Depends(authenticate_user)
    ],
):
    print(current_user)
    init_http_context(user=current_user)
    await cl.Message(content="Hello World").send()
    return HTMLResponse("Hello World")

Once an endpoint is protected, it will require to have a valid token in the Authorization header.

headers
{
  "Authorization": "Bearer TOKEN"
}

Generate a token

The token is the same token generated when you login in the Chainlit app. You can generate a token manually for a given user with this python script.

This will require to have a CHAINLIT_AUTH_SECRET. You can run chainlit create-secret to create one.

from chainlit.auth import create_jwt
import chainlit as cl

print(create_jwt(cl.User(identifier="USERNAME")))