Skip to content

Lua API

NeoWiki provides a Scribunto library at mw.neowiki for accessing structured data from Lua modules. Use Lua when you need to render multiple properties, iterate over collections, or build custom output. For simple inline values, the parser functions are usually enough.

If you want to...Use
Read one value from a propertynw.getValue
Read every value from a multi-valued propertynw.getAll
Get a page's Main Subject (label, schema, all properties)nw.getMainSubject
Get a Subject by its ID, regardless of which page it's onnw.getSubject
Run a read-only Cypher querynw.query
List all Child Subjects on a pagenw.getChildSubjects
Inspect a Schemanw.getSchema

For definitions of terms like Subject, Schema, and Statement, see the Glossary.

Loading the library

lua
local nw = require('mw.neowiki')

Functions

nw.getValue(propertyName, options)

Returns a single scalar value for a property. For multi-valued properties, returns the first value. Use nw.getAll() when you need every value.

ParameterTypeDescription
propertyNamestringRequired. The name of the property.
optionstableOptional. { page = '...' } or { subject = '...' }. If both are passed, subject takes precedence.

Returns

The first value of the property, type-converted for Lua:

Property typeReturned type
text, url, selectstring
numbernumber
booleanboolean
relationstring (target Subject's label, falls back to target ID if lookup fails)

Returns nil when the Subject does not exist, has no value for the property, or the value is empty.

Examples

lua
nw.getValue('Founded at')                              --> 2005
nw.getValue('Status')                                  --> "Active"
nw.getValue('Process owner')                           --> "Sarah Naumann"
nw.getValue('Status', { page = 'ACME Inc' })           --> "Active"
nw.getValue('City', { subject = 's1abc5def6ghi78' })   --> "Berlin"

nw.getAll(propertyName, options)

Returns every value for a property as a 1-indexed Lua table. Use this when a property is multi-valued and you need all values. Even single-valued properties are wrapped in a 1-element table.

Same parameters and resolution rules as nw.getValue().

Returns

A 1-indexed Lua table of values, type-converted as in getValue. For relations, each entry is the target Subject's label.

Returns nil under the same conditions as getValue.

Examples

lua
nw.getAll('Websites')
--> { [1] = "https://acme.com", [2] = "https://acme.org" }

nw.getAll('Products')
--> { [1] = "Foo", [2] = "Bar", [3] = "Baz" }

local websites = nw.getAll('Websites')
if websites then
    for _, url in ipairs(websites) do
        mw.log(url)
    end
end

nw.getMainSubject(pageName)

Returns the full data of a page's Main Subject as a Lua table.

ParameterTypeDescription
pageNamestringOptional. Defaults to the current page.

Returns

A Subject table (see Subject table format) or nil if the page does not exist or has no Main Subject.

Examples

lua
local subject = nw.getMainSubject()
if subject then
    mw.log(subject.label)         --> "ACME Inc."
    mw.log(subject.schema)        --> "Company"
end

local other = nw.getMainSubject('Berlin')

nw.getSubject(subjectId)

Returns the full data of any Subject by its ID, regardless of which page it lives on.

ParameterTypeDescription
subjectIdstringRequired. A Subject ID.

Returns

A Subject table (see Subject table format) or nil if no Subject exists with that ID (or the ID is malformed).

Examples

lua
local subject = nw.getSubject('s1abc5def6ghi78')

nw.getChildSubjects(pageName)

Returns every Child Subject on a page as a 1-indexed Lua table.

ParameterTypeDescription
pageNamestringOptional. Defaults to the current page.

Returns

A 1-indexed Lua table of Subject tables (see Subject table format). Returns an empty table {} (not nil) if the page has no Child Subjects, so it's safe to iterate the result directly with ipairs.

Examples

lua
local children = nw.getChildSubjects()

for _, child in ipairs(children) do
    mw.log(child.label)
end

nw.query(cypher, params)

Runs a read-only Cypher query against the graph database and returns each row as a Lua table. Use this when a single property lookup is not enough — for example, to join multiple Subjects, filter or sort in the query, or build a custom table.

ParameterTypeDescription
cypherstringRequired. A Cypher query. Must be read-only (no CREATE, SET, DELETE, etc.).
paramstableOptional. Parameter name → value. Use $name in the query to reference them.

Returns

A 1-indexed Lua table of rows. Each row is a string-keyed table where the keys are the Cypher RETURN aliases. An empty result is returned as {}, so it is safe to iterate with ipairs without a nil check.

Scalar values come back as strings, numbers, booleans, or nil. Nested Cypher lists become 1-indexed tables; Cypher maps become string-keyed tables. Graph types convert as follows:

Cypher typeLua shape
Node{ id, labels, properties }
Relationship{ id, type, startNodeId, endNodeId, properties }
Path{ nodes, relationships }

Temporal and spatial values (from Cypher functions like datetime() or point()) are not supported. Cast to a scalar in the query — e.g. toString(datetime()) or point.x(p).

Errors

Always throws on failure; wrap in pcall if you need graceful degradation.

  • Empty or whitespace-only cypher.
  • Write or non-read-only queries.
  • Cypher syntax errors, missing parameters, or database errors.

Expensive

Every call counts as an expensive parser function. Keep an eye on your page's expensive function limit if a template calls nw.query in a loop.

Examples

lua
local rows = nw.query( 'MATCH (s:Subject) RETURN s.name LIMIT 5' )

for _, row in ipairs( rows ) do
    mw.log( row['s.name'] )
end
lua
-- Parameterised — always prefer this over concatenating values into the query.
local rows = nw.query(
    'MATCH (s:Subject {schema: $schema}) WHERE s.`Valid` = $valid RETURN s.name, s.`Expiry date`',
    { schema = 'ISMS Document', valid = 'Yes' }
)

Integer comparisons need an explicit cast in the query — e.g. WHERE s.year = toInteger($year).

nw.getSchema(name)

Returns a Schema as a Lua table so your module can inspect it at runtime. Use it to build generic infoboxes, render a property list for any Schema, or check what properties a Subject should have — without hardcoding property names.

ParameterTypeDescription
namestringRequired. The Schema name (e.g. 'Company').

Returns

A Schema table, or nil if no Schema with that name exists. An empty or whitespace-only name and the reserved names page and subject also return nil, so bad input never errors — always guard with if schema then.

Top-level fields:

FieldTypeDescription
namestringThe Schema name.
descriptionstringThe Schema description. Omitted when empty.
propertiestable1-indexed list of properties, in Schema-defined order.

Every property entry has name, type, and required. Further fields depend on type:

Property typeAlways presentPresent only when set
textmultiple, uniqueItemsdescription, default
urlmultiple, uniqueItemsdescription, default
numberdescription, default, precision, minimum, maximum
selectmultiple, options (1-indexed list of { id, label } entries)description, default
relationmultiple, relation, targetSchemadescription, default

Optional fields are omitted entirely when unset, so check with if prop.description then …. Boolean flags in the "always present" column (such as required, multiple, uniqueItems) are emitted even when false — read them directly. if prop.required then would silently skip false as well as missing.

Errors

Raises a Lua error only when name is missing or not a string. Every other case — unknown Schema, empty string, reserved name — returns nil.

Expensive

Every call counts as an expensive parser function against the page's limit. Call nw.getSchema once per page and reuse the result rather than re-fetching inside a loop.

Examples

lua
-- Fetch a Schema and read its first property.
local schema = nw.getSchema( 'Company' )
if schema then
    mw.log( schema.name )                --> "Company"
    mw.log( schema.properties[1].name )  --> first property's name
end
lua
-- Render a property overview for the current page's Main Subject — works
-- for any Schema, because nothing is hardcoded.
local subject = nw.getMainSubject()
local schema = subject and nw.getSchema( subject.schema )

if schema then
    for _, prop in ipairs( schema.properties ) do
        local tag = prop.required and ' (required)' or ''
        mw.log( prop.name .. ' — ' .. prop.type .. tag )
    end
end
lua
-- Read optional fields only after checking they're set.
for _, prop in ipairs( schema.properties ) do
    if prop.type == 'number' and prop.minimum then
        mw.log( prop.name .. ' min: ' .. prop.minimum )
    end
end

Subject table format

Subject tables returned by getMainSubject, getSubject, and getChildSubjects have this structure:

lua
{
    id = 's1abc5def6ghi78',
    label = 'ACME Inc.',
    schema = 'Company',
    statements = {
        ['Headquarters'] = { type = 'text',     values = { [1] = 'Berlin' } },
        ['Founded at']   = { type = 'number',   values = { [1] = 2005 } },
        ['Status']       = { type = 'select',   values = { [1] = 'Active' } },
        ['Websites']     = { type = 'url',      values = { [1] = 'https://acme.com', [2] = 'https://acme.org' } },
        ['Active']       = { type = 'boolean',  values = { [1] = true } },
        ['Products']     = {
            type = 'relation',
            values = {
                [1] = { id = 'r1...', target = 's1...', label = 'Foo' },
                [2] = { id = 'r1...', target = 's1...', label = 'Bar' },
            },
        },
    },
}

Notes:

  • statements is keyed by property name. values within each statement is 1-indexed.
  • type is the property type at the time the Subject was last edited. If the Schema has changed since (e.g. a property was changed from text to select), older Subjects keep their original type until they are re-saved.
  • A relation's label falls back to the target Subject ID if the label cannot be looked up (e.g. a broken reference).
  • Per-relation properties (qualifiers) are not currently exposed via Lua. Use the REST API if you need them.

Performance

Calls that look up another page or a specific Subject ID count as expensive parser functions (against the page's expensive function limit). Calls that read from the current page do not.