Sunday, 30 January 2022

local mock data for SPA web service

Whenever I build SPA's in vue I use a poor mans IOC pattern, which in principle accomplishes leveraging a mock version of my service that instead of fetching data from a web api leverages local json files, however in practice it has very little to do with Inversion of control, though it could adhere to IOC principles I find my pragmatic approach less hassle. 

I start with creating env files: moq, dev, prod

.env.dev

VUE_APP_ENV=dev
VUE_APP_HEADER_COLOR=#9F1F36

.env.moq

VUE_APP_ENV=moq
VUE_APP_HEADER_COLOR=#FFD200

.env.prod

VUE_APP_ENV=prod
VUE_APP_HEADER_COLOR=

as you can see I also like to add a a color code so that i can easily tell which environment I am currently working on.

next I create a services folder where I keep all my logic that communicates outside my app and a mock_data folder where I keep json files that will represent the responses that I would get from my backend api.


so let's create a simple json file for our mock data, here i have created a openings.json file that contains three chess openings

[
  {
    "description""Sacrifice a pawn for a head start",
    "id""55d86e6f-b043-4544-ae6b-b88ee4ee134b",
    "name""King's gambit",
    "tags": ["kingsPawn""gambit"]
  },
  {
    "description""trade a pawn for central occupation",
    "id""52366eb7-fcad-4b58-9ad3-f73c1585c16a",
    "name""Queens's gambit",
    "tags": ["queensPawn""gambit"]
  },
  {
    "description""Win by oversight",
    "id""e5f7f895-073c-4a24-9df0-d9d65b6ad46a",
    "name""Scholar's mate",
    "tags": ["kingsPawn""dubious"]
  }
]

this would be the payload i would expect from some web api get openings, but for now we will just use local mock data.

**in your tsconfig.json and make sure to add "resolveJsonModule": true to the compilerOptions

next create a openingsService.ts file in which you should define an interface and two services a private mock service and a default prod/dev service. however rather than creating a context and pass that into the service service what i do is use the object.assign function to override my dev/prod services with a mock counter part.

import { IOpening } from "@/models/opening";

export interface IOpeningService {
  getOpeningsAsync: () => Promise<IOpening[]>
}

class MoqOpeningService implements IOpeningService {
  getOpeningsAsync: () => Promise<IOpening[]> = 
    async () => {
      return require<IOpening[]>("@/mock_data/openings.json");
    };
}

export default class OpeningService implements IOpeningService {
  constructor() {
    if(process.env.VUE_APP_ENV === "moq")
      Object.assign(thisnew MoqOpeningService());
  }

  getOpeningsAsync: () => Promise<IOpening[]> = 
    async () => {
      throw new Error("getOpeningsAsync not implemented");
    }
}

notice that in the constructor of the default class there is a check of which environment is running and if it is the "moq" version a new instance of the MoqOpeningService is assigned to the current opening service overriding everything defined in the interface. 

now that is fine and dandy however we have one final caveat, all of your mock data is going to be packaged in your dist folder when you build your project.

To exclude mock files from your build 

firstly you have to add a reference to the webpack npm package 

i used the 4.45.0 version because I was getting exceptions with the latest major 5+ version

npm install webpack@^4.45.0 --save-dev

with webpack install if you haven't already created a vue.config.js file go a head and do that at the root of your project, refer to the project structure screen capture at the start of this post.

//vue.config.js

// eslint-disable-next-line @typescript-eslint/no-var-requires
const webpack = require('webpack')

module.exports = {  
  configureWebpack: {
    plugins: process.env.VUE_APP_ENV == "prod" ? [
      new webpack.IgnorePlugin({
        resourceRegExp: /mock_data\/.*\.json$/,
        contextRegExp: /services$/
      })
    ] : []
  }
}

IgnorePlugin | webpack

now when you build your project your mock data will not be included in your dist folder.