Magic

Create Web apps in 1 SECOND


Hyperlambda Hello World

The word “Hyperlambda” originates from “hyper” and “lambda”, implying “web functions”. In order to understand Hyperlambda’s usefulness, it is therefor valuable to use Hyperlambda to create HTTP REST endpoints. In this article, I will take you from creating your first HTTP endpoint, to selecting and querying your database, securely - And I will do it in 10 minutes. Notice, if you prefer to watch video tutorials, here’s a video where I walk you through everything.

To create an endpoint, we will need to create a Hyperlambda file. Use for instance the “Files” menu item in your Magic Dashboard, and open up the folder called “modules”. Create a new folder inside this folder, and name your new folder “tutorials”.

Notice - By default Magic will protect files inside of the modules folder, so you’ll have to explicitly click the “Safe mode” slider to be allowed to create files and folders here. See the screenshot below.

Creating a new tutorials folder

Open this folder, and create a new Hyperlambda file and name it exactly as follows. Its name is important.

hello-world.get.hl

Click the file to edit it, and replace its default content with the following code.

return
   result:Hello World from Hyperlambda

Save the file, and open up the “Endpoints” dashboard link. Click the “Show endpoints” slider, and click the endpoint named “hello-world”, at which point you’ll see something resembling the following.

How your Hello World endpoint will look like

Click the “get” button. This will invoke the endpoint, and give you the following result.

{
   "result": "Hello World from Hyperlambda"
}

Explanation

The first parts of the filename, the “hello-world” parts, becomes it relative URL. Since we placed the file inside of our “modules/tutorials/” folder, its relative URL becomes “magic/modules/tutorials/hello-world”. The initial “magic/” parts of your endpoint’s URL, informs Magic that this is a dynamic endpoint, and will resolve to a Hyperlambda file inside your “backend/files/” folder. If you’re using Magic from your development machine on localhost, you can use the following URL to invoke your endpoint.

Notice - All Hyperlambda file IO operations will by default use the “backend/files/” folder as its “root folder”. This secures your other files, and your main web server operating system, from accidental changes. This can be configured though.

The first parts of the file extension, the “get” parts, declares that our file is an HTTP GET endpoint, that we can invoke using the GET verb. The last parts, the “.hl” extension, declares that this is a Hyperlambda file, making the Hyperlambda parser kick in and transform it to a lambda object. The lambda object is then evaluated, and whatever it returns, is transformed to JSON, which becomes the HTTP response. You can use the following verbs for your Hyperlambda files as an extension before the .hl parts.

This allows you to easily create an HTTP endpoint, wrapping any of the above HTTP verbs, controlling its URL in the process. Magic will treat your Hyperlambda files as if they were “functions”, and use your filename to figure out what HTTP verb to use to resolve it.

Notice - You can only use a-z, A-Z, 0-9, - and _ in your filenames when creating Hyperlambda endpoints. Otherwise the Hyperlambda/endpoint resolver will not accept your URL for security reasons. You should also be careful when using CAPS in your filenames, since Linux/OSX tends to see “FooBar.hl” as a different file than “foobar.hl”, creating all sorts of problems for you as you move your code between different operating systems. As a general advice, I’d encourage you to only use small characters in filenames when you create Hyperlambda files.

Creating an echo service

Create a new file called “echo.post.hl” in the same folder, and put the following Hyperlambda into it.

