Meridian handbook
Extending

Extending

The ways to add behaviour to a Meridian site without changing the engine: configuration and declared endpoints, templates and components, Go extensions loaded from engine.yaml, and experimental out-of-process plugins, with how to choose between them.

You are given the engine as a binary (engine-dmo or engine-cms) and never its source. Everything a site needs beyond what the binary does is added from the project directory, in one of four ways. Start with the first that can express what you need: each later one costs more to build, test and keep in step with engine releases.

WayWhat it addsWritten inRunsWhere it is described
ConfigurationContent types and fields, routes, sites and locales, declared endpoints (forms, JSON projections, calls to upstream APIs), outbound webhooks, scheduled feed imports, rolesYAML under config/Inside the engineContent modelling, Forms and submissions, Integrating
TemplatesEvery page’s markup, components, blocks, partials, the portal’s lookPongo2 templates under templates/Inside the engineTemplates
ExtensionsTemplate globals and filters, HTTP routes, commands, the asset helpers’ resolver, a dev server under serve --devGo, as a plugin (.so) built against github.com/wrux/meridian-extendInside the engine processExtensions
Project plugins (experimental)Logic a declared endpoint calls when YAML cannot express it: branching rules, a calculation, a partner SDKAny language that speaks the protocol; Go with github.com/wrux/meridian-pluginA separate process the engine supervisesProject plugins

Choosing

  • A new field, type, page or form. Configuration and templates. The engine validates both with engine-dmo check, renders them through the real pipeline with engine-dmo render, and reports every mistake at its file, line and column.
  • Reading another service’s JSON on a page or from an endpoint. A source in sources.yaml and the source() template function or a source step. See Sources and importers.
  • Telling another system that content changed. An outbound webhook in webhooks.yaml. See Webhooks.
  • A template function or filter the engine does not have (a class-name merger, a formatter for a house style, a lookup in a file the project ships). An extension.
  • An HTTP route that is not a page, a form or a JSON projection, or a command of your own beside the engine’s. An extension.
  • Wiring a frontend toolchain into the asset helpers and the dev loop. The first-party vite extension, or an extension of your own built the same way.
  • An endpoint whose answer needs control flow (a market that runs every Thursday except over Christmas, a price that depends on three inputs). A project plugin, marked experimental: its protocol may change in any release.

What none of them can do

  • Change how content is stored, validated, versioned or published, or add a field type. Field types are the engine’s.
  • Reach the database. Extensions and plugins see what the interfaces below hand them, and nothing else.
  • Change the admin’s screens. The admin’s look is themed with admin.theme in engine.yaml; its screens are the engine’s.
  • Bypass permissions. An extension’s route secures itself; it is given no session or permission check of the engine’s.

The checks that hold it together

Whichever way you choose, the same loop applies:

engine-dmo check --project .        # configuration, schema, templates and extensions, every problem at once
engine-dmo render / --project .     # a page through the real pipeline, in process
engine-dmo template api --project . # the template surface, including what your extensions declare

engine-dmo check loads every extension the project names and runs its Register, so a template that calls a global no extension declares, or passes a keyword the global does not take, fails the check rather than the page.

engine-cms behaves the same for everything on these pages: extensions and project plugins are core features of both bundles.