Web Development

Debugging VueJS + TypeScript with VS Code - Part 2

In the past I have written about how to setup VS Code to debug a VueJS + TypeScript project. Since writing that article it's a method I've continued to use and it works well. It's quick to spin up and quite reliably allows you to place breakpoints in code.

However one aspect of it that I don't like is it's not so much "Run and Debug" from VSCode, it's more just the debug part, as to use it you must first go to a terminal and run your VueJS application.

There's two problems with this:

1. Most of the time you will just run the application not debugging (because why debug when you don't have an issue), therefore when you need it, you have to redo what you just did that caused an error. Instinctively there is then a desire to guess at what was wrong rather than going to the extra effort of debugging (frequently when you do this your guess is wrong and you end up spending more time guessing at what was wrong than using the tool that will tell you).

2. As the debugger generally isn't running, exceptions never get flagged up, and unless you have the browser console open (and check it), you can remain oblivious to something going wrong in your app.

There is a solution though, and it's quite simple!

Using VS Code to launch via npm

First follow through my previous guide on debugging a VueJS and TypeScript application.

Next, in your launch.config file add a new configuration with the definition below. This will run the command in a debug terminal, effectively doing the same thing as you typing npm run serve.

{
    "command": "npm run serve",
    "name": "Run npm serve",
    "request": "launch",
    "type": "node-terminal"
  },

To get both our new and old configuration to run you can add a compound definition, that does both at the same time.

Here's mine.

"compounds": [
  {
    "name": "Run and Debug",
    "configurations": ["Run npm serve", "vuejs: edge"]
  }
],

My complete file now looks like this. Note you don't need configurations for edge and chrome, just use the one for the browser you use.

{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"compounds": [
  {
    "name": "Run and Debug",
    "configurations": ["Run npm serve", "vuejs: edge"]
  }
],
"configurations": [
  {
    "command": "npm run serve",
    "name": "Run npm serve",
    "request": "launch",
    "type": "node-terminal"
  },
  {
    "type": "pwa-msedge",
    "request": "launch",
    "name": "vuejs: edge",
    "url": "http://localhost:8080",
    "webRoot": "${workspaceFolder}",
    "breakOnLoad": true,
    "sourceMapPathOverrides": {
      "webpack:///./*": "${webRoot}/*"
    },
    "skipFiles": ["${workspaceFolder}/node_modules/**/*"]
  },
  {
    "type": "chrome",
    "request": "launch",
    "name": "vuejs: chrome",
    "url": "http://localhost:8080",
    "webRoot": "${workspaceFolder}",
    "breakOnLoad": true,
    "sourceMapPathOverrides": {
      "webpack:///./*": "${webRoot}/*"
    },
    "skipFiles": ["${workspaceFolder}/node_modules/**/*"]
  }
]
}

Now whenever you want to run the application, just run the compound Run and Debug and your VueJS app will start up and launch in your browser.

Which JavaScript for loop should I use?

When given a choice of 4 seemingly different ways to do the same thing, I've always been the person that wants to know what the difference is? To often we can blindly write code the way the person before us did and not understand the reason to do it a certain way, but what if the scenario has changed. When multiple options exist there's usually a reason why. It can be a language style improvement to make things easier to write, but more often than not there's a fundamental difference in what it actually does.

So, for loops, four seems like a lot, gotta be some differences right?

for

This is likely to be the first type of for loop you encounter when learning to program. It's a very manual type of loop where you create a initial expression, a condition expression to keep running the loop and an increment expression.

for (let i = 0; i < 5; i++) {
  console.log(i);
}

// Result:
// 0
// 1
// 2
// 3
// 4

The big downside of the for loop is it doesn't actually loop through a collection. e.g. If you had an array and wanted to loop through each of it's objects you could do this as follows:

const arr = ['a', 'b', 'c'];

for (let i = 0; i < arr.length; ++i) {
  console.log(arr[i]);
}

In effect the loop is incrementing a number and you then use that number to access the position of the array. The result is you have more code to write that isn't serving any real purpose.

However as this is a much more manual loop process you have far more control over the loop. e.g.

  • You could increment by a different number.
  • You could go backwards.
  • The condition might not be linked to the length of an array.
  • You might not even be looping through an array.

Pros

  • Potentially faster in performance.
  • Break statement can be used to come out of the loop.
  • It works with the await keyword.
  • Not just for looping through arrays.

Cons

  • Not as readable as others.
  • You have more code to write.

for...in

You probably don't want this one. Here's an example:

const arr = ['a', 'b', 'c'];

for (const i in arr) {
  console.log(arr[i]);
}

// Result:
// "a"
// "b"
// "c"

Although we're now specifically looping through a collection like an array and don't need to do all of that i < array.length stuff, what we're given isn't the object in the array but a property name to access it on the object.

Here's another example:

const arr = ['a', 'b', 'c'];
arr.foo = 'John'

for (const i in arr) {
  console.log(arr[i]);
}

// Result:
// "a"
// "b"
// "c"
// "John"

That looks a bit weird! The for...in loop doesn't just loop through an array, it loops through an objects enumerable properties, which could be the items in an array, but it could be other stuff too.

For this reason, unless your doing something quite niche its probably not the loop you are looking for and is likely to cause you an issue which your objects has one more property than you were expecting.

Pros

  • Can loop through all the enumerable properties on an object.

Cons

  • Looping through all the properties of an object isn't likely what you want.

forEach

As the name implies, a for each loop will iterate through each element in an array. Here's an example:

const arr = ['a', 'b', 'c'];

arr.forEach((i) => {
  console.log(i);
})

// Result:
// "a"
// "b"
// "c"

The forEach function takes an anonymous function as a parameter and will then call that function for each object in the array, passing in the object from the array.

This offers a big improvement over the initial for loop as we have a lot less code to write and we're actually given the object rather than a variable to go an find it.

However there are some downsides due to it effectively being a function call passing an anonymous function to execute.

Firstly you can't stop the forEach part way through. With the others you can use the break keyword to stop the iteration.

Secondly there's added confusion around the scope of what this is. Assuming your function is in a class, unless you use the arrow syntax (as in the example) you won't be able to access any of the other functions in your class as passing a regular function would change the scope.

// This works
class foo
{
myFunction() {
  const arr = ['a', 'b', 'c'];

  arr.forEach((i) => {
    this.outputValue(i)
  })
}

outputValue(x) {
  console.log(x);
}
}

// This Doesn't
class foo
{
myFunction() {
  const arr = ['a', 'b', 'c'];

  arr.forEach(function(i) {
    this.outputValue(i)
  })
}

outputValue(x) {
  console.log(x);
}
}

You also can't use an await within the foreach loop. e.g.

async function run() {
const arr = ['a', 'b', 'c'];
arr.forEach(el => {
  // SyntaxError
  await new Promise(resolve => setTimeout(resolve, 1000));
  console.log(el);
});
}

Pros

  • Much shorter to write and easier to read.
  • Iterator provides the object from the array.

Cons

  • Easy to mess up context of this.
  • No way to break the loop.
  • Async/Await doesn't work on functions within the loop.
  • Can only loop through arrays.
  • It's performance is slower (nice article here comparing the performance differences), not to such an extent it would matter on a small object, but large objects could start to take a hit.

for...of

When looping through an array, the for...of loop combines the best of all the other loops. It has the same simple syntax of the for...in loop but instead of enumerating over enumerable properties, it loops through the iterable objects such as the items in an array.

Like the forEach loop you are provided with the object in the array rather than it's index or property name.

const arr = ['a', 'b', 'c'];

for (const i of arr) {
  console.log(i);
}

// Result:
// "a"
// "b"
// "c"

You can also break the loop and access properties outside of the loop.

const stopValue = 'b'
const arr = ['a', 'b', 'c'];

for (const i of arr) {
  console.log(i);
  if (i == stopValue)
      break;
}

// Result:
// "a"
// "b"

Pros

  • Lowest amount of extra code to write.
  • Iterator provides the object.
  • Doesn't use an anonymous function so scope doesn't change.
  • Loop can be stopped as needed.
  • Async still works.
  • Works with more than just arrays.

Cons

  • Have to be looping over the iterable items in an object.
  • Can't easily access the index value.

Conclusion

If you want to iterate through something like an array, for...of would be my recommendation to use. A for...in loop is likely to be less relevant but has its place, and for all other loops which don't relate to the objects in an array a good old for loop still has it's place.

Dependency Injection with NextJS and TypeScript

Coming from a backend world, one thing that stands out when you start writing code in JavaScript, be it React, NextJS, Vue etc, is there's no great focus on code structure. Things like TypeScript bring back the concept of type checking that you'd be used to with a compiled language, and NextJS will give anyone familiar with ASP.NET MVC an alternative pattern for how a website should be constructed. But you can get the whole way through the NextJS tutorial without concepts like single responsibility, inversion of control or CQRS being mentioned once.

Now if your building a small site, maybe you can get away with not knowing or imlementing these things, but if you want to make code that's scalable or maintainable, it doesn't matter if you write in JavaScript or C# the same issues will exist but fortunately the same solutions do too.

Code Smells

Lets take a look at one of the functions on this site which gets the latest blog posts on the homepage.

import gql from &#39;graphql-tag&#39;;
import { Client, ApolClient } from &#39;../prismicHelpers&#39;

// Models
import { FeaturedPost } from &quot;../../Models/FeaturedPost&quot;

const latestPostsQuery = gql`
query latestPosts($category: String) {
allPosts (where : {category: $category}, first : 10, sortBy: post_date_DESC){
  edges {
    node {
      category {
        ... on Categories {
          name
        }          
      },
      title,
      image,
      post_date
      _meta {
        uid
      }
    }
  }
}
}
`;

export const getLatestPosts = async (category?: String) : Promise&lt;FeaturedPost[]&gt; =&gt; {
const queryOptions = {
  query: latestPostsQuery,
  variables: { category },
};

return new Promise((resolve, reject) =&gt; { ApolClient.query(queryOptions).then(response =&gt; {
  var posts: Array&lt;FeaturedPost&gt; = [];
  response.data.allPosts.edges.map((edge: { node: { title: { text: any; }[]; category: any; image: any; post_date: Date; _meta: { uid: any; }; }; }, key: any) =&gt; {
    posts.push({
      type: &quot;post&quot;,
      title: edge.node.title[0].text,
      image: edge.node.image,
      uid: edge.node._meta.uid,
      category: edge.node.category?.name,
      postDate: edge.node.post_date
    })
  })
  resolve( posts);
}).catch(error =&gt; {
  reject(error);
});
});
};

This bit of code queries Prismic's GraphQL API to get the latest 10 articles for a category and then maps the result onto an internal FeaturedPost model.

Some good things about it:

  • The Prismic logic is abstracted away from the rest of the application by mapping the results to a model.
  • It follows single responsibility by doing just 1 job.
  • All config for the API (e.g. URI) are separated into a common function rather than being duplicated.

However even though I've seen countless JavaScript examples which don't even do these good things, if this were C# and I was reviewing a pull request, I'd say it smelt and needed changing.

Here's some bad things about it:

  • It's taking a dependency on Apollo Client, which while is a great client for doing GraphQL queries, at the rate JS frameworks come and go we can't say we'll never replace it and in a large application that has countless queries that would be a lot of code to update.
  • There's also no way anything can call this without also taking a dependency on it. That means I now have a hierarchy of at least 3 functions with each one dependent on the next. If I ever wanted to add unit tests to my project I'd have a big problem.

Dependency Injection using TSyringe

These issues can be solved using Dependency Injection. There's quite a few around but the one I've chosen is TSyringe (https://github.com/microsoft/tsyringe).

It's built by Microsoft, which gives me some confidence in the amount of QA that will have gone into it, but more importantly it works the way I expect a DI framework to work. There's a good chance this could be because I'm used to working in the Microsoft stack and it therefore has natural similarities to DI frameworks in their own languages.

How to add TSyringe to a NextJS project

There's nothing specifically NextJS about TSyringe, you could use it with any TypeScript/JavaScript project, it just happens that that's what my blog is built using. However it also had a few issues getting it to work and I couldn't any articles around which explained how.

To set it up...

First install the package tsyring and reflect-metadata using npm.

npm install --save tsyringe reflect-metadata

Modify your tsconfig.json file to include the following settings, which will allow the use of decorators in TypeScript.

{
"compilerOptions": {
  "experimentalDecorators": true,
  "emitDecoratorMetadata": true
}
}

Now there's a few more packages your going to need. As of NextJS 12, Babel is no longer used and has been replaced with SWC to provide faster compile times. However TSyringe and most other DI frameworks use decorators to function (hence the tsconfig.js setting to turn them on), but the setting for SWC to allow this set to false in NextJS and there's no way for you to provide your own config. Fortunately Babel is still supported and you can customise it. Hopefully a future version of NextJS will address this.

Install the following packages as dependencies.

npm install -D @babel/core @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators babel-plugin-transform-typescript-metadata

Add a .bablerc file to your project with the following settings.

{
"presets": ["next/babel"],
"plugins": [
  "babel-plugin-transform-typescript-metadata",
  ["@babel/plugin-proposal-decorators", { "legacy": true }],
  ["@babel/plugin-proposal-class-properties", { "loose": true }]
]
}

Finally add this import to your _app.tsx file.

import &quot;reflect-metadata&quot;;

Using TSyringe

We're now ready to convert the code I had before into something more maintainable.

First I'm going to create a GraphQL Client interface which all my queries will use when they want to call a graph API. This has one function called query, which my functions will pass the graph syntax too along with a variables object.

import { DocumentNode } from &quot;graphql&quot;;

export interface graphClient {
query(query: DocumentNode, variables?: {}): any;
}

With this interface I can now turn my getLatestPosts function into a class with a constructor that will take in the instance of graphClient.

import { inject, injectable } from &quot;tsyringe&quot;;
import { iGetLatestPosts } from &quot;./iGetLatestPosts&quot;;
import gql from &quot;graphql-tag&quot;;
import { graphClient } from &quot;../iGraphQl&quot;;

// Models
import { FeaturedPost } from &quot;../../Models/FeaturedPost&quot;;

@injectable()
export class getLatestPosts implements iGetLatestPosts {
graphClient: graphClient;

constructor(@inject(&quot;graphClient&quot;) private graphClientParam: graphClient) {
  this.graphClient = graphClientParam;
}

private latestPostsQuery = gql`
  query latestPosts($category: String) {
    allPosts(
      where: { category: $category }
      first: 10
      sortBy: post_date_DESC
    ) {
      edges {
        node {
          category {
            ... on Categories {
              name
            }
          }
          title
          image
          post_date
          _meta {
            uid
          }
        }
      }
    }
  }
`;

public getLatestPosts = async (
  category?: String
): Promise&lt;FeaturedPost[]&gt; =&gt; {
  return new Promise((resolve, reject) =&gt; {
    this.graphClient
      .query(this.latestPostsQuery, { category })
      .then((response: any) =&gt; {
        var posts: Array&lt;FeaturedPost&gt; = [];
        response.data.allPosts.edges.map(
          (
            edge: {
              node: {
                title: { text: any }[];
                category: any;
                image: any;
                post_date: Date;
                _meta: { uid: any };
              };
            },
            key: any
          ) =&gt; {
            posts.push({
              type: &quot;post&quot;,
              title: edge.node.title[0].text,
              image: edge.node.image,
              uid: edge.node._meta.uid,
              category: edge.node.category?.name,
              postDate: edge.node.post_date,
            });
          }
        );
        resolve(posts);
      })
      .catch((error: any) =&gt; {
        reject(error);
      });
  });
};
}

Some things to note in this new class.

  • It's also now implementing an interface so that it can instantiated using DI.
  • The @injectable decorator allows TSyringe to inject the dependencies at runtime.
  • The constructor is decorating a parameter with @inject("graphClient") which means that parameter will be injected at runtime with whatever is configured against the graphClient token.
  • There are imports from tsyringe.
  • There are no references to the implementation of graphClient.
  • My function now has zero dependencies on Apollo Client and doesn't even know it's being used.

My implementation of graphClient looks like this.

import { autoInjectable } from &quot;tsyringe&quot;;
import { DocumentNode } from &quot;apollo-link&quot;;
import ApolloClient from &quot;apollo-client&quot;;
import { NormalizedCacheObject } from &quot;apollo-cache-inmemory&quot;;
import { graphClient } from &quot;./iGraphQl&quot;;

@autoInjectable()
export class apolloGraphClient implements graphClient {
apolloClient: ApolloClient&lt;NormalizedCacheObject&gt;;

constructor(apolloClient: ApolloClient&lt;NormalizedCacheObject&gt;) {
  this.apolloClient = apolloClient;
}

public query = async (query: DocumentNode, variables?: {}): Promise&lt;any&gt; =&gt; {
  const queryOptions = {
    query: query,
    variables: variables,
  };

  return new Promise((resolve, reject) =&gt; {
    this.apolloClient
      .query(queryOptions)
      .then((response: any) =&gt; {
        resolve(response);
      })
      .catch((error: any) =&gt; {
        reject(error);
      });
  });
};
}

Essentially all this function does is pass the parameters to the query function to an Apollo Client's query function. The Apollo Client itself is also being injected!

You may have expected this file to also instantiate the Apollo Client, and it could have, but I've gone to the extreme and the single purpose of this file is to act as a bridge between the business logic queries and what client is being used, so for that reason its injected.

You'll also notice that this time I'm decorating the class with @autoInjectable() and there is no decorator on the constructor parameter. More on this in a bit.

The homepage page for this site now looks like this.

import Head from "next/head";

import React, { useState } from "react";
import { container } from "tsyringe";
import { GetStaticProps } from "next";
import Layout from "../layouts/layout";
import { FeaturedRow1, FeaturedRow1Model } from "../components/featured-row-1";
import SectionHeading from "../components/section-heading";
import { iGetLatestPosts } from "../utils/queries/iGetLatestPosts";

export default function Home({
latestPosts,
webDevelopmentPosts,
sitecorePosts,
devOpsPosts,
}: {
latestPosts: FeaturedRow1Model;
webDevelopmentPosts: FeaturedRow1Model;
sitecorePosts: FeaturedRow1Model;
devOpsPosts: FeaturedRow1Model;
}) {
return (
  <Layout>
    <Head>
      <title>Hi My Name Is Tim</title>
    </Head>
    <SectionHeading heading="Latest Posts" link="blog"></SectionHeading>
    <FeaturedRow1 posts={latestPosts}></FeaturedRow1>
    <SectionHeading heading="Web Development" link="web-development"></SectionHeading>
    <FeaturedRow1 posts={webDevelopmentPosts}></FeaturedRow1>
    <SectionHeading heading="Sitecore" link="sitecore"></SectionHeading>
    <FeaturedRow1 posts={sitecorePosts}></FeaturedRow1>
    <SectionHeading heading="Devops" link="devops"></SectionHeading>
    <FeaturedRow1 posts={devOpsPosts}></FeaturedRow1>
  </Layout>
);
}

export const getStaticProps: GetStaticProps = async () => {
// Resolve interface for iGetLatestPosts
const instance = container.resolve<iGetLatestPosts>("iGetLatestPosts");

const latestPosts = await instance.getLatestPosts();
const webDevelopmentPosts = await instance.getLatestPosts("X8kFhxIAACcAn9oY");
const devOpsPosts = await instance.getLatestPosts("X8kFlRIAACkAn9pa");
const sitecorePosts = await instance.getLatestPosts("X8kFeBIAACkAn9nV");

return {
  props: {
    latestPosts: latestPosts,
    devOpsPosts: devOpsPosts,
    sitecorePosts: sitecorePosts,
    webDevelopmentPosts: webDevelopmentPosts,
  },
};
};

Pages in NextJS TypeScript don't use classes, so we can't do constructor injection to get the instance of our getLatestPosts query class. Instead we are using container.resolve<iGetLatestPosts>("iGetLatestPosts") to get the instance to token name iGetLatestPosts from the DI container.

Lastly in the _app.tsx file I am registering the classes on the container. I'm only including the relevant bit of the file here.

container.registerInstance(
ApolloClient,
new ApolloClient({
  link: PrismicLink({
    uri: prismicGraphUri,
    repositoryName: prismicRepoName,
  }),
  cache: new InMemoryCache({ fragmentMatcher }),
})
);

container.register(&quot;graphClient&quot;, apolloGraphClient);
container.register(&quot;iGetLatestPosts&quot;, getLatestPosts);

For the Apollo Client I am using register instance to register a specific instance and creating it at the same time. Notice the first parameter is the class name.

For the graph client and getLatestPosts query I am using the register method and rather than creating the instance of my implementation, just passing the class as the second parameter. The framework will handle creating an instance of them for me.

Notice the first parameter for the second two are strings rather than the actual interfaces. These are token names that the container will use to reference the instance value. With the Apollo Client, the framework will figure out the token name when it adds it to the container, but it can't do the same for an interface (if you try you will get an interface cannot be used as a type error) so you have to provide the token name instead. This is also the reason why the graph client implementation didn't need to use a string to inject the class instance in the constructor, whereas the other places did.

Personally I feel this is a weakness in the framework as it also means there is no type checking either when registering or resolving from the container.

Summary

We have seen that using JavaScript doesn't prevent us from using the same concepts as we would with any other more traditional backend language.

We've also seen how a NextJS application while slightly more awkward, can still be set up to use dependency injections.

Finally we've had a look at how to actually configure some code to have complete separation between logic within a solution.