add:x:../*/return
   get-nodes:x:@.arguments/*
return

Then go back to your “Endpoints” menu item, find your “echo” endpoint, click it, and invoke it with the following JSON payload.

{
  "name": "Thomas Hansen",
  "age": 77
}

When you click the “post” button, the endpoint returns the exact same payload as it was given. The way this works, is that any arguments passed into your endpoint, will end up inside of an [.arguments] node, inside of your lambda, as it is being executed. So your lambda will actually look like the following when it’s executed, due to its arguments collection being passed into it.

.arguments
   name:Thomas Hansen
   age:long:77
add:x:../*/return
   get-nodes:x:@.arguments/*
return

Of course, the above [.arguments] node, will contain whatever you pass into it, allowing you to pass in any argument you wish. Try modifying the file to see this process, and change your “echo” file’s content to the following.

lambda2hyper:x:../*
log.info:x:-
add:x:../*/return
   get-nodes:x:@.arguments/*
return

Execute your endpoint, and go to your “Logs” menu item in your dashboard, and click the top item - At which point you’ll see something like the following.

Log item result

As you can see, we were able to successfully transform the entire lambda object into a string, as it was being executed - For then to put this string into our log. And as we did, we could clearly see the [.arguments] collection, that wasn’t a part of our original Hyperlambda, but rather dynamically injected into the lambda object as arguments. You can of course pass in any JSON object you wish to this endpoint. Try the following JSON as your payload to understand how JSON is transformed into a lambda object during invocation.

{
  "name": "Thomas Hansen",
  "age": 77,
  "addresses": [
     {
        "address": "Foo bar st 77",
        "zip": "98765",
        "state": "California",
        "type": "residence"
     },
     {
        "address": "Howdy Street 67",
        "zip": "12345",
        "state": "Greece",
        "type": "vacation-residence"
     }
  ]
}

Then refresh your log, and click on the last log item to see its details. This will result in something resembling the following.

.arguments
   name:Thomas Hansen
   age:long:77
   addresses
      .
         address:Foo bar st 77
         zip:98765
         state:California
         type:residence
      .
         address:Howdy Street 67
         zip:12345
         state:Greece
         type:vacation-residence
lambda2hyper:x:../*
log.info:x:-
add:x:../*/return
   get-nodes:x:@.arguments/*
return

So obviously, the entire payload is transformed from JSON to lambda, and sent into your endpoint as an [.arguments] node. The . parts above, implies that addresses is an array, where each . is an entry in the array.

Restricting arguments

You can also explicitly declare what arguments your endpoint can handle, by creating a declaration [.arguments] node in your Hyperlambda, such as the following illustrates.

.arguments
   name:string
   age:long
add:x:../*/return
   get-nodes:x:@.arguments/*
return

If you save the above Hyperlambda into some file called e.g. “explicit-arguments.get.hl”, and you refresh your “Endpoints”, and select the new endpoint - You will see that the endpoint evaluator automatically determines that this endpoint can handle arguments, what the argument names are, and what their types are. Below is a screenshot illustrating how this reflects unto your endpoint’s meta data.

Passing in arguments to Hyperlambda endpoints

You can now add arguments to your HTTP endpoint, by for instance pasting the following into your query parameters textbox.

name=Thomas&age=25

If you invoke the endpoint now, you’ll end up with the following result.

{
  "name": "Thomas",
  "age": 25
}

If you try to invoke the endpoint with a “foo” argument however, such as the following illustrates, the endpoint will return an error.

Invoking endpoint with unknown argument

The Hyperlambda evaluator doesn’t really discriminate between JSON payload arguments and QUERY parameters. From your Hyperlambda file, an argument is an argument - Period. But if you try to pass in an argument that the endpoint doesn’t allow for, the file will never be executed for security reasons.

Notice - The Hyperlambda [eval] slot, which is its “execution engine”, will not try to invoke anything starting with a . character as its node name. Which is why you will often see arguments, and data segments, starting out with a . character in this documentation.

Selecting data from your database

Notice - This Hyperlambda assumes you’ve somehow got the “Sakila” database from MySQL installed. If you don’t, you can exchange the “sakila” parts below with an existing database you’ve got somewhere, and modify the SQL to become valid SQL towards that database. Just make sure you restrict the number of records you select, in case you have thousands of records in your table, to avoid having to wait too long for the result to show up. If you’re not using MySQL, you’ll also have to exchange the [mysql.] parts with for instance [mssql.] to retrieve data from a Microsoft SQL Server installation.

Create a new file in your “Files” menu, inside your “/modules/tutorials/” folder, and name it “read-data.get.hl”. Put the following content into your file.

mysql.connect:sakila
   mysql.select:select * from actor limit 10
   return:x:-/*

Go to your “Endpoints” menu item, and refresh it if necessary, for then to choose the “read-data” endpoint, and click “get”. At which point you’ll see something resembling the following.

[
  {
    "actor_id": 1,
    "first_name": "PENELOPE",
    "last_name": "GUINESS",
    "last_update": "2006-02-15T02:34:33.000Z"
  },
  {
    "actor_id": 2,
    "first_name": "NICK",
    "last_name": "WAHLBERG",
    "last_update": "2006-02-15T02:34:33.000Z"
  }
]

Parametrizing your SQL

By combining the above endpoint with arguments, we can easily parametrize our SQL, to allow for things such as paging, filtering, sorting, etc. Modify your existing “read-data.get.hl” file, and put the following code into it.

.arguments
   limit:long
   offset:long
mysql.connect:sakila
   mysql.select:select * from actor limit @limit offset @offset
      @limit:x:@.arguments/*/limit
      @offset:x:@.arguments/*/offset
   return:x:-/*

Then try invoking the endpoint, but this time with the following arguments.

Paging your result

Authorization

Magic contains built in JWT authorization, which you can leverage in your own endpoints. Put the following line of code at the top of your file, just beneath its [.arguments] node to secure your endpoint.

auth.ticket.verify:root, admin

You have now secured access to this endpoint, such that only users belonging to the “root” or “admin” roles can invoke it. The above value is a comma separated list of roles, allowing a user to access the endpoint, only if he or she belongs to any of the roles listed - All other users will be denied access. The complete code we ended up with can be found below.

.arguments
   limit:long
   offset:long
auth.ticket.verify:root, admin
mysql.connect:sakila
   mysql.select:select * from actor limit @limit offset @offset
      @limit:x:@.arguments/*/limit
      @offset:x:@.arguments/*/offset
   return:x:-/*

You can also add comments to the file, to make it more readable. Hyperlambda accepts both multiline and single line comments, but you cannot put a comment on any line where you have Hyperlambda code. The [.description] node below, adds meta data to your endpoint, which gives it a humanly readable descriptive text as meta data. The meta data generator will return this when meta data is requested. Below is our file from above, but this time with a meta description node, in addition to some comments and spacing to make it more readable.

/*
 * This file selects items from your Sakila actor
 * database table, and returns them to the client
 * as JSON.
 */
.description:Returns actors from your Sakila database

// Arguments this endpoint can handle
.arguments
   limit:long
   offset:long

// Making sure only root and admin users can access the endpoint
auth.ticket.verify:root, admin

// Connecting to our database
mysql.connect:sakila

   // Selecting items from our database
   mysql.select:select * from actor limit @limit offset @offset
      @limit:x:@.arguments/*/limit
      @offset:x:@.arguments/*/offset

   // Returning the results of the above select statement
   return:x:-/*

You can of course use your favourite IDE or text editor to edit your Hyperlambda, but the Dashboard’s Hyperlambda editor happens to have very good Hyperlambda support, such as auto completion, syntax highlightning, etc - So it’s probably easier to use the Dashboard Hyperlambda editor.