TodoMVC Full Stack with Azure Static WebApps, Node and Azure SQL
Davide Mauri
Posted on September 17, 2020
Note |
---|
This article is part of #ServerlessSeptember. You'll find other helpful articles, detailed tutorials, and videos in this all-things-Serverless content collection. New articles from community members and cloud advocates are published every week from Monday to Thursday through September. Find out more about how Microsoft Azure enables your Serverless functions at https://docs.microsoft.com/azure/azure-functions/. |
TodoMVC is a very well known (like ~27K GitHub stars known) application among developers as it is a really great way to start to learn a new Model-View-Something framework. It has plenty of samples done with different frameworks, all implementing exactly the same solution. This way is very easy to compare them against each other and see what is the one you prefer. Creating a To-Do App is easy enough, but not too easy, to be the perfect playground to learn a new technology.
The only issue with TodoMVC project is that it "only" focus on front-end solutions. What about having a full-stack implementation of the TodoMVC project with also back-end API and a database? Well it turns out that there is also an answer for that: Todo-Backend. There are more than 100 implementations available! Pretty cool, uh?
If you want to have a test run building a full-stack solution using a new technology stack you want to try, you are pretty much covered.
Full Stack with Azure Static Web Apps, Node, Vue and Azure SQL
Lately I was intrigued by the new Azure Static Web Apps that promises an super-easy Azure deploy experience, integration with Azure Function and GitHub Actions, and ability to deploy and manage a full-stack application in just one place, so I really wanted to try to take the chance to create a 100% serverless TodoMVC full stack implementation using:
- Vue.Js for the frontend as I find it really really cool and powerful;
- Azure Static Web Apps as I can manage the full-stack app just from one place and deploy just by doing a git push;
- Node.js for the backend, as I'm learning it and I want to keep exercising. Not to mention that is very common and very scalable;
- Azure SQL as I want to have a database ready for anything I may want to throw at it;
I searched in the TodoMVC and TodoBackend but didn't find this specific stack of technologies...so why not creating it myself, I thought? Said and done! Here's some notes I took while building this.
Azure Static Web Apps
Still in Preview but I loved it as soon as I saw it. Is just perfect for a full-stack development experience. In one shot you can deploy front-end and back-end, make sure they are correctly configured to work together (you know, CORS) and correctly secured.
Deployment is as easy as configuring a GitHub Action, that is actually automatically done for you, even if you still have full access to it, so you can customize it if needed (for example to include the database in the CI/CD process).
Azure Static Web Apps will serve a static HTML whatever you specify as the app
and will spin up and deploy an Azure Function using Node.js to run the back-end using anything you instead specify as the api
:
As you can guess from the configuration, my repo contains the front-end in the client
folder and the back-end code in the api
folder:
Front-End: Vue.js
As I'm still learning also Vue I kept the code very simple and actually started from the TodoMVC Vue sample you can find on the Vue website: TodoMVC Example.
I like this sample a lot as it shows the power of Vue.js using a single file. Very easy to understand if you have just started learning it. If you are already an experienced Vue user, you'll be happy to know the Azure Static Web Apps has a native support for Vue, so that you can build and deploy Vue CLI. I'm honestly not that expert yet so I really like the super-simple approach that Vue also offers. Plus I also think that the super-simple approach is perfect for learning, which make it just great for this post.
Call a REST API
The original TodoMVC sample uses a local storage to persist To-Do data. Thanks to the Watchers feature that Vue provides, the code JavaScript code you need to write is very simple as any changes to a watched list - todo
in this case - is automatically persisted locally via the following snipped of code:
watch: {
todos: {
handler: function(todos) {
todoStorage.save(todos);
},
deep: true
}
},
Of course, to create a real-world full-stack sample, I wanted to send the To-Do list data to a REST API, avoiding the usage of local storage, to enable more interesting scenarios, like collaboration, synchronization on multiple devices and so on.
Instead of relying on a Watcher, which would unfortunately send the entire list to the REST API and not only the changed item, I decided to go for a more manual way and just call the REST API just binding them directly to the declared methods:
methods: {
addTodo: function () {
var value = this.newTodo && this.newTodo.trim();
if (!value) {
return;
}
fetch(API + "/", {headers: HEADERS, method: "POST", body: JSON.stringify({title: value})})
.then(res => {
if (res.ok) {
this.newTodo = ''
return res.json();
}
}).then(res => {
this.todos.push(res[0]);
})
},
Connecting the addTodo
method to an HTML object is really simple:
<header class="header">
<h1>todos</h1>
<input class="new-todo" autofocus autocomplete="off" placeholder="What needs to be done?" v-model="newTodo"
@keyup.enter="addTodo" />
</header>
With these changes done, it's now time to take a look at the back-end.
Back-End: Node
Azure Static Web Apps only support Node.js as a backend language today. No big deal, Node.js is a great, fast and scalable language that works perfectly with Azure Function and Azure SQL so we're really good here. If you are not familiar on how to run Azure Function with Node.js and Azure SQL make sure to read this article: Serverless REST API with Azure Functions, Node, JSON and Azure SQL. As Azure Static Web Apps uses Azure Functions behind the scenes, everything you learned for Azure Function will be applicable to Azure Static Web Apps back-ends.
The client will send a HTTP request to the back-end REST API passing the To-Do payload as JSON. For example to mark a To-Do as done, this JSON
{"completed":true}
will be send via a PUT request:
https://xyz.azurestaticapps.net/api/todo/29
to set the To-Do with Id 29 as done. If everything is ok the REST API will return the entire object, to make sure the client always have the freshest data:
[{
"id":29,
"title":"Write about Vue",
"completed":1
}]
Thanks to Azure SQL support to JSON, the back-end doesn't have to do a lot...just turn an HTTP request into a call via the TDS protocol supported by Azure SQL but beside that there isn't a lot to do. JSON will be passed as is, so what the back-end really has to do is to make sure that depending on the HTTP request method invoked, the correct Azure SQL operation will be executed. For example a PUT request should call and UPDATE statement. Implementation is very easy:
switch(method) {
case "get":
payload = req.params.id ? { "id": req.params.id } : null;
break;
case "post":
payload = req.body;
break;
case "put":
payload = {
"id": req.params.id,
"todo": req.body
};
break;
case "delete":
payload = { "id": req.params.id };
break;
}
If you have more complex needs you may decide to implement one function per HTTP request method, but it this case would have been an overkill. I really try to follow the KISS principle as much as possible. The simple the better. But not simpler! (Of course if that would be production code I would check and make sure that JSON is actually valid and harmless before passing it to Azure SQL. Never trust user-provided input, you never know!)
Database: Azure SQL
Azure SQL has been created with just one simple table:
create table dbo.todos
(
id int not null primary key
default (next value for [global_sequence]),
todo nvarchar(100) not null,
completed tinyint not null
default (0)
)
As a developer I still prefer to use JSON in the backend and to send data back and forth to Azure SQL, so that I can also minimize the roundtrips and thus improve performances, so all the stored procedures I'm using have this very simple signature:
create or alter procedure [web].[get_todo]
@payload nvarchar(max)
Then inside the stored procedure I can then use OPENJSON
or any of the JSON functions to manipulate JSON. This way it becomes really easy to accept "n" To-Do as input payload. For example, let's say I want to delete three To-Dos at once. I can pass something like
[{"id":1}, {"id":2}, {"id":8}]
and then just by writing this
delete t from dbo.todos t
where exists (
select p.id
from openjson(@payload) with (id int) as p where p.id = t.id
)
I can operate on all the selected To-Dos at once. Super cool, and super fast! The ability of Azure SQL to operate both with relational and non-relational features is really a killer feat!
Why Azure SQL and not a NoSQL database?
Answering that question could take a book so let me try to summarize. A NoSQL database for a To-Do list app is more than enough. But I always try to think about future improvements, and I want to make sure than anything I'd like to do in future will be reasonably well supported by my database. I might need to have geospatial data, to aggregate data to do some analytics, I may want to use graph or I may need to create a concurrent system to allow more than one person working on he same to-do list and I need a structure without locks. All these things are available inside Azure SQL without requiring me to use anything other than a technology I already know. This means that I'll be super productive. I won't even have scalability issues as with Azure SQL I can go up to 100 TB.
A To-Do list has a pretty well-defined schema, and the performance I can get out of a properly designed relational database are exceptional and cover a huge spectrum of use cases. With a NoSQL database I might squeeze a bit more performances when I focus on a very specific use case, but at the expense of all the others. I really want to keep door open to any improvement so, for this time, for my use case and future needs, I think Azure SQL is the best option I have here.
Keep in mind that well-defined schema doesn't mean carved in stone. I can have all the flexibility I may want as I can easily store To-Do as JSON (or just a part of it) into Azure SQL, mixing relational and non-relational features, allowing end-users to add custom field and properties if the want to. Actually, you know what? That looks like a great idea for a post. I'll definitely write on on this topic, so stay tuned!
Conclusion
Creating and deploying a full-stack solution is really easy now, thanks to Azure Static Web Apps. Completely serverless, you can just focus on coding and design while enjoying the simplicity - along with scalability and flexibility - that serverless solution offers. Azure SQL will guarantee that your solution is future-prof, providing scalability out and up to 100 TB with all the perks of a modern post-relational database, like multi-model support, security built-in, columnstore, lock-free tables and anything you may need in your wildest dream.
As usual enjoy the full source code here: https://github.com/Azure-Samples/azure-sql-db-todo-mvc
Posted on September 17, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.