Wrapping up this first look at the frontend application built with Vue.js, in this episode we make it work with the group management Web API we developed so far.

For the walk-through you can check out the next video, but if you prefer a quick read, skip to the written synthesis.

The playlist for the whole series is here.

Intro

In this episode we will finally connect the frontend and the backend, by making HTTP calls to the groups service. To do the HTTP calls we’ll use axios.

Service client interface

We’ll start by creating an interface to represent our groups service client. As usual in these frontend posts, this isn’t mandatory, but I tend to give into my C# habits 🙂

To start with, we create a new folder called data inside src. In the new folder we create another one called models, to keep the models that represent what we get (and send) from the service.

data\models\group-model.ts

export interface GroupModel {
    id: number;
    name: string;
    rowVersion: string;
}

GroupModel is the same as we have seen in the other application areas. Let’s get right into something new, the service client interface, GroupsEndpoint (this isn’t my best episode in terms of naming 😛).

data\groups-endpoint.ts

import { GroupModel } from './models/group-model';

export interface GroupsEndpoint {
    getAll(): Promise<GroupModel[]>;
    getById(id: number): Promise<GroupModel>;
    add(group: GroupModel): Promise<GroupModel>;
    update(group: GroupModel): Promise<GroupModel>;
    remove(id: number): Promise<void>;
}

If you remember the GroupsService we had in episode 12, this will be very familiar. It’s basically the same, just adapted to TypeScript. All methods return Promises, as they perform IO operations.

Service client implementation

Before implementing the service client, we need to add axios to the project. As I’m using npm, I did it by running npm install --save axios.

Now we can implement the interface we defined.

data\groups-service.ts

import { GroupsEndpoint } from './groups-endpoint';
import axios from 'axios';
import { GroupModel } from './models/group-model';

// TODO: handle eventual errors

export class GroupsService implements GroupsEndpoint {
    private readonly baseUrl: string = '/api/groups';

    public async getAll(): Promise<GroupModel[]> {
        const response = await axios.get(this.baseUrl);
        return response.data;
    }

    public async getById(id: number): Promise<GroupModel> {
        const response = await axios.get(`${this.baseUrl}/${id}`);
        return response.data;
    }

    public async add(group: GroupModel): Promise<GroupModel> {
        const response = await axios.post(this.baseUrl, group);
        return response.data;
    }

    public async update(group: GroupModel): Promise<GroupModel> {
        const response = await axios.put(`${this.baseUrl}/${group.id}`, group);
        return response.data;
    }

    public async remove(id: number): Promise<void> {
        const response = await axios.delete(`${this.baseUrl}/${id}`);
    }
}

There’s not really a lot to explain, but let’s take a look.

For starters, we’re defining a base url for the requests. Note that it starts with /api, but that is not part of the route we defined in our service. We’ll see why in a bit, regarding the development server proxy.

Regarding the method implementations, they’re all async and make use of axios to make the requests, using the various HTTP methods we defined when creating the web API.

We don’t have any error handling logic yet, we should implement it eventually, but for now it’s not very important.

Using the service client in Vuex actions

Now that we implemented the service client, we need to use it in the actions. We could just import the GroupsService and use it in the actions implementation, but to make it easier to test the actions if we feel like it sometime in the future, I created a factory that receives a GroupsEndpoint instance.

store/modules/groups/actions.ts

import { ActionTree } from 'vuex';
import { GroupsState, Group } from './state';
import { RootState } from '@/store/state';
import { GroupsEndpoint } from '@/data/groups/groups-endpoint';

export const types = {
    LOAD_GROUPS: 'groups/loadGroups',
    ADD_GROUP: 'groups/add',
    UPDATE_GROUP: 'groups/update',
    REMOVE_GROUP: 'groups/remove'
};

export const makeActions = (groupsEndpoint: GroupsEndpoint): ActionTree<GroupsState, RootState> => {
    return {
        async loadGroups({ commit }): Promise<void> {
            const groups = await groupsEndpoint.getAll();
            commit('setGroups', groups);
        },
        async add({ commit }, group: Group): Promise<void> {
            const addedGroup = await groupsEndpoint.add(group);
            commit('add', addedGroup);
        },
        async update({ commit }, group: Group): Promise<void> {
            const updatedGroup = await groupsEndpoint.update(group);
            commit('update', updatedGroup);
        },
        async remove({ commit }, groupId: number): Promise<void> {
            await groupsEndpoint.remove(groupId);
            commit('remove', groupId);
        }
    };
};

Taking a look, the actions logic is almost the same, but now we make API requests before passing the values to the mutations that update the state. The actions also became async, as they call the also asynchronous service methods.

Notice that in the add and update actions we pass the response from the service to the mutations, not the action payload. This is because there are some properties that are initialized or updated server side, and we need to keep that information.

Configure development server proxy

If you recall from when we were developing the API, we didn’t enable CORS, as we’ll be serving the frontend and the API from the same domain. When it’s all working together on a server, we’ll use a reverse proxy to map requests where the route starts with /api to the backend services, and the rest will go to the frontend.

To achieve a similar goal during development, we can configure Vue’s development server to proxy the API requests.

In the root of the project, so one step above src, we create a new file called vue.config.js where we can setup, among other things, the dev server to proxy the API requests.

vue.config.js

module.exports = {
    devServer: {
      proxy: {
        '^/api': {
          target: 'http://localhost:5000',
          ws: true,
          changeOrigin: true,
          pathRewrite: {
            '^/api': ''
          },
        }
      }
    }
  }

In this configuration file, we’re saying that all requests that start with /api go to http://localhost:5000, where we have hosted the API in dev so far. There’s also some more options, but the most relevant one is the path rewrite, that removes the /api from the route, as it is not expected in the groups service API. For the couple of booleans, ws means that we also proxy web socket requests and changeOrigin changes the origin of the host header to the target url.

You can see more about these configurations here and here.

Outro

With this post we wrap up our first adventure in the frontend in this series. In these 3 episodes we built a simple Vue.js application, which allows for a very simple management of groups. We made use of components, a store and made it work nicely with the web API.

In the next post we’ll be back to C#, but we’ll of course get back to the frontend eventually.

Links in the post:

The source code for this post is here.

Feedback always appreciated!

Thanks for stopping by, cyaz!