I hear ya cluckin' big chicken! the nervous rustling of code, tech, and life

Simply SPA – REST, Web API and Vue.js

HeresJohnnyKnow enough to be dangerous!

Index

1. Companion media

2. Covering the basics

3. Original Project Background  (and a gratuitous rant about Upwork)

4. Credits and Contributors

5. Why build a SPA?

6. Considerations and hurdles to building a SPA

7. Getting comfortable with REST

8. Getting comfortable with ASP.NET Web API

8. Basic API Testing and Debugging

9. Getting Comfortable with Vue.js and the ToDo SPA

10. Next Steps

Companion Media

  • GitHub repository (Visual Studio 2015 solution files)
  • ToDo SPA Live Demonstration. The demonstration is limited in that, (as a security precaution), database updates are not allowed.
  • A companion YouTube Playlist to introduce the use of Fiddler with VS 2015. The videos also help to visualize SPA, REST, and HTTP using this ToDo SPA.

Covering the Basics

This post focuses on the fundamentals of building a simple SPA. This includes setting up and testing RESTful endpoints (“resources” in REST nomenclature) using a simple ASP.NET Web API project template. The SPA UI uses a minimalist Vue.js (and vue-router.js) configuration. It demonstrates simple and snappy 2-way binding via 3 SPA routes; list, edit and add. This post does not delve into more advanced Vue.js topics; i.e. components, vuex, single file components, and associated tooling required for more robust applications.

Also, this example uses the “full” Vue.js build (vue.js) rather than the “runtime-only” build (vue.runtime.js). The detail about what that means comes later in the Vue.js section.

Another design caveat worth mentioning is that I used jQuery, (for AJAX support), and Bootstrap (for its glyphicons and button support). Vue.js has no dependencies beyond an ECMAScript 5 compliant JavaScript engine. In this example, the ToDo SPA was an add-on to a pre-existing web app; jQuery and BS3 were already in use and therefore being cached. As an aside, Axios is currently the preferred library for AJAX requests within a Vue.js app, (see here for more detail).

Original Project Background

On a recent Upwork gig I was asked to add a simple ToDo-styled task manager to an existing mobile web app. The new functionality was to augment an application used by mobile IT tech’s to track time and mileage.

I was given free rein over the design of the ToDo interface just as long as it was housed within a preexisting Visual Studio solution, and used the same domain. I was thankful for this because the preexisting application harbored some structural anomalies that I did not want to propagate.

Credits and Contributors

Some of this Vue.js ToDo is borrowed from a Vue.js CRUD on GitHub; especially the refreshingly simple approach to templating and routing. The app. is also influenced by the Vue.js example at TodoMVC.com.

Most of the built-in Visual Studio project templates (for Web API) add lots of fluff (packages and libraries) that distract from learning the fundamentals. As an alternative, I used a no-nonsense template named “WebApi 2 Empty Template” (by Lee Cheneler). It may be installed via VS (Tools => Extensions and Updates => Templates), or downloaded directly here. If you decide to use this barebones template you will need to perform a little housekeeping via the VS NuGet Package Manager. In a nut shell, 1) restore its missing packages, 2) update all the packages, and 3) add in Entity Framework. Note that at least one VS exit and reload may be required along the way.

Why build a SPA?

It’s easy to build a basic ToDo app using ASP.NET WebForms or MVC. However, the single page application (SPA) model is a great way to grow your client-side skills and dive into RESTful API design.

Empower your Data

Although REST isn’t explicitly defined in terms of the HTTP protocol, it is most commonly realized via the web. HTTP clients live on every viable platform so having a RESTful API can open up all sorts of possibilities for your organization’s mission critical data. The same API can be leveraged to drive applications across the entire spectrum; a toaster or refrigerator, an Android or iPhone, or a PC. It’s broad and sweeping, and something that Web Services (SOAP and WSDL) was never able to deliver.

Facilitate a Snappy UX (User Experience)

