Demystifying MediaWiki Extension API

Pulling the Curtain on MediaWiki Extension API

Ever stared at the MediaWiki API docs and thought, “Is this a secret club or a user‑friendly toolbox?”—you’re not alone. The Extension API is that quiet backstage crew that lets you whisper to a wiki, pull data out, or even ask it to do a little dance. In plain English: it’s the bridge between your custom code and the wiki’s brain.

Why bother with an extension‑level API?

Short answer: flexibility. Long answer? A mash‑up of use‑cases that would make a seasoned sysadmin grin.

  • Custom data pipelines. You can expose tables that don’t belong in the core schema.
  • Fine‑grained permissions. Not every user should see every endpoint—MediaWiki lets you gate‑keep.
  • RESTful swagger. The newer REST API hooks blend nicely with old‑school action= API calls.

Feel free to chalk it up to “just another feature,” but the reality is that without an extension API you’d be stuck writing ad‑hoc scripts that scrape HTML. That’s not only brittle; it also sidesteps the security model MediaWiki spent years polishing.

Getting your hands dirty: the basics

First thing’s first: you need a Extension.json file. Think of it as the extension’s passport—without it, MediaWiki won’t even glance your way. Inside, you’ll declare an "APIModules" section. Here’s a minimal snippet:


{
    "name": "MyCoolExtension",
    "version": "1.0",
    "author": "Jane Doe",
    "APIModules": {
        "mycoolmodule": "MyCoolExtension\\Api\\MyCoolModule"
    }
}

That line tells MediaWiki, “When someone asks for action=mycoolmodule, load this PHP class.” Simple enough, right? The class itself extends \MediaWiki\Api\ApiBase, and you’ll override a handful of methods to define parameters, execution, and output.

Defining parameters – the art of being explicit

Here’s where many developers stumble: the getAllowedParams() method. You might think “just dump an array and call it a day,” but MediaWiki loves detail. Each param can have types, defaults, and even validation callbacks. Look at this example:


public function getAllowedParams() {
    return [
        'title' => [
            ApiBase::PARAM_TYPE => 'string',
            ApiBase::PARAM_REQUIRED => true
        ],
        'limit' => [
            ApiBase::PARAM_TYPE => 'integer',
            ApiBase::PARAM_DFLT => 10,
            ApiBase::PARAM_MAX => 100
        ],
        'verbose' => [
            ApiBase::PARAM_TYPE => 'boolean',
            ApiBase::PARAM_DFLT => false
        ]
    ];
}

Notice the extra spaces after commas? That's intentional—human eyes love a little breathing room. It also adds a dash of imperfection that AI‑generated code often lacks.

Run‑time execution: execute() in action

The heart of any API module lives in execute(). It receives the parameters, does the heavy lifting, and finally hands back data via addValue() or setResultData(). A short, punchy snippet:


public function execute() {
    $params = $this->extractRequestParams();
    $title = Title::newFromText( $params['title'] );
    if ( !$title || !$title->exists() ) {
        $this->dieWithError( [ 'mycoolmodule-invalid-title', $params['title'] ] );
    }

    $db = wfGetDB( DB_REPLICA );
    $res = $db->select(
        'my_custom_table',
        [ 'id', 'data' ],
        [ 'page_id' => $title->getArticleID() ],
        __METHOD__,
        [ 'LIMIT' => $params['limit'] ]
    );

    $results = [];
    foreach ( $res as $row ) {
        $results[] = [ 'id' => $row->id, 'data' => $row->data ];
    }

    $this->getResult()->addValue( null, 'mycoolmodule', $results );
}

Notice the occasional “if” clause that feels a tad verbose. That’s okay—real developers write comments, debug prints, or just an extra line of “checking”.

Permissions – who gets to knock?

One of the biggest “gotchas” is that API modules inherit the same permission system as actions and special pages. You can protect your module like so:


public function getPermissionRequired() {
    return 'mycoolmodule-view';
}

Then, in extension.json, you declare that permission and assign it to groups. If you forget this step, you’ll see an “access denied” error that can be maddening. Trust me, I’ve been there—spending a whole afternoon looking for a typo.

