Minimal Vue and beyond

Vue gives us the ability to include it in a a minimal way by adding a simple script dependency (or installing it into your project’s node_modules using yarn add vue). Vue also comes with a CLI (vue-cli) for building application from a scaffolding application.

Let’s start at the simplest level and create a basic HTML page (no bundling, no built-in web server, nothing) and start writing Vue code. Create an index.html file

<!DOCTYPE html>
<head>
  <!-- development version -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <!-- production version
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  -->
</head>
<html>
  <body>
  </body>
</html>

The development version of Vue comes with console warnings etc. whereas the production version is optimized.

Let’s now place the following into the body tag

<div id="app">
  <button v-on:click="clicked">{{ message }}</button>    
  <div>{{ count }}</div>
</div>

<script>
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World',
    count: 0
  },
  methods: {
    clicked: function(event) {
      this.count++
    }
  }
})
</script>

What we’ve done is create the root (app) div, which corresponds to the el within the Vue options. Hence that Vue object is bound to the app element. It includes data in the form custom data, in this case a message (the usual Hello World example) but also a counter in the form of the count variable. The Vue options also include methods, in this case one labelled clicked is used in the button within the HTML.

Notice this use of this. to access the count in the method.

Now, in the HTML we use v- prefixed attributes to bind the elements to our Vue object (data and methods). So the button is bound to the v-on:click which in turn is assigned the method clicked as created in the Vue options.

Notice also the usage of the {{ }} (Mustache syntax) to allow us to inject (in this case) Vue data into the HTML.

As stated, there’s no web server or the likes, so we can now just drop this index.html into our chosen webserver and serve up Vue enabled web pages.

Taking it to the next step

In the previous example we use the Vue script from a CDN, but we can also run

yarn add vue

To include vue within the node_nodules of an application/website. Obviously this would be the route taken for anything more than the trivial example (above).

We’re not going to do anything more with this simple approach for building a Vue applications, however it does demonstrate that you can build a Vue application from scratch, choosing our preferred bundler etc. ourselves if required.

vue-cli

Let’s turn our attention now to creating a Vue application via the vue-cli tool.

So we’ve seen a minimal Vue page and we’ve seen we can bring Vue into our application in a way that allows us to pick and choose our technologies. With vue-cli we instead generate an application with all the “standard” pieces (i.e. bundler, webserver etc.).

Start by installing the vue-cli using

yarn global add @vue/cli

To check all went well, just run

vue --version

To create a project we simply run

vue create my-project

Obviously replacing my-project with your project name.

You will now be prompted to answer a couple of questions, for example

  • Please pick a preset
  • Pick the package manager to use when installing dependencies

See https://cli.vuejs.org/guide/creating-a-project.html#vue-create for a far more complete look at this stage.

If you simply choose the defaults you get bable, eslint and yarn configurations which is not actually what I wanted for this project.

So for the first prompt (Please pick a preset), I selected Manually select features and selected TypeScript, Router, Linter and Unit Testing, I then end up with further prompts asking whether I want class style components etc.

You’ll get the idea, vue-cli gives you default options all along the way.

Note: You can have vue-cli save your presets within ~/.vuerc or on Windows %USERPROFILE%/.vuerc, the option to save the presets is at the end of the available options for configuring your project.

If you prefer a UI to configure things with, then vue also comes with vue ui which will run a webapp (by default on port 8000). I’ll leave the reader to investigate the options themselves.

Once you’re happy with your configurations and you’ve generated your application, cd into your new project folder (cd my-project in case) and then run

yarn serve

This will start-up a server (by default on port 8080).

If we take a look at the code generated, you’ll notice we’ve moved far beyond the minimal use of Vue. I won’t cover too much here as really the topics around the generated code requires a post of their own, but suffice to say I’ve added a router (via the configuration selector). Vue creates main.ts which basically starts everything and mounts #app, which is within the new file type App.vue. The .vue files, I suppose are a little analogous to React’s JSX files in that they contain code and HTML, although obviously somewhat differently to JSX.

I tend to think of React as code with HTML/tags embedded in it (or returned from it) whereas Vue is more like markup with code embedded within it (similar to JSP, ASP etc. in that respect).

So just quickly looking at the generated Home.vue file (as stated, we’ll cover these more in another post). We have the following

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue';

