Category Archives: TypeScript

One log decorator to rule them all

One log to rule them all

In a previous post we looked at implementing log decorators using the experimental features of TypeScript.

Obviously it’d be far nicer if we had a single log decorator that we can apply to each place where a decorator might be used, i.e. a log decorator which can be used instead of logClass, logMethod etc. We can simply create a log function which takes any number of arguments and have this delegate to the specific log function as required (a factory function basically). I’m not going to write the whole thing here, but something along the lines of (the following code) would do the job

function log(...args: any[]): void {
  switch(args.length) {
    case 1: 
      // return logClass
    case 2: 
      // return logProperty     
    case 3: 
      return (typeof args[2] === "number") ? 
        logParameter :
        logMethod
      default:
         throw new Error();    
   }
}

inversify

I wanted to use an IoC pattern/library in a project I was working on (a node server application where I would slot in the specific implementations of the data and services at runtime).

I cam across inversify, there may be other IoC’s for TypeScript, but this is the one I looked into and worked a treat for me.

yarn add inversify reflect-metadata

You will need to enable experimentalDecorators and emitDecoratorMetadata within the tsconfig.json file.

"experimentalDecorators": true,        
"emitDecoratorMetadata": true, 

as per the invertsify documentation, we should also have the following tsconfig.json properties set

"target": "es5",
"lib": ["es6"],
"types": ["reflect-metadata"],
"module": "commonjs",
"moduleResolution": "node",

As is usual in C# (at least), the best way to work with IoC is to define an interface for our abstraction and then an implementation. However for inverisfy we also define a Symbol for the interfaces. So let’s assume we’re going to have a Service interface along with RestService and WebsocketService.

First create your Service.ts file and here’s a simple example

interface Service {
  call(data: string): void;
};

export default Service;

Now let’s create a couple of implementations, RestService.ts

import "reflect-metadata";
import Service from "./Service";
import { injectable } from "inversify";

@injectable()
class RestService implements Service {
  call(data: string): void {
    console.log("RestService called");
  }
}

export default RestService;

As you can see, we decorate the class with @injectable as this class may be injected into another class.

Here’s our WebsocketService.ts

import "reflect-metadata";
import Service from "./Service";
import { injectable } from "inversify";

@injectable()
class WebsocketService implements Service {
  call(data: string): void {
    console.log("WebsocketService called");
  }
}

export default WebsocketService;

Within the types.ts (below) we create the symbols (keys/identifiers) for use with our types.

const Types = {
  Service: Symbol.for("Service"),
  Application: Symbol.for("Application")
};

export default Types;

Here’s an Application interface Application.ts

interface Application {   
  run(): void;
}

export default Application;

MyApplication.ts

import "reflect-metadata";
import { inject, injectable } from "inversify";
import Application from "./Application";
import Types from "./types";
import Service from "./Service";

@injectable()
class MyApplication implements Application {
  private readonly service: Service;

  constructor(@inject(Types.Service) service) {
    this.service = service;
  }

  run(): void {        
    this.service.call("");
  }
}

export default MyApplication; 

In the above you can see we’re injecting (via the @inject decorator on the constructor) a type of symbol type Service. Hence this is where the server implementation will be injected into.

To supply inversify with the implementations for various types, we create the file inversify.config.ts. In the example file below we bind the symbol type/identifier with an implementation.

import { Container } from "inversify";
import Service from "./Service";
import RestService from "./RestService";
import Types from "./types";
import WebsocketService from "./WebsocketService";
import Application from "./Application";
import MyApplication from "./MyApplication";

const container = new Container();

//container.bind<Service>(Types.Service).to(RestService);
container.bind<Service>(Types.Service).to(WebsocketService);
container.bind<Application>(Types.Application).to(MyApplication);

export default container;

Finally, here’s an example implementation of the index.ts

import container from "./inversify.config";
import Application from "./Application";
import Types from "./types";

const application = container.get<Application>(Types.Application);
application.run();

There’s more to inversify than just the code listed above, such as you can have multiple Service implementations and use named bindings to receive the one you want and more.

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.

Express server with TypeScript

Express is a popular web application server in JavaScript.

Let’s create a really quick and simple expression server which will be a good starting point for further posts which use this library.

I’ll assume you’ve created a folder, run yarn init and tsc –init, next up run

  • yarn add express
  • yarn add -D @types/express
  • Add a new file, mine’s named server.ts
  • Add the usual scripts
    "scripts": {
      "build": "tsc",
      "start": "node server.js"
    }
    

The code to run up a really simple server is as follows

import express from "express";

const server = express();

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

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

Using our scripts, run yarn build followed by yarn start. The server should start on port 4000, so now navigate to http://localhost:4000 using your preferred browser and that’s it.

We’ve created a server instance on port 4000, we’re routed any root (i.e. /) calls to the response, which in this case returns a simple HTML string.

The “is” type guard in TypeScript

The is keyword can be used to narrow types in TypeScript. What this means is that, for example if we’re testing whether an any parameter is a string a number or whatever, TypeScript will see the type as that type in subsequent code.

For example if we have the following functions

function isString(test: any): test is string {
  return typeof test === "string";
}

function isNumber(test: any): test is number {
  return typeof test === "number";
}

Each function simply takes a parameter of type any then if the return is true the parameter in subsequent code is seen by TypeScript as that type, i.e. Let’s assuming we have a some function which happens to use these functions, like this

function run(a: any) {
  if(isString(a)) {
    console.log(a)
  }
  if(isNumber(a)) {
    console.log(a)
  }
}

If you’re using Visual Code (for example) the TypeScript type popup (display when you move the mouse over the parameter a will show the type to be any in the run parameter list, or in any of the isXXX function parameters. But in the console.log after each isXXX function, TypeScript will interpret the type to be a string (in the case of isString) and a number (in the case if isNumber) hence using intellisense on the variables will list the methods etc. for a string and those for a number respectively

So basically the is keyword will appear to convert the parameter to the specified type if the function returns true.

To look at it another way we could instead write something like this

function isString(test: any): boolean {
    return typeof test === "string";
}

function isNumber(test: any): boolean {
    return typeof test === "number";
}

function run(a: any) {
  if(isString(a)) {
    const b = a as string;
    console.log(b)
  }
  if(isNumber(a)) {
    const b = a as string;
    console.log(b)
  }
}

For completeness, here’s the function syntax for a const style function

const isString = (test: any): test is string => {
  return typeof test === "string";
}

Simple node based HTTP server

In a previous post we used webpack to run an HTTP server, in this post we’re going to create a bare bones HTTP server using the http package.

In your chosen folder run the usual commands

  • yarn init y
  • tsc –init
  • Add a folder named public off of the folder you source will be in (this will be where we add static html files)

Now lets’ add the required packages

  • yarn add node-static @types/node-static

Add the following to the package.json

"scripts": {
  "start": "node server.js",
  "build": "tsc"
}

In the public folder add index.html with the following

<html>
  <head></head>
  <body>
  Hello World
  </body>
</html>

Now let’s add the code to start up our server (mine’s in the file server.ts)

import ns from "node-static";
import http from "http"

const file = new ns.Server("./public");

http.createServer((request, response) => {
    request.addListener("end", () => {
        file.serve(request, response);
    })
    .resume()
})
.listen(4000);

Simple run yarn build then yarn start and the server will start. Navigating your preferred browser to http://localhost:4000/ will then display the HTML file, i.e. Hello World text.