Much of SPAs responsiveness can be attributed to the decoupling of data (typically formatted as JSON) from its presentation components: HTML, CSS and JavaScript. So instead of full page reloads (more prevalent in server-centric frameworks like ASP.NET WebForms and MVC), AJAX calls execute in the background to update only the portions of the UI that need it. This is done by exposing “resources” on the server. Resources are sort of like web services and applications that rely on web services (SOAP and WSDL) share many of the same advantages. However, REST requires far less overhead, both in terms of configuration (on both server and client), as well as with transport costs, (JSON is more compact/efficient than XML). Combine all that with:

* advances in raw device/hardware power, and

* exciting new cross-browser capabilities, (web standards & APIs)

SPA brings a near-native UX; a responsive and snappy UI that can rival any native mobile or desktop application.

Holistically more Agile

Also, given the separation of concerns (SoC) baked into the REST style, a more Agile development process can be realized. In project planning, if a well-formed, RESTful API model is agreed upon, two teams, (backend and frontend) can more easily work in parallel. Using just a text editor to mock up JSON, and proxy debuggers like Fiddler. Affective unit testing can progress even without a fully functional API.

Considerations and hurdles to building a SPA

Perhaps JavaScript is the biggest hurdle for a traditional, Microsoft-based web application team. Even today, late-2017, there are still organizations that simply don’t have the resources or will to retool. Many still label JS as something less than a true programming language, and/or something to be learned over a long weekend. JavaScript may be quirky but it’s far from trivial or inconsequential. If your group is spinning web apps, and you’ve no resident JavaScript pro’s, it’s time to change your mindset.

Another common hurdle to building a SPA is in choosing a framework. SPA frameworks present a tradeoff between complete flexibility, and buying into another’s opinion on how best to build a SPA. As argued by Michael Mikowski in “Do you really want a SPA framework?”, perhaps a better approach is to master:

* JavaScript, HTML5, CSS3,

* SPA-centric web standards, and

* SPA architectural patterns; REST, AJAX, routing, templating etc.

I believe there are merits in this approach, especially if you’ve a giant budget! There is no way around the learning curve associated with SPA, and frameworks definitely come with their own learning curve. If you want flexibility and simplicity consider looking past the hyped up frameworks like Angular, React, and Ember. Thankfully there are newer, “next-gen” frameworks that are generally more intuitive and can be adopted in a more progressive manner. Vue.js and Aurelia are good examples, they are relatively less popular but quickly gaining momentum. This is because they play well with other JS libraries and are relatively simple, flexible and scalable.

Also, SPA has somewhat matured over the last few years, many challenges associated with the SPA model have been mitigated. Certainly with business applications, (portals and tools used primarily by employees, partners or web-savvy customers), many of the old SPA complaints are a non-issue. For example, because a SPA functions all within the context of a single web page, (UI updates are handled with AJAX, not a page refresh), things like search engine optimization (SEO) and forward/backward page navigation are not intrinsically supported. However, if your business users are familiar with native smartphone apps, they can easily adapt to a well-designed SPA; one where history-based navigation is depreciated. And generally speaking, SEO is not a requirement to business productivity apps. Having said all of that, client-side routers can be leveraged to deal with these issues; they are plentiful and easier than ever to plug in.

And finally, as a closely related aside, a SPA that is expected to scale gracefully requires a modern workflow. In the brave new world of client-side mega-apps, this is where JavaScript "tooling" comes in and unfortunately there are lots of moving parts; Node, NPM, ES6 and transpiling, linting, bundling and minification. Mastering the modern JavaScript stack and development workflow is not for the faint of heart. It could perhaps warrant the hiring of outside help to jump-start a robust build infrastructure.

Getting comfortable with REST

One of the many great things about REST is that it’s intuitive, far more so than say SOAP. This makes it easier to conceptualize and model a new API from scratch. There are many great posts that explain its historical impetus and fundamental concepts. If you’re new to REST, here are a few great introductions: The History of REST APIs and The Fundamentals of REST API Design.

