The Weblang low code programming language lets you write safe, portable and efficient code with minimal logic. Created to let users run code on your server, but can be extended to run anything anywhere.


Ready to try it out?


npm i weblang


let weblang = require('weblang')
let code = '=hello: world'
let state = await weblang.init().run(code)

How it works

Generally, setting a variable starts with =, variable lookup starts with $, and functions start with @.

Variables have dynamic types, just as with YAML. All variables are global, there is no scope, not even inside if and else blocks.

Functions are added through extensions. Even the core functionality can be overridden, Weblang is meant to be extended.

Set and Get variables

Variables are stored in state.vars:

# Set string variable
=hello: world

# Set number variable
=number: 1

# Set bool variable
=hello: true

# Set object variable
  a: 1
  b: 2

# Set object one liner syntax
=hello: { a: 1, b: 2 }

# Set array variable
  - 1
  - 2

# Set array one liner syntax
=hello: [1, 2]

# Set array index
=hello[0]: 3

# Set variable from other variable
=hello: world
=bye: $hello

# Set object value from other variable
=hello: world
  name: $hello

# Set array index from other variable
=hello: world
  - $hello

# Set variable, nested, dot notation
=hello.name: world

# Delete variable, making $hello undefined
=hello: null

# Delete value from object
  name: null

# Delete value from object, dot notation
=hello.name: null

# Delete array index, dot notation
=hello[0]: null

# Set variable from object, dot notation
    deep: 1
=bye: $hello.name.deep

# Set variable from array, dot notation
  - 1
  - 2
=bye: $hello[0]

# Set variable from object array, dot notation
  - name: nils
=bye: $hello[0].name

# Non existing variables are empty strings
=bye: $hello

# Set literal '$', prevents var lookup
=bye: $$hello

# Setting the same object variable merges the values
=hello: { a: 1 }
=hello: { b: 2 }

@log: $hello
# { a: 1, b: 2 }

If then else

Minimal logic is achieved through @if, @then and @else:

# If with then
      eq: nils
  =hello.name: hans

# Multiple checks
      eq: nils
      eq: /hello
  =hello.name: hans

# Checks works with dot notation as well
  $hello.name.eq: nils
  =hello.name: hans

# If then else
      eq: hans
  =hello.name: guro
  =hello.name: kari

The validations inside the if-section are from the d8a validations:

required: true # Can not be undefined
eq: 5          # Equal to
ne: 5          # Not equal to
gt: 5          # Greater than
lt: 5          # Less than
gte: 5         # Greater than or equal to
lte: 5         # Less than or equal to
in:            # Must be in list
  - 1
  - 2
  - 3
nin:           # Must not be in list
  - 1
  - 2
  - 3
length: 5      # Length of string must be
min: 5         # Minimum length of string
max: 5         # Maximum length of string
match: /regex/ # Must match regex
is: boolean    # Must be true or false
is: string     # Must be a string
is: number     # Must be a number, integer or decimal (float)
is: integer    # Must be an integer
is: decimal    # Must be a decimal number
is: date       # Must be a date
is: id         # Must be an id
is: object     # Must be an object
is: array      # Must an array
is: email      # Must be an email address
is: url        # Must be a URL
is: undefined  # Must be undefined
is: null       # Must be null

# Multiple types
is: [string, number]

# Use 'isnt' as the opposite of 'is'
isnt: null

# Works for all of the ones in 'is'
isnt: string
isnt: number
isnt: [email, id]


The @return command sets a variable in state.return. Using @return causes execution to be halted.

# Return a string
@return: hello

# Return a string variable
=hello: world
@return: $hello

# Return an object variable
  name: world
@return: $hello

# Return an array variable
  - 1
  - 2
@return: $hello

# Return a variable, dot notation
    baner: 1
@return: $hello.name


You can prefill the state with your own variables:

let req = { pathname: '/hello' }
let state = await weblang
  .init({ vars: { req }})


Weblang can (and should) be extended with your own commands.

Define an extension function like this:

// Extension function called db
function db({
  state,    // the runner's state with vars and return
  code,     // the actual code sent to weblang, untouched
  tree,     // the syntax tree like object, with ids
  branch,   // the current object being processed
  node,     // the key of the current branch
  current,  // the value of the current branch
  key,      // the setter key, usually starts with '='
  id,       // the internal id of the node
  run,      // the run function that runs your code
  opt,      // the options passed to weblang
  expand,   // the expander function used internally
  load,     // the loader, converts yml string to object
  get,      // use this to get variables and run pipes
  set,      // use this to set variables
  ok        // the validation function used for if tests
}) {

  // Example use of set
  set('=internal', 'hello')

  // Whatever you return will be in your setter
  return { id: '1' }

Write some code like this:

var code = '@db: user/create'

then run the code like this, while also adding the extension:

let state = await weblang.init({ ext: { db } }).run(code)

To set the result of the function, use the extension variable setter syntax:

=result@db: user/create

and the result variable will be available in state.vars.result.


Variables can be run through pipes, which are functions that transform a value.

If the pipe does not exist, it is ignored.

# Use pipes with string
=hello: hello | upcase

# Use pipes with variables
=hello: hello
=bye: $hello | upcase

# Use pipes with return
@return: hello | capitalize

# Multiple pipes
@return: hello | upcase | downcase | capitalize

# Pipe parameters
@return: list | join delimiter=+ max=5

You can add your own pipes, or replace the built in ones, using the pipes option:

// Add a pipe named 'hello'
let state = await weblang
    pipes: {
      hello: function({ val }) {
        if (typeof val != 'string') return val
        return 'hello ' + val

and then use it like this:

@return: world | hello

The pipes receive all the same variables as with extensions.


MIT Licensed. Enjoy!