export default {
  name: 'home',
  components: {
    HelloWorld,
  },
};
</script>

This is pretty easy to understand, like ASP.NET of old, we’re creating templates for our HTML which may also contain Vue code (as per our original minimal index.html). So the template can be thought of as our HTML component, then the script contains the code associated with that template (ofcourse you might simply have a template if no code is required). In this script we’re really just hosting the HelloWorld.vue component, passing props to it in the form of the msg=”Welcome to Your Vue.js App”.

Note: There are different ways to create components, we’ll look at in other posts, for now we’ll simply assume the above is the usual layout of our Vue components.

Vue with Visual Code

I tend to use Visual Code for most of my web development, for Vue it’s worth installing the Vetur plugin which gives Vue tooling for VS Code. This includes syntax highlighting, auto-complete, linting etc.

Note: The vue-cli also included a script to run linting across your code if you want to run that in a shell.

Redux actions and reducers

We tend to split our redux code into two “sections”, an action function and a reducer function which usually uses a switch to then return state based upon the supplied action.

Actions

An action can be thought of (within Redux) as a message or something that happens (hence an action). We might write an action creator as a function, like the following

export const onLoad = (name: string) => {
  return {
    type: ActionType.Load,
    payload: name
  };
};

this returns the action which has a shape of

{ type, payload } 

The type is required (as this is what we switch in the reducer) but the payload might not be, depending upon the action.

Alternatively we might dispatch an action using the redux store, for example

store.dispatch({ type: ActionType.Load, payload: name });

The key thing when creating actions is that they should not change anything but should simply state that some action should be taken and pass any data required within the reducer.

Note: the payload can be of more complex types, usually the type is simply a string representing the name of the action.

Reducers

Reducers responds to our action messages. The usual way to write a reducer is to have a function like the following

export default (state: any = initialState, action): any => {
  switch (action.type) {
    case ActionType.Load:
      return {
        ...state,
        queryName: action.payload
      };
    // ...
  }
  return state;
}