REST originates from a doctoral dissertation by Roy Fielding, in it he describes an architectural style bracketed by constraints. The limiting undertone of a "constraint" is ironically what drives its benefits and success. A deep dive into REST is outside the scope of this article so I’ll be focusing on the bits that are most relevant to building a simple CRUD-centric, RESTful API via Microsoft’s .NET, Web API framework. Wikipedia has a decent section devoted to Fielding’s constraints a well written take on how REST constraints apply to modern web application design.

REST Basics and a few comparisons to Web Services (SOAP and WSDL)

An API that follows the REST (Representational State Transfer) architectural style (thereby conforming to its constraints) is said to be “RESTful”. Resource modeling is the exercise of designing a RESTful API frontend, which is simply an addressable URI hierarchy that describes how the API is organized and accessed for client/browser consumption.

Resources can be just about anything but for our purpose we’re talking about “todos”, simple tasks assigned to a given user. The concept of “resource” and “resource modeling” is front and center in REST. A REST resource is somewhat analogous to a web service endpoint but it’s more abstract; it’s conceptually closer to an object in OOP. A resource is some “thing” (or things) that can be uniquely accessed via URI (Uniform Resource Identifier). As an abstraction, it should not be reasoned about in terms of data formats, or transfer protocols. For example, a web service endpoint is generally thought of in terms of XML being served to a client. A REST resource is much different in that the tangible and serialized representation of a REST resource is separate from the resource itself. So, make note of 2 important REST buzzwords: “resource” and “representation”.

For comparison purposes, a web service is generally thought of in terms of some action or procedure, (like “getTodos{userId}, or addTodo{userId}” ); it is grammatically more like a verb. In a non-RESTful API you may find the “intent” of a particular endpoint embedded in the procedure name, the URL and/or the query string. A REST resource is more like a “noun”…a person, place or thing(s). In REST, the “get” and “add” are typically both handled by one RESTful URI, “…/api/users/{userId}/todos/”. The HTTP verb (a.k.a. HTTP method), in the request from the client, defines intent and action. The most common CRUD scenarios translate to the following HTTP verbs:

Create - HTTP POST,

Read - HTTP GET,

Update - HTTP PUT and,

Delete – HTTP DELETE

The REST Resource Model

The RESTful resource model should be designed around the business domain, that is to say that the objectives of what is consuming your API should drive the naming of its resources. Key nouns that define business rules and logic are good candidates for root level resource names. Hierarchical, or parent-child, relationships can be modeled as sub-resources.

Modeling the “Todo” resource

With respect to the Todo SPA, the primary goal (per client specification) was to list Todos assigned to a single logged in user. Subordinate to this was:

1) the ability for a logged in user to make changes to todo's assigned to them (including re-assignment to a different user) and,

2) the ability for a logged in user to add new todos for any user.

Clearly there are two resources, a "Todo" and a "User", and a relationship between them. Every Todo must be assigned to a user, and if the converse holds true, something like, “there can be no Todo without an assigned User”, then the relationship is relatively straight forward. “/api/users/{userId}/todos/” is the URI template that defines the resource hierarchy. It starts with “api” by convention and also to prevent routing conflicts. Segments within curly brackets signify a parameterized value and the other segments are literals. Using this template we can fill in the model with respect to CRUD operations and HTTP verbs:

CRUD Operation

HTTP Method

URI Template/Pattern

Create (add new todo)

POST

/api/users/{userId}/todos

Read (get all todos for user or a single todo)

GET

/api/users/{userId}/todos/{todoId}


here the {todoId} is optional, if it's left off then all of the users' todo's are returned

Update (change a todo assigned to user)

PUT

/api/users/{userId}/todos/{todoId}

Delete (remove a todo assigned to a user)

DELETE

