Getting Started with the online IDE

In this tutorial we’ll take you through the steps of going from a blank model to a small application.


Project Layout

To get you up and running we’ve set up a project template in your online ide. The build system we’re going to use expects a layout with files and folders in a particular structure, and this template sets you right on track for anything but the most complicated things you can build with Alan (like connecting to external databases).

You’ll notice some top level directories here.

For now, you can ignore .alan, .vscode, deployments, migrations, systems and wiring. We’ll touch on some of those later, but we’ve got defaults set up there that will work for most basic projects.

First, lets take a look at the application model, as for most projects you’ll spend most time editing this file.

Application Model

Open the interfaces/model-hours/application.alan file in an editor. We’ll be using Visual Studio Code in the screenshots, but you can use whatever you like.

The template already has an example model set up that covers some basics. Let’s walk through it:

The model is a nested structure not unlike JSON. At the first level you’ll see some keywords starting different sections of your model:

So, starting at the root, you describe the data model of your application. You compose a data model from properties of the 6 built-in data types that Alan defines:

Build It & Run It

While getting an application for free is nice, it’s even nicer to build your own. Open the interfaces/model/application.alan file in your editor. You will notice that it is almost completely empty. Let’s quickly try to actually boot up this example project, before we proceed to make our own application.

This should be fairly easy:

Upgrade with a migration

When clicking the button ‘Alan Deploy’, you get a list from which you can choose a deployment type. For your first deployment, choose the empty option from the list. This will initialize your application with an empty dataset.

After you have completed at least one successful deployment, you can make changes to your application model and you can choose for a deployment of the migrate type which enables you to migrate your existing application data to your new application version:

This will generate a default migration ‘from_release’ which should be updated until you have specified a data source for every property in your model:

Complete documentation of the migration language is available online. This is an example where a static value is provided for a text property:

root = root as $ (
	'Name' : text = "John Doe"
)

After this, ‘migrate’ deployments can be repeated iteratively. After every successful deployment, you will need to update your migration to match your latest application model version.

At some point, it might be useful to have newly generated migration based on your latest application model. You can do this by opening the file migration.alan in the directory migrations/from_release and executing the ‘Generate migration’ command from your editor. Choose from_release as migration name, server/model.lib.link as target model and “mapping from target conformant dataset” to generate a migration that expects the source and target models to be the same while preserving your application data.

Your own application model

Let’s start over with this in your application.alan file:

users
	dynamic : .'Users'
		password : +'User Data'.'Password'
		password-status : ?'Login Status'
			active : 'Active' ( )
			reset  : 'Password Reset' ( )

interfaces

root {

}

numerical-types

This is a clean slate for an application that has users and requires authentication.

Add users

Now let’s add those users to the data model:

root {
	'Users': collection ['Username'] {
		'Username': text
	}
}

But we’re not there yet. In the users section, we specified that we store user passwords in the 'Password' property and the login status in a 'Login Status' stategroup. Let’s also add Type to the Users, such that we can configure some basic authorization rules.

root {
	'Users': collection ['Username'] {
		'Username': text
		'User Data': group {
			'Login Status': stategroup @default: 'Suspended' (
				'Active'-> { }
				'Password Reset'-> { }
				'Suspended'-> { }
			)
			'Password': text
		}
		'Type': stategroup (
			'Admin'-> { }
			'Reader'-> { }
		)
	}
}

Now, let’s press ‘Alan Build’ to see if everything is ok. There should be exactly one error: something is wrong in our client settings. Press F8 to quickly go to the error, or navigate to ./systems/client/settings.alan in the explorer. In settings.alan it says: anonymous login: enabled. But wait… we removed anonymous from users section in our application model, so that is no longer ok. To fix it, simply change enabled to disabled, and you should now be able to build the project successfully.

Add permissions

We only want Admin users to create and delete Users. Also, the password (hash) and login status are personal data that should not be updated by other users. To achieve that, we need to configure some permissions:

root {
	can-read: any user
	can-update: any user ?'Type'|'Admin'

	'Users': collection ['Username'] {
		'Username': text
		'User Data': group { can-update: user == ^
			'Login Status': stategroup @default: 'Suspended' (
				'Active'-> { }
				'Password Reset'-> { }
				'Suspended'-> { }
			)
			'Password': text
		}
		'Type': stategroup (
			'Admin'-> { }
			'Reader'-> { }
		)
	}
}

So, now we’ve defined a 'Users' collection, where each key in the collection will serve as the ‘username’ and there is a password they’ll need to provide to log in. Below we will give you some specifics about how this works, but first: let’s take a look at what we have built.

Click ‘Alan Deploy’, and select the empty deployment type from this list. This will inject a default username and password for first time use. When it’s done deploying, open your application and sign in with these credentials:

Set your password, attempt a password reset, and add an account for a family member if you like.

So, what just happened… how does this work? First, some context. The user keyword is special: it refers to an authenticated user. An authenticated user is an entry from the collection of Users because we specified dynamic : .'Users' in the users section.

With can-read: any user at the root type, we have specified that any authenticated user can read data downwards from the root node. Similarly, can-update: any user ?'Type'|'Admin' specifies that only admins can update data downwards from the root. That is, until overridden by a redefinition of can-read or can-update.

For the User Data, that is exactly what we want to do: the User Data should only be updated when the signed in user equals the to-be-updated user. The expression user == ^ takes care of that: the runtime takes the authenticated user node and ensures that it equals the parent (^) node of the to-be-updated User Data node.

Add some collections

We have authentication and authorization, but our app lacks purpose right now. Why not build a little multi-user todo app (when not sure what to do, make a todo app right?). So, let’s say our users are involved in projects and each project has stuff that needs to be done.

root {
	can-read: any user
	can-update: any user

	'Users': collection ['Username'] {...}
	'Projects': collection ['Project Name'] {
		'Project Name': text
		'Todos': collection ['Todo'] {
			'Todo': text
		}
	}
}

We should probably have a little more information for each todo, like when it was created, or to which user it was assigned. It probably doesn’t hurt to be able to write down some details about the todo either.

'Todos': collection ['Todo'] {
	'Todo': text
	'Created': natural 'date and time'
	'Description': text
	'Assignee': text -> ^ ^ .'Users'
}

Now we have written several things that need some explaining. Let’s take them one by one.

Numbers

'Created': natural 'date and time'

The property Created is a natural number property, which holds a value greater than zero. We also specified a numerical type: date and time. Properties that are the same kind of number (a ‘date’, ‘kilograms’, or ‘minutes’) all have the same numerical type. This ensures that when you’re defining computations, you will end up with the correct numerical type for the resulting value. More on that later, right now you need to register that numerical type:

numerical-types
	'date and time'

To help the user interface interpret this and serve up a nice date-time picker, we need to annotate this.

numerical-types
	'date and time' @date-time

References

The other thing that’s special here is the ‘Assignee’.

'Assignee': text -> ^ ^ .'Users'

An Assignee property holds a text value, but we want to ensure that it refers to an entry from the Users collection. Let’s break down the syntax here:

To check your new additions, build the project again. If all is well, just deploy it to see how it works in practice.

Next steps

The project template has a model filled with examples that cover what we call derivations: ways to do math with numbers or derive state groups from other data.

You can annotate your model to set default values and number formats. Check the model language docs for more details.

Migrations can be edited by hand, for instance to bootstrap your application with more data than is automatically generated. Learn more about it in the migrations tutorial.

If you want to learn more, check our documentation or ask us directly!