So we simply switch, based upon the action type (hence we’d usually have the action type string as a const, which must be unique across all your application reducers, hence a style such as the following is not unusual, where we prefix the action type value with some form of namespace followed by the action name

export enum ActionType {
   Load = "myapp/load",
   // ...
}

We can clone and add different values to the state using either the spread operator, i.e.

{ ...state, queryName: action.payload }

or via

Object.assign({}, state, { queryName: action.payload }); 

Rules for reducers

  1. Must always return the state even if no changes are made an even if the state is null but must never return undefined.
  2. Treat state as immutable, hence do not change state but simply return new state (which might be cloned and with changes from the previous state).
  3. A reducer should process every action even if it just simply returns the original state.

How to target the browsers we want to support

When developing a public facing web application we will usually want to try to be compatible with as large an audience and hence as large a selection of browsers (and their versions) as possible. Even within a private/intranet web site we’ll want to set some compatibility level to work with.

To that end, a useful addition to eslint is the eslint-plugin-compat plugin eslint-plugin-compat plugin.

yarn add eslint-plugin-compat -D

As per the documentation we should add the following to the .eslintrc.json file

{
  "extends": ["plugin:compat/recommended"],
  "env": {
    "browser": true
  },
  // ...
}

and within the package.json file we add the browserslist array, the following is an example of a possible setup

{
  "browserslist": [
    "> 0.2%", 
     "not dead",
    "not ie <= 11",
    "not op_mini all"  
  ]
}

The values within browserslist allow us to create a query that can reduce/increase the number and types or browsers supported as we wish.

For example the above suggests we want to support all browsers with more than 0.2% global usage statistics, where the data for this is taken from browserl.ist. This site also allows us to type these browserslist values into the edit box (removing quotes and new lines), i.e.

> 0.2%, last 2 versions, not dead, not op_mini all

Clicking Show Browsers will list the browsers that fall within the query range.

We can also execute the following command from our shell/terminal

npx browserslist "> 0.2%, last 2 versions, not dead, not op_mini all"

and this will also show you the browser that match that query also.

React context (developing a ThemeProvider)

I’ve been looking into theming a React project and whilst I ultimately intended to use the theming capabilities within material-ui I was interested in how I might write our own.

We’re going to be using React context.

Let’s start by creating a theme.ts file which will include our “base” theme style

export const theme = {
  button: {
    backgroundColor: "white",
    borderColor: "red",
    color: "blue",
    borderWidth: "2px",
    borderStyle: "solid",
    padding: "10px",
    fontSize: "24px",
  }
};

In the above code we created a button property which obviously defines our (rather horrible) default button style. Now we need some way to apply this theme (and any new version of our theme) to our application.

Hence we create a ThemeProvider (ThemeProvider.tsx in my case), which looks like this

import React from "react";
import { theme } from "../theme";

export const ThemeContext = React.createContext(theme);

export function ThemeProvider(props: any) {
  return (
    <ThemeContext.Provider value={props.theme}>
      {props.children}
    </ThemeContext.Provider>
  );
}

In this case, we create a React context, and assign a default value to it (in the shape of our base theme). The ThemeProvider function is then used to wrap our application supplying the theme to all components that wish to use it. So here’s an example of the ThemeProvider in use (in the App.tsx)

import React from 'react';
import { ThemeProvider } from './components/ThemeProvider';
import { Button } from './components/Button';
import { myTheme } from './myTheme';

const App: React.FC = () => {
  return (
    <ThemeProvider theme={myTheme}>
      <div className="App">
        <Button>Themed Button</Button>
      </div>
    </ThemeProvider>
  );
}

export default App;

You’ll notice we’ve also taken this opportunity to supply our overridden theme (with a slightly nicer button style) which is shown below (myTheme.ts)

export const myTheme = {
  button: {
    backgroundColor: "green",
    color: "white",
    borderWidth: "2px",
    borderStyle: "solid",
    padding: "10px",
    fontSize: "24px",
  }
};

As you can see from our App.tsx code, we’re using a Button component, so let’s look at that code now (Button.tsx)

import React from "react";
import { ThemeContext } from "./ThemeProvider";

export const Button = (props: any) => (
  <ThemeContext.Consumer>
    {theme => (
      <button style={theme.button} {...props}>
        {props.children}
      </button>
    )}
  </ThemeContext.Consumer>
);

In this code we consume the React context we created via the ThemeProvider file and apply the style to the button, also passing through any props to the button.

Now if you were to run this application the button will be displayed with our new theme (a nice green background button). If you change the App.tsx theme property to the default “base” theme, then we our default style.

CSS calc, you little beauty

When you have something like a React/material-ui appbar and maybe toolbars these would usually by set to a pixel height which is great, until we want to then expand a component beneath them and have it fill the available space ?

CSS calc() is here to save the day.

Here’s an example style in material-ui style code. In this code our appbar and toolbar’s combined height is 123px and we want the next component to fill the “container”. The following therefore calculates the available space by subtracting 123px from whatever 100% is.

const styles = (_theme: Theme) => ({
  container: {
    width: '100%',
    height: 'calc(100% - 123px)'
  },
});

Obviously we need to wrap our parent component in the withStyles function, i.e.

export withStyles(styles)(MyComponent);

then in the render method

const { classes } = this.props as any;
// ...
<div className={classes.container}>
// component to fill this space
</div>

Decorators (attributes/annotations) in TypeScript

TypeScript experimental (enabled by setting “experimentalDecorators”: true within the tsconfig.json) enables a feature known as decorators, in C# we call them attributes and Java they’re annotations. Basically they are way to extend the functionality of classes, either by adding meta data or adding code to enable AOP type functionality.

To define a decorator we simple write a function which is evaluated, then called by the runtime allow for us to write AOP etc.

A decorator function takes different arguments depending upon whether it’s used on a class, property, parameter or method. Let’s assume we were to create a logging AOP decorator, we’d use the following signatures.

Let’s assume we’re writing one of those classic AOP functions, a log function.

Class Decorators

The class decorator has a simple form, a single argument of type Function. This argument is basically our class function.

function logClass(target: Function): any {
  // implementation
}

If our desire is to intercept and effect the creation of our classes then we will need to execute this Function and then in essence add our own prototypes or the likes. If our aim is to log the creation of this class, for example if we want to view performance numbers, then we can obviously log before and after creation via the decorator.

Let’s create a fairly standard looking class decorator which in this case has extended the logClass signature to be type safe

function logClass<T extends {new(...constructorArgs: any[]): any}>(ctor: T) {
  const newCtor: any = function (...args: any[]) {
    // pre ctor invocation
    const f: any = function () {
      return new ctor(...args);
    }
    f.prototype = ctor.prototype;
    const instance: any = new f();
    // post ctor invocation
    return instance;
  }
  newCtor.prototype = ctor.prototype;
  return newCtor;
}

If we want to get the name of the class we (at least in TypeScript) will need to have a line such as

const target: any = ctor;

and from this target we can now get the name of the type, for example

console.log(target.name);

In usages we simply write the following

@logClass
class MyClass {
}

Method Decorators

The method decorator is somewhat more complicated, as one would expect, as it takes three arguments, looking like this

function logMethod(
  target: any, propertyKey: string, 
  propertyDescriptor: PropertyDescriptor): PropertyDescriptor {
  // implementation
}

The first argument is the instance of the class, the second is the method name and the final one relates to the PropertyDescriptor parameters for the method which is of the following sample shape

{
  value: [Function],
  writable: true,
  enumerable: true,
  configurable: true
}

For a usage example we might write something like this

function logMethod(target: any, 
   propertyKey: string, 
   propertyDescriptor: PropertyDescriptor): PropertyDescriptor {
  const method = propertyDescriptor.value;
  propertyDescriptor.value = function (...args: any[]) {
    // pre method call
    const result = method.apply(this, args);
    // post method call
    return result;
  }
  return propertyDescriptor;
};

In usages we simply write the following

class MyClass {
   @logMethod
   run(): void {
   }
}

Parameter Decorators

We can apply a decorator to an argument/parameter of a method with a decorator with the parameters show below

function logParameter(
   target: any, propertyKey: string | symbol, 
   propertyIndex: number): void {
  // implementation
}

The target is the instance of the target object, the propertyKey is the name of the method the parameter we’re decorating is on.

Note: As per TypeScript documentation on this decorator – a parameter decorator can only be used to observe that a parameter has been declared on a method.

In usages we simply write the following

class MyClass {
   @logMethod
   run(@logParameter options: any): void {
   }
}

You’ll notice the addition of the logMethod in the example above. The thing is the parameter decorator in this case needs to work alongside the method decorator because the parameters within this decorator will simply be the instance of the object or prototype, the method name and the index of the parameter. There isn’t any way from these three parameters to directly get the value passed into the parameter, hence we’d probably tend towards using the logParameter to add a prototype or the likes to allow the logMethod to then query for these prototypes.

However we might also be able to utilities the reflect-metadata npm package to embed meta data alongside our types – we’ll look at this in another post.

Property Decorators

A property in TypeScript is a field in C# terms. The format of the function is shown below, as per other functions, the target is the object or prototype. The propertyKey is the name of the property/field the decorator’s associated with.

function logProperty(target: any, propertyKey: string): void {
  // implementation
}

In usages we simply write the following

class MyClass {
   @logProperty
   public running: boolean;
}

We can insert our own implementation of the property’s getter/setter in the following way

function logProperty(target: any, propertyKey: string): void {

  let value = target[propertyKey];
  const getter = () => {
    return value + " Doo";
  };

  const setter = (newVal: any) => {
    value = newVal;
  };

  if (delete target[propertyKey]) {
    Object.defineProperty(target, propertyKey, {
      get: getter,
      set: setter,
      enumerable: true,
      configurable: true
    });
  }
}

In the above we create our own getter and setter, then remove the existing property replacing with our own.

Further…

Decorators are (at the time of writing this) in stage 2 proposal state, see https://github.com/tc39/proposal-decorators and are different in implementation to those experimental features within TypeScript.

Apollo GraphQL client

Based upon our previous implementation(s) of a server it’s now time to write some client code.

  • yarn add apollo-client
  • yarn add apollo-cache-inmemory
  • yarn add apollo-link-http
  • yarn add graphql-tag
  • yarn add isomorphic-fetch
  • yarn add -D @types/isomorphic-fetch

Now let’s create a simple script entry (this extends the previous post’s scripts section)

"scripts": {
  "build": "tsc",
  "client": "node client.js"
}

Now let’s create the file client.ts which should look like this

import ApolloClient from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import gql from 'graphql-tag';
import fetch from 'isomorphic-fetch';

const cache = new InMemoryCache();
const link = new HttpLink({
  uri: 'http://localhost:4000/',
  fetch: fetch
})

const client = new ApolloClient({
  cache,
  link,
});

client.query({
  query: gql`
    {
    users {
      firstName
      lastName
    }
  }`
  })
  .then((response: any) => console.log(response.data.users));

The response.data.users should now output the array of User objects.

Here’s an example of a mutation

client.mutate({
  mutation: gql`
    mutation {
      create(user: {
        firstName: "Miner",
        lastName: "FortyNiner"
      }) {
        firstName
        lastName
      }
    }
  `
}).then((response: any) => console.log(response.data.create));

Note: I had lots of issues around the fetch code, with errors such as Invariant Violation:
fetch is not found globally and no fetcher passed, to fix pass a fetch for your environment like
https://www.npmjs.com/package/node-fetch.
. The addition of the isomorphic-fetch solved this problem.

Writing our first Apollo GraphQL server

In the previous posts we’ve seen how to build an express based server and eventually add GraphQL to it.

graphql-express is not the only option for implementing GraphQL servers, let’s now look at Apollo – I cannot say whether one library is better than the other as I’ve not used them enough to comment although.

Let’s go through the process of creating a new project for this, so carry out the following steps

  • Create a folder for your project
  • cd to that folder
  • Run yarn init -y
  • Run tsc –init
  • Add a folder named models and a file named user.ts (we’ll use the same model/data from the previous posts), here’s the code for this file
    export default class User {
      constructor(public firstName: string, public lastName: string) {
      }
    }
    
    export const stubData = [
      new User('Scooby', 'Doo'),
      new User('Fred', 'Jones'),
      new User('Velma', 'Dinkley'),
      new User('Daphne', 'Blake'),
      new User('Shaggy', 'Rogers'),
    ];
    

So that’s the basics in place for the support data and model, let’s now look at the specifics for Apollo.

We’re going to create a file for the resolvers named resolver.ts and here’s the code

import User, { stubData } from "./models/user";

export const resolvers = {
  Query: {
    users: () => stubData,
  },
  Mutation: {
    create: (parent: any, args: any): User => {
      const data = new User(args.user.firstName, args.user.lastName)
      stubData.push(data);
      return data;
    }
  }
}

As you can see, we specify the Query and Mutation separately and the code’s pretty much the same as the express-graphql implementation except in the create mutation parameters. In this case the parent will supply any parent nodes whilst the args supplies the parameters from the mutation call.

Now we’ll create the file server.ts which will have the (as I’m sure you guessed) the server code as well as the schema definition (Apollo names this typeDefs).

import { ApolloServer, gql } from "apollo-server";
import { resolvers } from "./resolvers";

const typeDefs = gql`
type User {
    firstName: String
    lastName: String
  }

  input UserInput {
    firstName: String
    lastName: String
  }

  type Mutation {
    create(user: UserInput): User
  }

  type Query {
    users: [User]
  }
`;

const server = new ApolloServer({ typeDefs, resolvers });

server.listen()
  .then(({ url }) => {
    console.log(`Server listening to ${url}`);
  });

Note that Apollo uses a template tag gql function to declare the GraphQL schema, which along with the resolves is passed into the ApolloServer, then we simply start the server.

If you now run yarn build followed by yarn run the server will run up with default port 4000. Navigating to http://localhost:4000/ will display the GraphQL playground.

So, as you can see, Apollo is very simple to set-up for use as a GraphQL server.

Adding GraphQL to our Express server

In the previous two posts, we firstly create a basic server, then extended this with REST functionality, now lets add GraphQL capabilities.

First off, run the following

  • yarn add express-graphql graphql
  • Add a folder named schema

With the schema folder add a new file userSchema.ts with the following code

import { buildSchema } from "graphql";

export const schema = buildSchema(`
  type User {
    firstName: String
    lastName: String
  }

  type Query {
    users: [User]
  }
`);

In the above code, we create a GraphQL schema based upon our User type, along with the type Query for us to use the User type.

Next we’ll create a folder named resolvers along with the file userResolver.ts. GraphQL uses resolvers to interact with the data via GraphQL (like the code in the indexController in the previous post).

import { stubData } from "../models/user";

export const userResolver = {
  users: () => {
    return stubData;
  }
}

Now let’s update the server.ts file by adding the GraphQL code, I’ll include the whole file here (at least the specifics for GraphQL)

import graphqlHTTP from "express-graphql";
import { schema } from "./schema/userSchema";
import { userResolver } from "./resolvers/userResolver";

const server = express();

server.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: userResolver,
  graphiql: true,
}));

