Example Typescript Monorepo Part 2 API

George Harwood • Jan 24, 2018

This is part 2 of a series called “Example Typescript Monorepo”. If you haven’t read through part 1, I would recommend doing that before reading the following.

In this tutorial we will be using a variety of different technologies including typescript, express, gulp, yarn-workspaces, and module-alias.

Before we begin, I just want to mention that my goal is to help you setup your project for local development, to give you the best experience possible around this fairly new take at a typescript/javascript monorepo, and to hopefully show you a few things you didn’t know before. I don’t want to force my architectural opinions on anyone, but I will be modeling my code after clean architecture principles. You can read more about it on page 28 of this document.

I will also mention my editor of choice is vscode.

Let's get coding.

Our Goal will be a couple simple endpoints that send a string from the Core directory and Infrastructure directory. We won’t be getting into db’s, or really anything too intensive. Again, my goal is to just help you get set up.

At the end of this part our directory structure should looks something like this:

api
  src
    controllers
      ...
    index.ts
  gulpfile.js
  package.json
core
infrastructure
native
...


To start open up the project you built from part 1, and add a directory called api. In that directory create a file called package.json and add the following:
<pre><code> "name": "api",<br> "version": "0.0.1",<br> "description": "API for Monorepo",<br> "main": "index.js",<br> "scripts": {<br> "api": "gulp"<br> },<br> "license": "UNLICENCED",<br> "devDependencies": {<br> "@types/body-parser": "^1.16.8",<br> "@types/express": "^4.0.39",<br> "del": "^3.0.0",<br> "gulp": "^3.9.1",<br> "gulp-batch": "^1.0.5",<br> "gulp-sourcemaps": "^2.6.1",<br> "gulp-typescript": "^3.2.3",<br> "gulp-watch": "^4.3.11",<br> "typescript": "^2.6.2"<br> },<br> "dependencies": {<br> "body-parser": "^1.18.2",<br> "express": "^4.16.2",<br> "module-alias": "^2.0.3",<br> "reflect-metadata": "^0.1.10"<br> }<br>}</code></pre>
We will also need to add api in the workspaces attribute of the top level package.json file. Should look like this afterwards:

"workspaces": [
    "core",
    "native",
    "infrastructure",
    "api"
  ],


Add another file called gulpfile.js. We will be using gulp to watch our javascript files that were output by typescript and start our server. We didn’t have to do this in the react-native part because react-native handles hot reloading for us. Inside of the gulp file include:

const spawn = require("child_process").spawn;

const del = require("del");
const gulp = require("gulp");
const batch = require("gulp-batch")
const ts = require("gulp-typescript");
const sourcemaps = require("gulp-sourcemaps");
const watch = require("gulp-watch");

let node;

gulp.task("server", function () {
  if (node) node.kill();

  console.log("Launching server");
  node = spawn("node", ["--harmony", "--inspect=0.0.0.0:9229", "../dist/api/src"], {stdio: "inherit"})
  node.on("close", function (code) {
    if (code === 8) {
      console.log("Error detected, waiting for changes...");
    }
  });

});

gulp.task("default", ["server"], function () {
  watch('../dist/**/*.js', batch(function (events, done) {
    gulp.start('server', done);
  }));
});

process.on("exit", function () {
  if (node) node.kill()
});


Add a folder called src in the api directory and in that folder add a file called index.ts. This will be the entry point for our api. Inside of it add:

require('module-alias').addAlias("~", __dirname + "/../../")

import * as express from "express";
import * as bodyParser from "body-parser";
import controllers from "./controllers";

const app = express();
const port = process.env.PORT || 9229;

app.use(bodyParser.json());

app.use((req: any, res: any, next: any) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
  res.header("Access-Control-Allow-Headers", "Origin, Content-Type, Accept");
  next(); 
});

app.get('/', (req: any, res: any) => {
  res.json('Example MonoRepo API');
});

app.use('/api', controllers);

app.listen(port);

export default app;


This is a pretty typical entry point for an express application, keep in mind you could use hapi, restify, etc here as well. I just like the simplicity of express.
Next let's add a folder called controllers in monorepo/api/src. In that folder create a file called index.ts. This file will bundle up all of our controllers, but we will only be creating one controller for the purposes of this tutorial. In the file add:

import * as express from "express";

const controllers = express.Router();
import helloWorldController from "./helloWorldController";

controllers.use("/hello", helloWorldController);

export default controllers;


This assigns the helloWorldController to the /api/hello route. As a sibling to index.ts add a file called helloWorldController.ts. In that file add:

import * as express from "express";

import core from "~/core";
import infrastructure from "~/infrastructure";

const helloWorldController = express.Router();

helloWorldController.get('/core', (req: any, res: any) => {
  //Talk to a service that talks to a db
  res.json(core);
});
helloWorldController.get('/infrastructure', (req: any, res: any) => {
  //Talk to a service that talks to a db,
  res.json(infrastructure);
});

export default helloWorldController;


Okay we are almost there. Let's add our gulp command to the root package.json.

"scripts": {
    ...
    "api:start": "cd api && gulp", //
    ...
},


Lastly let's do a yarn run reset:modules to clear out all our node_modules and yarn again. This is just a precautionary step.

Now run yarn run watch. You might need to stop and start the terminal that was running this previously from the first part of this post series.

Then run yarn run api:start. If you don’t see any errors try going to http://localhost:9229/api/hello/core. You should see a friendly "Hello Core World! Message.
If you don’t…I am sorry. Check against my example-typescript-monorepo to see if you are missing anything. Or feel free to submit an issue!
Until next time.