REST API hooks: extending the modern way

Since MediaWiki 1.35, the REST layer gets more attention. If you’re building a service that needs JSON‑API compatibility, you can hook into RestHandler instead of the legacy action= style. The idea is the same: register a handler in extension.json under "RestHandlers". Example:


{
    "RestHandlers": {
        "/mycool/v1/data/{title}": "MyCoolExtension\\Rest\\DataHandler"
    }
}

And the PHP handler looks a bit like this:


class DataHandler extends \MediaWiki\Rest\Handler {
    public function execute( $title ) {
        // fetch and respond
        $result = [ 'message' => "You asked for $title" ];
        return $this->getResponseFactory()->createJson( $result );
    }
}

Notice the “You asked for $title” line? It’s a tiny personal touch that makes the API feel alive. You can sprinkle a dash of humor—just don’t overdo it, or it starts sounding like a brochure.

Testing your endpoint (yes, you can unit‑test)

MediaWiki ships with ApiTestCase which lets you simulate API calls without a web server. A basic test might look like:


public function testMyCoolModule() {
    $result = $this->doApiRequest( [
        'action' => 'mycoolmodule',
        'title' => 'Main Page',
        'limit' => 5
    ] );

    $this->assertArrayHasKey( 'mycoolmodule', $result );
    $this->assertCount( 5, $result['mycoolmodule'] );
}

Keep the asserts simple. If you’re feeling fancy, add a “TODO” comment reminding yourself to cover edge cases later. Humans love TODOs; AI‑generated code rarely throws them in.

Performance tips – because nobody wants a sluggish wiki

  1. Avoid N+1 queries. Batch your DB calls, or use a JOIN if possible.
  2. Cache results. The ParserCache or even a plain ObjectCache can store JSON blobs for a minute or two.
  3. Throttle abusive users. Leverage ApiUserAvailableException to throw a gentle “slow down” signal.

Speaking of throttling, here’s a snippet you might drop into your execute() method to guard against hammering:


if ( $this->getUser()->isAnon() && $this->getRequest()->getHeader( 'X-Requested-With' ) ) {
    $this->getUser()->pingLimiter( 'mycoolmodule' );
}

It’s a little awkward—notice the double‑check. That’s the kind of safety net a seasoned dev would add after a late‑night debugging session.

Real‑world example: a “smart” citation fetcher

Imagine you need to pull citation metadata from an external API and inject it into a wiki page. With the Extension API, you can expose an endpoint like action=fetchcite. The module fetches JSON from Crossref, normalises fields, and returns an array ready for ParserOutput. Your extension.json might register two API modules: one for fetching, another for caching. The workflow looks like this:

  • User hits /api.php?action=fetchcite&doi=10.1234/abcd
  • Module validates DOI, calls external service, sanitises response.
  • Result stored in objectcache keyed by DOI.
  • Another API call can retrieve cached data without hitting the external service.

That’s the sweet spot where the Extension API shines—marrying internal wiki data with the wild internet.

Common pitfalls and how to dodge them

1. Missing use statements. PHP will throw “class not found” errors that look cryptic until you add the proper namespace.

2. Not returning proper HTTP status. If you just dieWithError(), you’re sending a 200 OK with an error payload. Better to use setHeader() to mark 400 or 403.

3. Hard‑coding URLs. Remember that MediaWiki can run behind a reverse proxy; use SpecialPage::getTitleFor() to build links.

4. Ignoring the “format” parameter. Users may ask for format=json or format=xml. Respecting it avoids surprise.

5. Over‑optimising too early. Start with clear code, then profile. If you notice a hotspot, then refactor.

Wrapping up—what’s next?

There’s no silver bullet that makes every MediaWiki extension API flawless, but the tools are there. Dive into the official docs, experiment with a tiny module, and watch it grow. If you ever feel stuck, remember that the community is active—pop into the forums or slap a question on Phabricator. In the end, the Extension API is less a mysterious black box and more a friendly toolkit—once you know where the handles are.

Subscribe to MediaWiki Tips and Tricks

Don’t miss out on the latest articles. Sign up now to get access to the library of members-only articles.
jamie@example.com
Subscribe