const port = 4000;
server.listen(port,
  () => console.log(`Server on port ${port}`)
);

In the above we’ve specified graphiql: true which allows us to via the GraphIQL page using http://localhost:4000/graphql

Now if you type the following into the left hand (query) pane

{
  users {
    firstName
    lastName
  }
}

Running this from GraphIQL should output all our stubData.

We’ve essentially got the users method from our original code but now let’s add the equivalent of the create method, which in GraphQL terms is a mutation type.

In the userSchema.ts file, change the schema to look like this

export const schema = buildSchema(`
  type User {
    firstName: String
    lastName: String
  }

  input UserInput {
    firstName: String
    lastName: String
  }

  type Mutation {
    create(user: UserInput): User
  }

  type Query {
    users: [User]
  }
`);

We’ve added an input type UserInput along with the type Mutation. Next, within the userResolve.ts file change the userResolve to add the create function, as below

export const userResolver = {
  users: () => {
    return stubData;
  },
  create: (data: any) => {
    stubData.push(new User(data.user.firstName, data.user.lastName)); 
    return data.user;
  }
}

Let’s test this code by executing the following query via GraphIQL

mutation {
  create(user: {
    firstName: "Miner",
    lastName: "FortyNiner",
  }) {
    firstName
    lastName
  }
}

