Get Set and Deploy — The Aurelia Experiment, Part 2
Liveblogging web development on Aurelia
This is the second part in the series about the development process of an Aurelia web application, written as it happens. The goal of part 2 is to build a simple prototype application using the selected technologies from Part 1. I will go through setting up the development environment, creating the front-end project, creating a server-side project for the API, and finally connect the two together. Everything should be up and running in Azure when I’m done.
Setting Up
First I need to make sure I have all the tools installed. Node.js version 6 has just been released under LTS plan so I replace my 4.x with 6.9.1, and also update npm by running npm install npm -g in command prompt running as administrator. That gives me npm version 3.10.9. With npm I then install the latest Aurelia CLI, which is currently at version 0.21.0. Just to make sure, I also update Git. There’s probably few more installs required if you haven’t installed any of these before, but I think you can easily find tutorials to get everything set up.
Visual Studio Code is the editor I will mostly be using. One of the reasons for choosing the lightweight code editor instead of the full-featured Visual Studio IDE is the recently released Aurelia Plugin for VS Code. I will also make some notes to compare the two editors. I’m not very experienced VS Code user and I generally prefer more graphical interfaces, but with Aurelia most of the heavy lifting will be done by the CLI, so the editors key functions are code highlighting, formatting and intellisense. With server-side C# I will probably still rely on the full IDE.
Creating Aurelia Project
Until now I have always started a new Aurelia project by copying the official skeleton application. With the CLI, I simply run au new and follow the instructions:
Using custom-option (3), I create a project with TypeScript transpiling (2), Sass CSS pre-processor (3), unit tests included and Visual Studio Code as the editor (1). After confirming the set up, CLI will install all the project dependencies. This takes about three minutes. After the installation is complete, I get a single warning about angular-protractor typings being deprecated:
I’m not too worried about that for now. The warning message suggests some problems with typings for Protractor (End-to-End testing framework for Javascript applications, originally built for AngularJS but can be used with Aurelia). I will probably write a separate part focused on testing later.
Before making any changes to the project, I create a new repository for the project at GitHub. At this point I realize that this should have been the very first step, before creating the project. So to get the repository inside the project I clone the repository in Github to a temporary folder and move the .git folder into my project root (tip from stackoverflow). VS Code quickly realizes there’s a repository and shows all files as changes. Just few clicks and I have my initial project committed.
Running the application in development environment is really simple with the built-in web server. You just call au run in command line and point the browser to the correct url, which defaults to localhost:9000. To have the application reload automatically when you make changes to the source files, add a --watch flag after the command.
Update
At this point, few weeks passes without a chance to continue writing/developing the project. The first thing when coming back is to check for any updates for the framework and other dependencies. This is especially true in early stages of development. There is a tool called Monterey for Aurelia projects that I sometimes use to check which packages in the project have updates available. In this case however, I decided to re-create the project from scratch, after updating Aurelia CLI to version 0.22.0. There are some small differences in the project contents when created with newer version (check out the diff).
While at it, I also update the rest of the packages. This includes aurelia-tools (1.0.0), gulp-typescript (3.1.2) and gulp-tslint (6.1.3). After the update, if you try to run the project, there will be the following warning:
This is because of some changes in the gulp-typescript interface. The fix is as simple as the warning message suggests, just a small change in the build task in aurelia_project/tasks/transpile.ts.
Layout
Next I will try to get sass and bootstrap working. Sass is already included in the project thanks to the cli, but it doesn’t have any styles yet. I want to be able to use bootstrap variables to customize how bootstrap works, so I will npm install bootstrap-sass --save-dev. Then I create the main stylesheet in src/styles/app.scss. For now it only imports all the bootstrap styles from the bootstrap-sass package.
There are couple of different approaches for including the stylesheet in the application. The old way would be to add a link-tag in the head-section of the page. However Aurelia Cli does not output processed css files by default. They are part of the application source code and will be included in the bundles. So instead of direct linking, css needs to be imported through the module loader. The first attempt is to add import in the typescript code in app.ts:
import 'styles/app.css!';
This results in an error in the build process, causing the watcher to stop (which is a bit annoying, because I will need to restart the watcher every time I have a typo in the imports). The error seems to be that the “!” at the end has no effect and the module loader still tries to load the file app.css.js, expecting a javascript module. After quick googling I notice the Styling your Application section in the Cli documentation, which advices to use a require-element in the template:
<require from=”./styles.css”></require>
This works as expected, and I can now use bootstrap classes in my template html. All the styles, including the whole bootstrap, is now included in my application bundle. It is good to notice that this adds about 154k to the bundle size, so in some scenarios splitting the stylesheet into a separate bundle (allowing separate caching) might be worth considering.
Including the bootstrap javascript is a whole nuther story. Bootstrap falls into the legacy library category when used with Aurelia Cli, and it depends on jQuery. On top of that, we would need to get our hands to the type definitions (bootstrap.d.ts in DefinitelyTyped seems to be lacking 0.0.2 versions behind), and somehow handle the fact that we use bootstrap-sass port instead of the default bootstrap package. Weighting the benefits of the JS components agains all this, I think I can live without those for now. Most of the functionality is really simple to implement with Aurelia anyway.
Content
I don’t yet have a clear plan about the purpose of the application. There are few ideas, but at the moment I concentrate on getting all the different parts working together. Nevertheless I will need some kind of content to demonstrate basic functionalities like Api calls. For that purpose I’ll use a concept of Note (think a Post-It / sticky note). I will start with a very simple Note-component that has some content and nothing else. To make things a bit more interesting, I will use contenteditable to allow users to edit the content. You can see the view model in note.ts and template in note.html.
A detour
There are libraries I could use to make a bit more user-frienly content editor. I really like the simplicity of the Medium editor, so after another quick googling, I decide trying out Medium.js. First thing is to download the package as a development dependency by running npm install moment.js --save-dev. Then I add the dependency for the Aurelia Cli to bundle with the other libraries by adding a line in the vendor-bundle dependencies array in aurelia_project/aurelia.json. After failed attempts to use the library, I realize that it probably needs to be included as a legacy library (like Bootstrap above), so I add the following few lines based on cli documentation examples:
{
"name": "medium.js",
"path": "../node_modules/medium.js",
"main": "medium",
"exports": "Medium",
"resources": [
"medium.css"
]
},
This seems to work, and I can import the library by adding import * as Medium from ‘medium.js’ at the top of the note.ts file. With libraries that support modules, import is usually in the form import { Object, AnotherObject } from ‘library’, but with legacy libraries the previous format is usually required. With TypeScript, there’s another step required to get the library working with typed source code: type definitions. Most common libraries have type type definition available either with the official distribution or as a community contribution (usually in DefinitelyTyped registry), but Medium.js doesn’t seem to have anything available. You could always write the type definition yourself, but as a workaround, a dummy definition can be used to prevent compilation errors. The project contains a folder custom_typings for custom type definitions, where the following content can be added in a file called medium.d.ts.
declare module ‘medium.js’;declare class Medium {
constructor(options?: any);
}
To be honest, I didn’t get that right the first time. The first line is the dummy module definition, and the Medium class declaration allows using Medium as a class, which is how the original module works if you include the library as a javascript file and not with a module loader.
So all good? When I create a Medium.js object agains my note, I’m not impressed. It doesn’t seem to do much else than simply setting contenteditable=”true”, at least in Chrome. Might be just that I’m not using it right, but a quick look at the Medium.js Github repository explains a lot. There’s basically no activity, and last release is from December 2015. The official-sounding name probably got my hopes up. This library probably isn’t worth the trouble, so I end up reverting all my changes and go back to standard contenteditable.
Create API
A server-side application with a persistent storage is required (well, not exactly, but we’ll keep it simple) to share notes between users and keep them saved. Here I go all in to test the tooling experience with Visual Studio / ASP.Net Core / Azure / GitHub, all technologies that should be supported out of the box. My aim is to have an ASP.Net Core application runinng on Azure, the code committed to GitHub and automatically deployed from selected branch, and a SQL database for the Api to connect to without any configuration in the source code.
The process to create a new application is straightforward. I create a new project and a solution for it, select ASP.Net Core Web API template, tick Create new Git repository and Host in the cloud, select and/or create the required Azure services (in my case I select my subscription, create a new resource group, app service plan, app service, sql server and sql database), and hit Create. Then I open the GitHub window, log in to my GitHub account and publish my Git repository. I can see the first two commits that were made by VS. So far all seems to work quite well, although Visual Studio does crash once when browsing to pull requests in the GitHub window.
Next I head to the Azure portal, where I can see all the newly created resources in the resource group I selected:
I’ve chosen Free-tier products for all the services, so I don’t have to worry about pricing. There are some limitations, but nothing I need to worry about in a small and simple prototype. The interesting bit is the automatic deployment. I authorize Azure portal to use my GitHub account and select the repository and branch (master in currently the only available brach). There’s also an option to set up automatic performance testing, so although I don’t really know how that works, I just go ahead and set that up too.
The deployment doesn’t seem to trigger automatically after setting it up, so I try committing a small change to the repository. Normally I would probably commit to a branch and then merge it to the master, possibly from a pull request, but now I just go ahead and commit to master. This seems to fire some actions in Azure, and after a while a whole lot of actions (more than 1000 requests), so I assume the performance tests are running. I browse to the url, and there’s my API running!
To keep the length of this part 2 somewhat in control, I leave the database-related stuff for later. Now I need to get the Client deployed and make it load notes from the Api.
Fetch!
I have been avoiding fetch for way too long now. A while before getting into Aurelia I read a blog post from Chrome mastermind Jake Archibald that is still one of the best explanations of fetch and it’s benefits, with a very practical approach. But despite that and Aurelia’s strong recommendations to use fetch over xhr, this is the one part where I remained safe distance from the cutting edge. Over a year later, I still haven’t used fetch much more than few quick tests. I think it’s time to change that, so I use this opportunity to get comfortable with the new way of retrieving resources from the Api.
A good place to start is the Aurelia documentation for HTTP services. First I need to install the aurelia-fetch-client through npm and include the package in the vendor bundle. Then I add a class that injects HttpClient from the aurelia-fetch-client module. But after adding the class, the application fails to compile. Turns out that TypeScript doesn’t have the Fetch Api in the default type definition library. Luckily there’s a simple solution: typings install ds~whatwg-fetch --global --save.
“Wait, what…wg?” Yep, the syntax is a bit awkward, but that’s basically telling the typings-tool to
- install type definitions from ds source (the aforementioned DefinitelyTyped library)
- for whatwg standards specification of the Fetch Api
- as global library (globally available as window.fetch())
- and save the details to typings.json (to keep track of installs and update easily).
That all works nicely, but there’s still a compile error:
Looks like definitions for URLSearchParams and ReadableStream are still missing. With Google I quickly find a solution for the latter from a TypeScript issue: typings install ds~whatwg-streams --global --save. For the URLSearchParams, the current solution in the Aurelia skeleton application seems to be a separate type definition file in aurelia-fetch-client repository, which I can install with typings install github:aurelia/fetch-client/doc/url.d.ts --save --global. And with that I finally have my project compiling again and I can commit the changes.
Next up is the actual code for fetching resources. To separate Api-calls from the rest of the application, I create NotesApi class that injects HttpClient from aurelia-fetch-client library. Then I start writing a method for retrieving all notes from the Api. I’m quite impressed how fluently IntelliSense works with TypeScript in VS Code, and equally happy with the documentation of Aurelia packages.
The code for fetching the resource is a simple fetch() call to the correct endpoint in the Api url, and a call to response.json to convert the response data from json string into javascript object (array of strings in this case). I use the environment configuration in aurelia_project/environments/dev.ts to set the base URL for all requests. To allow a request from different domain (or port), I need to set up CORS in the server application.
Finally I add data binding to the Node component and add loading and displaying of nodes in the front page. Now I got everything working, but only locally. I still need to deploy the Aurelia application into Azure.
Note that if you want to support IE (or other browsers that don’t implement fetch), you will need to use a polyfill. This should be simple as adding the library to the project and adding import ‘fetch’; to the main module, but with the prototype I will stick with browsers that do support it.
Deploy
The final step to get the whole system up and running is to deploy the client application to Azure. A simple solution would be to just run au build --env prod and deploy the result manually to Azure. But rather than doing something that I already know, I will try building a continuous deployment just like with the server application. The difficult part is that the client requires running au build after checking out the branch from git repository. To do this, I will need to add some custom deployment scripts for Azure, and for that I need Azure CLI (or at least it makes the job much easier).
First I install the cli by running npm i -g azure-cli (note that you will need admin privileges to install global modules). Then I run azure config mode asm to change the mode to service management (default is resource manager). To create deployment script for Kudu, I run azure site deploymentscript --node, which creates files .deploy and deploy.cmd for node-based deployment. Once I have the Azure web app created and automatic deployment from GitHub set up, Azure will get the latest code from the repository, run npm install as specified in deploy.cmd and deploy the result on the web server. Only remaining issue is how to run au build.
To customize deployment on Azure, Kudu will give you the most control over what happens. I’m completely new to Kudu, but the script doesn’t look too difficult to understand. To help exploring, I use Kudu console that I can access through Advanced Tools in Azure portal.
Once in Kudu services, I select Debug Console > CMD from the top menu and browse to the site/repository folder. There I can test the commands that will be run when a new version is pulled from the repository.
Getting the deploy script working takes quite a while. Some helpful tutorials are Get Started with Node.js web apps guide from Microsoft (although my app isn’t exactly Node.js app, but it is using node to deploy), Kudu wiki, and a bit old but helpful wiki page about Azure deployment that I happened to run into when googling.
There are a few changes I need to make to the deployment script. First, I need to change the order of the commands in the deployment part to first run npm install, then run au build, and as a final step run kuduSync to copy the files to wwwroot. For the build script I add a build-aurelia script in package.json so I don’t have to locate aurelia-client command. The environment is selected by adding an App setting setting AURELIA_ENVIRONMENT for the Web app in Azure portal. The value will be available in the script with the same name, so I can add -- --env %AURELIA_ENVIRONMENT% in the build-aurelia call (the first double dash is required to pass the arguments to the script). I will use stage as the environment, so I also need to add appUrl to the environment configuration in aurelia_project/environments/stage.ts. I would actually prefer to have the url directly in the App settings in Azure, but I can come back to that later.
That is the deployment done, and any changes I make and push to the main branch in GitHub are automatically deployed in Azure.
What next?
Now I have a working Aurelia application backed by an Api running on Azure with continuous deployment set up. In the following parts, I can move on to more Aurelia-specific subjects such as data binding, custom components and templates.