Building a realtime cryptocurrency price tracker
Learn how to work with Aurelia's reactive binding system to work with frequent data changes.
Welcome to the Aurelia crypto price checker tutorial. In this tutorial, you will learn how to build an Aurelia 2 application that works with the Coingecko price API to get cryptocurrency prices and refresh them when they change.
Just want the code? You can find the code for this tutorial on GitHub here. Feel free to use this code as a guide or starting point for your Aurelia applications.
What we will be building
A cryptocurrency dashboard that updates in real-time whenever the price of cryptocurrencies changes. Think of it as a stock ticker of sorts, but for cryptocurrency.
We will be interacting with the very generous Coingecko API to get the price data which allows 10 calls per second. For this tutorial, we will be calling the API once per second.
This tutorial will teach you how to work with Aurelia's binding system, briefly touch on Dependency Injection, show you how to make an API request, organize your code and create a value converter to format some currency values for display.
A demo of the application you will be building can be found here.
Prerequisites
Before going any further, you should be familiar with some basic Aurelia concepts and some fundamental Javascript ones. While these are not hard prerequisites, please know that some concepts used in this tutorial out of context might be confusing or difficult to understand.
You have familiarized yourself with the Aurelia template syntax.
You are familiar with component lifecycles (which we will use
binding
in this tutorial).You are familiar with Aurelia value converters and how they can be used to transform data.
You are familiar with Dependency Injection. You don't need to be a master of it, just familiar with its existence and why it matters in Aurelia.
You are familiar with
async/await
syntax. A great resource for learning can be found here.
Create the app
We will use TypeScript & Vite for this tutorial, enabling Shadow DOM to keep our styles encapsulated. Don't worry if you're unfamiliar with these concepts yet; you will learn as we go.
For this, we will use the Aurelia makes
command-line tool to create a new Aurelia application and to save time, passing in the options we want.
This shorthand syntax tells the Aurelia CLI to create a new project configured with TypeScript and Shadow DOM for style encapsulation.
Create our configuration file
We will now create a .json
file containing the cryptocurrencies we want to monitor.
Inside the src
directory, create a new file called config.json
and save it. We will watch six cryptocurrencies for this tutorial, but feel free to add more if you like.
This is just a JSON object with an array of cryptos called coins
. Feel free to add your own cryptocurrencies to this file. Make sure they conform to the coin list on Coingecko.
Create an API service
A good practice when working with APIs in Aurelia is to create a service (a singleton class) that makes the calls to the API and returns the data. This keeps the logic separate from our view models. It also allows it to be tested and is just a good habit to get into.
Create a new directory inside of src
called services
and then create a new file called api.ts
. Let's inject the Aurelia Fetch Client and write a method to fetch the prices.
Let's go over this line-by-line. Firstly, this is TypeScript, which looks/works very similar to Javascript, except it aims to provide some safety by alerting you to simple mistakes or errors before you build or deploy them.
We import the
IHttpClient
interface we will inject into our app on theconstructor
method. The benefit of using TypeScript is that you get auto-injection instead of injecting the client manually. The Aurelia Fetch Client wraps the native Fetch API and makes it more "Aureliafied".We create a
getPrices
method, which accepts an array of strings (our cryptocurrencies). We make this method async to make working with the promise that Fetch returns a lot cleaner than chaining.then
and.catch
functions in our code.When dealing with
async/await
, it is good practice to wrap your calls in atry/catch
to catch any errors that might occur (timeouts, erroneous requests, missing credentials).We then request the Coingecko API, passing in our cryptocurrencies. By calling the
Array.toString()
method, it will automatically create a comma-separated string of values as the API expects. You could also use.join
to do this as well.When making Fetch requests, the resulting Fetch call with allow us to get the response value, we know we are going to be getting JSON, so we return the
request.json()
call, which is a promise.
As for errors, if we encounter them, the catch
will capture those, and we return the error value. We have everything we need now.
Inject our service into the app
Because we used the makes
tool by default, my-app.ts
and my-app.html
will be the view model and view that are displayed. We will use these files to add in our API calls and other pieces of logic.
Inside of my-app.ts
replace the contents with the following:
We import our newly created API service as well as the
config.json
file we created in the first step asconfig
We initialize an empty object in our class called
prices
which will store our price dataWe inject the API and define it on our constructor
We specify a component lifecycle hook called
binding
which we make asynchronousInside of
binding
we make a call to ourgetPrices
method and store the value in our class propertyprices
The returned value is an object
Displaying the data
We now have our crypto prices. Let's display them. Inside of my-app.html
(our view for the app) we'll reference these price values.
This will look familiar if you have brushed up on Aurelia's template syntax. We are using interpolation to display values, but do you notice something? Prices are the object we defined inside of our view model. We are then accessing each property of this object inside of the view.
We know that the value inside of ${}
is evaluated by Aurelia. In this instance, we are referencing our prices
object and we know for each cryptocurrency we are watching that it returns a property followed by another child property for the price called usd
.
Updating the data
We have a working application of sorts. It's not pretty, but it's functional. We have a problem, though. Any price updates after the page loads won't be rendered. Because these are price values, we want to ensure the user always has the most up-to-date information. We need to tell our view model to update the prices for us regularly.
And add the priceUpdate()
call to binding function.
We poll the Coingecko API every second to update prices and store them in the prices
object. If these prices change, they are updated in the view because everything is reactively bound. Aurelia is watching this object and the properties we've bound to in our view. We use a basic setInterval
to poll the server continuously. Feel free to adjust the frequency.
Seeing what we have so far
We have the basis of a functional web application that we can now run. Open up a Command Prompt/Terminal window and navigate to your project directory, then run the start
command:
This will run the Aurelia development server, and a browser window/tab should open with your application running on the default port 9000
. Provided the Coingecko API is working, you should see unformatted prices in the view.
Making currencies look pretty using a value converter
You might have noticed our prices are displayed, but they're not formatted. At the time of this tutorial, the price of 1 Bitcoin was USD 34,354.00. However, it is being displayed without currency formatting as 34354.
So, now we will create a value converter using the Internationalization API to format our currency values as properly formed values in our view.
Inside of src
create a new folder called resources
and then inside of resources
another folder called value-converters
. It is best practice to have your resources inside a resources/components folder like this. Create a new file called currency-value-converter.ts
.
You can read about value converters here to understand what this value converter is doing.
We will not be going into detail about the Internationalization API. Still, it is highly recommended you read up on it as this is intended to be the standard way to work with dates, times, currencies and other types of localization data on the web.
Import and use our new value converter
To use this, we have to register it with Dependency Injection. This tells Aurelia our value converter exists, and we have several options. In this tutorial, we will be including it from within our view using the import
element, but you can also register it inside of the view model or globally inside of main.ts
.
If you want to learn other ways to inject dependencies, consult the documentation on components.
Open up my-app.html
again and add in the following:
This looks the same as earlier, except we are importing our new value converter. You might also notice we are using the value converter syntax with a pipe |
followed by currency
which is the name of our value converter.
The app should automatically refresh with the new changes if you still have the app running. You should now see properly formatted currency values (complete with a dollar sign and thousand separators).
Styling it with Bootstrap
We could have ended the tutorial in the previous step, but aren't you itching to make it look nicer? Using Bootstrap 5 and some simple markup, we can.
To install Bootstrap in the project directory run npm install bootstrap
Configuring Shared Styles for Shadow DOM
Unlike conventional web applications, we use Shadow DOM to keep styles encapsulated. You must make them shared styles when working with third-party global CSS libraries like Bootstrap.
Fortunately, Aurelia already helps cater for this in your apps. Inside of main.ts
you will notice some commented-out code and notes talking about shared styles.
We need to import Bootstrap's CSS first at the top of the file:
Using ?inline
is only needed for the Vite bundler and ShadowDOM. This allows us to add shared styles into our application.
Now, uncomment the .register
code already present in main.ts
. Also, make sure you replaced shared
with bootstrap
and uncomment the StyleConfiguration
import from the aurelia
package at the top as well.
Your main.ts
file should look pretty much identical to this one:
This now injects Bootstrap styles into all of our Shadow DOM components. The only downside is the global styles targeting elements like html
or body
do not get carried over (an intentional limitation of Shadow DOM).
However, you can fix this by adding the Bootstrap CSS into the header of our index.html
if you want those. Or, you can add them yourself. If you want them, add the Bootstrap CDN include in the header of index.html
. You can get the most up-to-date style include here.
Update the markup
Now we have Bootstrap added. It's time to update the markup in my-app.html
to add the data into a styled table. This is all purely markup, so we are not using any new Aurelia concepts here. You can safely copy and paste this.
Conclusion
You just created an Aurelia application that utilized a few common concepts. All of the code for this tutorial can be found on GitHub here. Feel free to do whatever you want with this. Use it as a guide or even starting point for your applications.
Reactive binding using interpolation to display values
Aurelia's dependency injection to include a separate file that handled our API calls
How to transform values in your view using value converters (taking one value and making it something else)
How to work with Shadow DOM and, specifically, what to do in the case of global style packages like Bootstrap
How to work with the Fetch client to make requests as well as
async/await
to work with promises in a cleaner way.
A working example of this tutorial can be found here.
Last updated