Extending our Express based server

In my previous post I showed how to create an Express server in TypeScript. Let’s now extend things…

To start with, create some folders (the names are not important, in that this is not using convention based folder names). We’re going to set things up in a similar way to I’ve done for C# and Java…

  • Add a new folder controllers
  • Add a new folder models

Create a file server.ts in the root folder. Here’s the code from the previous post plus some extras

import express from "express";
import bodyParser from "body-parser";

const server = express();

server.use(bodyParser.json());

server.get("/", (request, response) => {
  response.send("<h1>Hello World</h1>");
});

const port = 4000;
server.listen(port, 
  () => console.log(`Server on port ${port}`)
);

The body-parser exposes middleware etc. in this case we’re adding the ability to work with JSON content type.

Now let’s change the existing route / and add some more, below is the additional code required in server.ts

import * as indexController from "./controllers/indexController";

server.get('/', indexController.index);
server.get('/users', indexController.users);
server.get('/users/create', indexController.create);

// POST implementation
// server.post('/users/create', indexController.create);

We now need to implement the model and the controller, so starting with the model, create the file user.ts within the models folder and it should look like this

export default class User {
  constructor(public firstName: string, public lastName: string) {
  }
}

export const stubData = [
  new User('Scooby', 'Doo'),
  new User('Fred', 'Jones'),
  new User('Velma', 'Dinkley'),
  new User('Daphne', 'Blake'),
  new User('Shaggy', 'Rogers'),
];