Not implemented, only admin user is allowed to delete a todo (permissions and roles not implemented)

 

Curve ball !!!

There is however, one last business requirement; the ability to toggle a todo completion status directly from the user’s list of todos. This falls outside the neat and tidy realm of an update via HTTP PUT. In REST, a resource can also be a business process, and toggling the completion status of a ToDo may require additional workflow; authorization, logging, message notification, etc.  Given that REST is not a defined protocol, there tends to be debate on how best to handle outliers. To model this requirement, I chose to use HTTP POST  and the following URI template:

          “/api/users/{userId}/todos/{todoId:int}/changeofstatus

Modeling the “User” resource

Given that a user can reassign a ToDo to a different user (per specification, see #1 above), a list of user names (with primary key/id) is an important requirement that our model needs to account for. I chose to model the URI as:

     “api/users/names

for this purpose. And given that this resource returns a list, HTTP GET is the proper verb.

Note that I could have used the URI “api/users" to fetch the list of user names. However, it's reasonable to assume that the "user" resource will eventually require its own UI, supporting its own CRUD operations to manage users and their access. So therefor it is perhaps best to reserve “api/users” for the the future.

Getting comfortable with ASP.NET Web API

Even though ASP.NET Web API is now being upstaged by Web API for ASP.NET Core, (and if you’re new to both), there is still value in going back and reading a good intro. The fundamentals of ASP.NET Web API carry through to .NET Core. I believe one of the best introductions to Web API was written by Rick Strahl way back in 2015. The first 2-3 sections of the article (everything before “Getting Started” section), is great copy on the evolution of Microsoft’s approach to HTTP services. Also, if you’re interested in reading a great summary of how ASP.NET Core fits into the puzzle, I suggest reading “Goodbye Web API: Your Guide to RESTful APIs with ASP.NET Core”, by Matt Watson. The first couple of sections do a great job of explaining the relationships and evolution of the following .NET libraries and frameworks:

ASP.NET MVC   >   ASP.NET Web API   >   ASP.NET Core (the logical confluence of MVC and Web API)

Enter ASP.NET Web API

Rather than dive into lots of detail on the inner workings of the ASP.NET Web API framework, I’ll touch on the basics of Routing, a little about Action Results and basic API debugging. If you are completely new to Web API then you may want to browse over Microsoft’s well written guide by Mike Wasson.

Routing (in Web API)

Every client HTTP request will need to be directed to a C# class and in MVC and Web API parlance this class is called a controller. Typically, logic associated with each REST resource is encapsulated within its own controller class. C# methods in a controller class that process specific operations, (CRUD operations for example), are called action methods, or simply, actions. The binding of RESTful URIs to actions is called “routing”.

In ASP.NET there are two types of routing, convention-based routing and attribute-based routing. In convention-based routing the URI is compared against a list of URI templates, called a route table, and if a match is found a series of “conventions” are used to direct processing to the right controller and action. One advantage of convention-based routing is that URI templates can be defined and maintained in one place; in the Global.asax (or WebApiConfig.cs). A succinct write-up about convention-based routing may be found here. Some URI modeling scenarios (hierarchical/nested models) were awkward with this approach so attribute-based routing was added.

In attribute-based routing the controller class and its action methods are decorated with attributes that explicitly define matching URI signatures. For example, the ToDo controller class may use a "RoutePrefix" attribute to define a root-level route. The following 2 snippets come from ToDoController.cs.

1
2
3
4
5
6
7
8
namespace TodoApi.Controllers
{
    [RoutePrefix("api/users/{userId:int}/todos")]
    public class ToDosController : ApiController
    {
        private TodoContext db = new TodoContext();
         
......

Note the ":int" in the user id segment of the attribute. This is called a route constraint and acts as a gatekeeper; if that particular parameter/segment does not convert to an integer value then the URI will not be matched to this controller.

Further, the action methods define their own attributes. For example, the action method defined to handle the retrieval of 1 or all todos uses the "Route" attribute as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[HttpGet]
[Route("{todoId:int?}")]
// Handles requests for CRUD "Read". "todoId" is optional, if it's null, all todos are
//   returned, otherwise the list will contain a single todo.
public IQueryable<tblTodo> GetTodosByUser(int userId, int? todoId = null)
{
    var todos = db.tblTodos.Where(t => t.fkUserId == userId);
 
    // If valid todoId is passed then return a list with just a single todo
    if (todoId != null)
        todos = todos.Where(t => t.RecId == todoId);
 
    return todos;
}

Note the ":int?" in the todo id segment of the attribute. Given the URI has already matched the controller-level route prefix, then this binds the URI to the "GetTodosByUser" method if the following segment contains and integer (the Todo Id) or nothing (for all Todo's assigned to the user).

Explicit HTTP verb/method binding

Note that there are also attributes to explicitly bind action methods to HTTP verbs. As an alternative there are naming conventions that serve the same purpose. For example, method names that begin with “Get” will (by convention) be mapped to HTTP GET. I prefer to override the rules of convention and use the HTTP method attributes, e.g. [HttpGet], [HttpPost], [HttpPut], etc. Using these attributes helps clarify the intent and purpose of each action method.

Attribute-based routing take-aways

As matter of practice, when relying on attribute-based routing, it’s a good idea to decorate every controller method with attributes. If the method is truly an action method, explicitly decorate it with both an [Http…] method attribute as well as a [Route(“…”)] attribute. Even if the full route is defined in the controller-level [RoutePrefix(“…”)] attribute, the action may still require [Route(“”)]. In fact, (as I discovered) the following action will not match if

     [Route("")]

is omitted.

1
2
3
4
5
6
7
        [HttpPost]
        [Route("")]
        // Handles requests for CRUD "Create"
        public HttpResponseMessage AddTodoByUser(int userId, [FromBody] tblTodo todo)
        {
            var todos = db.tblTodos;
......

If non-action methods exist within the controller class, (helper methods that do not map to a route), it’s a good idea to decorate them with the [NonAction] attribute so everyone knows (including the framework) that the method is not an action method.

As a final note on routing, both approaches can be combined and deployed in the same project, to leverage the advantages of both.

Action Results

An “Action Result” is simply the return value of an action method. From the action result, the Web API framework builds an HTTP response message to send back to an HTTP client. The process of building a well-formed HTTP response message is abstracted away and for the most part, the framework does a good job of handling just about any return type. For example, even void can be returned; an HTTP response message with status code “204" No Content” is generated. The framework provides 2 types that are designed to give you complete control of the HTTP response, HttpResponseMessage and IHttpActionResult. They allow you to easily modify the status code, (and any http response header), as well as overriding how the content (message body) gets serialized. Any other return type returns status code 200 and the framework serializes the type for the response message body. More detailed information about Web API serialization can be found here, and info on media formatters here.

Here are a few great links that provide more guidance and insight into action method return types; how and when to choose between the different return types, etc.:

* Choose the right return type for WebApi controllers by Alastair Crabtree

* Action Results in Web API 2 by Mike Wasson, and

* Action Results in Web API by Srinivas Vadepally

The Todo API purposely uses a smattering of return types to encourage discovery; a great segue into the next topic.

Basic API Testing and Debugging

Given the client/server nature of HTTP and a REST API, debugging is required in both environments; client and server. On the server side, the Visual Studio debugger is utilized to step through code controllers in the same way that you would debug a WebForms or MVC project. On the client side there are a number of simple techniques that can be helpful.

The simplest way to test a resource, (thru to its corresponding action method inside a controller), is to enter the full URL in to the browser’s address text box. Depending on the “Project URL” (defined in the VS project properties, ‘Web’ tab), you should be able to enter something like: http://localhost:58949/api/users/2/todos.

Requests from the browser’s address bar always generate an HTTP GET request this URL should generate a list ToDos for userId 2.

RawApiResultsExample

To gain deeper insight into what is happening at the HTTP level you can use tools built right in to most browsers. For example, F12 in most browsers will activate a set of developer tools for all things browser front-end. Generally, most of these interfaces have a network tab or panel that allows inspection of HTTP requests and responses (headers and body).

There are also other, standalone tools that can be used to intercept, analyze, test and troubleshoot HTTP traffic. I prefer Fiddler to the Chrome Developer Tools Network panel, and WireShark is another popular option. I have created a companion YouTube Playlist to introduce the use of Fiddler with VS 2015. The videos also help to visualize SPA, REST, and HTTP using this ToDo SPA.

There are lots of good YouTube videos that demonstrate the use of these tools, here are a few:
The Network Panel In The Chrome Developer Tools

Using Chrome Developer Tools: Network (2/6)

Part 3: ASP NET Web API - API Testing and Debugging with Fiddler

Getting Comfortable with Vue.js

The first thing I noticed about Vue.js is that the documentation is well-organized and easy to read. So at this point I have no misgivings about shouting, "read the guide!" Also, if you're not a JS tooling/development workflow guru, don't allow yourself to get bogged down in "Installation" section; jump straight to the "Introduction" section.

The second thing I noticed is that Vue.js doesn't try to be all things to all people. As its name implies, it is primarily concerned with the "view" portion of the MVVM pattern. It allows one to dabble in SPA without having to dive too deep into framework details. Given a decent understanding of things like JS objects and functions, you can churn out helpful micro-apps and utilities relatively quickly. As your needs adapt you can incrementally scale-up by adopting more advanced features. Vue.js bills itself as a “progressive framework” that is “incrementally adoptable”, and these attributes help drive its popularity.

Vue.js also integrates well. A simple Single Page Application can easily co-exist with server-side frameworks like WebForms, MVC, PHP, Rails or whatever. Also, it plays well with existing client-side libraries and frameworks; jQuery, Bootstrap, etc.

The Vue.js "full" build and the "runtime-only" build.

The full build includes the Vue.js compiler for compiling templates into render functions. The runtime-only build expects precompiled templates which unnecessarily complicate this entry-level stab at Vue.js. So, the full Vue.js build does simplify things but there can be noticeable performance hits associated with compiling templates on the fly, especially large and complex templates. Perhaps more importantly, the Vue.js compiler makes us of the “new Function()”, (aka the JavaScript Function() constructor), which can be vulnerable to injection attacks, (XSS and the like). In many environments this is a bad idea and/or not permitted. For example, the default CSP (content security policy) for Chrome extensions will block the new Function() constructor. So without explicitly relaxing the CSP, the full Vue.js won’t compile templates in a Chrome extension (i.e. your extension UI won't work). You can refer to following resources for more information: Chrome Extensions CSP and Vue.js builds and CSP environments.

The ToDo SPA

The entire ToDo UI is housed in 2 relatively small files.

todoSpaUi.html

This could just as easily have been a Razor view (.cshtml file) or a WebForm (.aspx file). I used an .html file to emphasize that the UI code, in its entirety, is client/browser-based. The layout consists of a main container DIV and 3 html template elements. Each template corresponds to a SPA page, or route. In this simple SPA there are 3 separate route interfaces:

1) List of Todos (for a given user),

2) Add Todo, and

3) Edit Todo.

HTML markup within a template tag is not rendered by the browser, it will remain inert until its associated route is activated. Each of the 3 templates corresponds to a Vue component and its route. Vue.js supports many different approaches to templating and routing.

All of this app’s JavaScript (except for some inline, demo-related, faux login code), gets loaded at the bottom, after the document is fully loaded.

todoSpaUi.js

All of the JavaScript is encapsulated in an IIFE (immediately-invoked function expression) that takes in the window object as its sole argument. This helps minimize variable clutter in the global scope, and also helps facilitate future tooling/scaling efforts; modularization, linting, bundling, etc.

The JavaScript is structured in 3 loosely defined sections that are labeled with comments.

1
2
3
4
5
6
7
8
9
10
// *** Section 1: define reusable local/private utility functions ***
.....
 
 
// *** Section 2: define List, Add, Edit component constructors ***
.....
 
 
// *** Section 3: define Routes and root Vue instance ***
.....

The first section defines a number of locally scoped (private) function expressions. These are reusable utility functions that are used by the List, Add and Edit components; primarily jQuery AJAX calls and a function to help manage CSS class binding.

The second section is where the three Vue components are defined, one for each of the three SPA routes; List Todo’s, Add ToDo and Edit Todo. The Vue.js .extend() method is used to create the 3 component constructors that are referenced in the router configuration. Note how the template elements’ id, (see todoSpaUi.html), is assigned to the template property of the .extend options.

1
2
3
4
5
// *** Section 2: define List, Add, Edit component constructors ***
   
 exports.TodoList = Vue.extend({
     template: '#todo-list',
     data: .....

The “#” tells Vue.js to use JavaScript’s querySelector() method to lookup the template and use it’s innerHTML as the template; associating the component with the <template>.

The final section of JavaScript is where everything is tied together. A new VueRouter instance is created and the 3 SPA routes are mapped to their respective components. Not to be confused with routing in ASP.Net MVC or Web API, client-side routing is required to mimic a multi-page, non-SPA application. The routing logic will update the DOM with the appropriate interface using hash-based routing. Essentially, anything after the # hash in the URL is considered a route. It works because changes in the URL, after the hash, do not trigger a page reload. Note that this is an overly simplistic take on client-side routing; for more details see the official vue-router documentation.

And finally a root Vue instance is created; it defines the target element to mount the SPA to and ties the Vue instance to the VueRouter instance and its defined route/component mapping. The entire 3rd section is below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// *** Section 3: define Routes and root Vue instance ***
 
// ---------------------------------------------------------------------
// Define routes (for vue-router/js, see https://router.vuejs.org/ for doco)
// note: named $TodoRouter for easy alphabetical reference in browser Dev tools
// The 'name' setting (named routes) is optional, but it can allow for more
//    readable navigation links, via router-link element or programmtic
//    $TodoRouter.push()
//
// ---------------------------------------------------------------------
exports.$TodoRouter = new VueRouter({
    routes: [
      { path: '/', component: TodoList },
      { path: '/todo-add', component: TodoAdd, name: 'todo-add'},
      { path: '/todo/:todo_id/edit', component: TodoEdit, name: 'todo-edit' }
    ]
});
 
 
// Define global navigation guard to verify valid user state prior to resolve
exports.$TodoRouter.beforeResolve(function (to, from, next) {
    var r = sessionStorage.getItem("userId");
    if ( !r || isNaN(parseFloat(r)) || !isFinite(r) )
        alert("Please select a demo user.");
    else next();
});
 
// Finally, define the root vue instance via Vue constructor
// note: named $TodoApp for easy alphabetical reference in browser Dev tools
exports.$TodoApp = new Vue({
    el: '#app',
    router: exports.$TodoRouter,
    template: '<router-view></router-view>'
});

Next Steps

If you enjoy learning about full-stack methodologies then a plunge into SPA will provide endless opportunity. This post is just a hint of what is the "new normal" in web application design. Time permitting, I hope to improve on this codebase by posting follow-ups...possible topics:

  • Conversion to Vue.js components (possibly single-file components), and associated  NPM workflows
  • Conversion of backend to ASP.NET Core
  • Nix jQuery mayhem and move to Axios and delve into the promise object
  • Explore adding offline caching to app. Also, to be more useful, each ToDo record should have an "Added By" field to discourage wise guy's from harassing coworkers with torrents of ToDo tasks!