Infogami
Infogami is the wiki framework that Open Library is built on top of. You'll see it mentioned throughout the docs, and understanding the basics will save you a lot of head-scratching when you're looking for "where does this route live?" and can't find a controller.
The Short Version
Open Library runs on a stack of three layers:
- web.py — a minimal Python web framework (similar in spirit to Flask). It handles HTTP requests, routing, and provides the
web.ctxrequest context. - Infogami — a wiki engine built on top of web.py. It adds the concept of typed pages, versioning, and a generic database layer.
- Open Library — the application code in this repository, which defines specific types (editions, works, authors) and all the features you see on the site.
Infogami lives in a separate repository at github.com/internetarchive/infogami and is included as a git submodule at vendor/infogami/.
The Two Things Infogami Does
1. It defines typed pages
Every URL on Open Library is an Infogami page, and every page has a type. You can see all the types at openlibrary.org/type. The important ones:
| Type | What it represents | URL pattern |
|---|---|---|
/type/edition | A specific book edition | /books/OL...M |
/type/work | A logical work (all editions of a book) | /works/OL...W |
/type/author | An author | /authors/OL...A |
/type/user | A user account | /people/username |
/type/page | A generic wiki page (e.g. /about) | /about |
Each type has a schema that defines its fields. The base type classes (like Edition, Work, Author) are defined in openlibrary/core/models.py and then extended with Open Library-specific behavior in openlibrary/plugins/upstream/models.py. Both files register their classes with Infogami at startup.
2. It stores everything as typed objects
Infogami's database layer (called infobase) stores every object in two core PostgreSQL tables:
thing— one row per object, with anid,key(the URL path),type, and revision trackingdata— a JSON blob for each revision of each thing, keyed bything_idandrevision
There are also supporting tables for indexing common fields (datum_str, datum_int, datum_ref), versioning (version), transactions (transaction), and accounts (account). But the mental model is simple: every object is a row in thing with its full data as JSON in data.
How Infogami Routing Works
This is the part that confuses most newcomers.
Most routes you'll work with are defined explicitly by Open Library in openlibrary/plugins/. These have clear Python controller classes with URL patterns.
But pages that map to Infogami types (books, works, authors, wiki pages) are handled automatically by the Infogami engine. There is no explicit controller in the code. Instead:
- A request comes in (e.g.
/books/OL1M/Foo) - Infogami recognizes the URL matches a registered type (
/type/edition) - It fetches the object from the database
- It passes the object as
pageinto the matching template atopenlibrary/templates/type/{type}/view.html
So when you're wondering "where is the route for /books/...?" — there isn't one in the traditional sense. The route is implied by the type registration (see register_types() in core/models.py). The template is at templates/type/edition/view.html, and the page variable is an edition object whose properties are defined across core/models.py (base class) and plugins/upstream/models.py (extensions).
The Migration to FastAPI
Open Library is actively migrating away from web.py toward FastAPI. Here's what that means in practice:
- New API endpoints go in
openlibrary/fastapi/, not inopenlibrary/plugins/. These use standard FastAPI patterns: Pydantic models for validation, dependency injection for auth, and explicit type annotations. - Old web.py endpoints are marked
@deprecated("migrated to fastapi")but not removed until the FastAPI replacement is stable in production. - The Infogami wiki framework itself (typed pages, versioning, the
thing/dataschema) will remain for the foreseeable future. For now, you'll encounter both systems.
If you're building a new API endpoint, use FastAPI. See the FastAPI Migration guide for the workflow and patterns.
How to Query Data
From controllers, use web.ctx.site to fetch records:
doc = web.ctx.site.get("/works/OL5285479W")
keys = ["/works/OL5285479W", "/works/OL257943W"]
docs = web.ctx.site.get_many(keys)For direct SQL against the feature tables (not the Infogami tables), use:
from openlibrary.core import db
oldb = db.get_db()
oldb.query("SELECT count(*) FROM bookshelves_books")Where Things Live
| What | Where |
|---|---|
| Type definitions (base classes) | openlibrary/core/models.py |
| Type extensions (OL-specific behavior) | openlibrary/plugins/upstream/models.py |
| Type registrations | openlibrary/core/models.py |
| Infogami-type templates | openlibrary/templates/type/ |
| Plugin routes (web.py, legacy) | openlibrary/plugins/ |
| FastAPI endpoints (new) | openlibrary/fastapi/ |
| Reusable template fragments | openlibrary/macros/ |
| Infogami source code | vendor/infogami/ |
Learn More
- Lifecycle of a Network Request — traces a request through the full stack
- Understanding the Data Model — database schema details
- Template Rendering Guide — how templates receive and render data
- Infogami source code
- Legacy Infogami documentation — the original reference, somewhat outdated but still useful for details