The stubData ofcourse is just here to give us some data to start things off.

In the controllers folder, add a new file named indexController.ts and the file should look like this

import { Request, Response } from "express";
import User, { stubData } from '../models/user';

export const index = (req: Request, res: Response) => {
  res.send("<h1>Methods are</h1><ul><li>users</li><li>create</li></ul>")
};

export const users = (req: Request, res: Response) => {
  res.json(stubData);
};

export const create = (req: Request, res: Response) => {
  const newUser = new User(req.query.firstName, req.query.lastName);
  stubData.push(newUser);
  res.json(newUser);
};

// POST implementation
// export const create = (req: Request, res: Response) => {
//   const newUser = new User(req.body.firstName, req.body.lastName);
//   stubData.push(newUser);
//   res.json(newUser);
// };

The scripts for package.json should also be taken from the previous post.

If you now run yarn build then yarn start your server will be up and running. Using the following URL’s

  • http://localhost:4000/
  • http://localhost:4000/users
  • http://localhost:4000/users/create?firstName=Miner&lastName=FortyNiner
  • will result in, the first URL returning a simple help screen using HTML, the second will list the current array of users using JSON and finally the third URL will create a new user, the response will show the new user as JSON, but if you call the users method again you will see the newly added user.

    I’ve also listed the POST implementations which you’d probably more likely use for mutations, but the GET implementations are fine in this instance and easier to test via our preferred browser.