Create an online IDE with Angular 6 + NodeJS ~ Part #2

Ofir G
8 min readOct 16, 2018

--

Part #2 — Building Express-Typescript app boilerplate

What you will do on this part

On this part of the project you will create a starter boilerplate of node express server implementing REST API, using ExpressJS framework and all written in Typescript 😍 .

Contents Table — Part #2

  • Short intro to REST and ExpressJS framework
    ◢ A little REST
    ◢ The Famous ExpressJS !
  • Express server Starter Boilerplate in Typescript.
    ◢ The Folders Structure.
    ◢ Convenience of nodemon.
    ◢ Start Serving.
    ◢ Adding Routes to your Server.
    ◢ Modulated Routes
    ◢ Configure environment variables.

If you haven’t checked out the previous post in the series, here is the link:
Part #1 — Building your IDE client-side with Angular and Bootstrap

A little REST 💤

In the following post, you will set the stage for your REST API application, to do so, we must discuss a bit first on what is a REST API, and how to build one.
What that means for you, in very few words, is that on this server’s API implementation you will go along the lines of the REST rules and ideas.
You will use the REST HTTP methods and only in their designed context, all other (most) conceptual rules of the REST API was laid for you by Express framework.

Still, as a web developer, there are some things you’ll need to know about this subject, in general.
The term “REST” refers to an architectural style (wiki page), the REST API simply follow some of the concepts of this style, the implications are that as a developer you constrains yourself to the REST rules and by that simplifies your API’s development process.

So, the most significant rules of REST you’ll need to follow :

  • Uses stateless interactions — the server will not store any state information, each request will be handled independently, and will not behave differently due to stored state data.
  • Communicates over HTTP — the server will use only HTTP methods, in their correct context.
  • Uses standard HTTP status codes — to any server response there will be attached an HTTP status code, with a certain role that will reflect the response’s result.

ℹ️ extra resources: restfulapi.net, relativity.com, zapier.com, uci.edu

The Famous ExpressJS!

ExpressJS library is a framework for NodeJS, designed for building web applications and APIs in a very simplistic and minimalist way while remaining highly configurable.
It has been called the de facto standard server framework for NodeJS.

If you’re not familiar with the usage of express, you will be glad to hear that it’s got a highly repetitive pattern, meaning that once you got the express server setup, your code will more or less apply to most of your express API apps.
For my own projects, I use my starter boilerplate and extend it to each app specifications.
That starter boilerplate resulted from the similar and repetitive usage in express, and with that boilerplate, you will build this project’s server.

Express server Starter Boilerplate in Typescript

The Folders Structure

My express server applications follow this folders structure, I recommend you to follow it as well (at least on this project, so we will be in sync):

server                 /* at the root of the project*/
| |--config /* setting custom env variables */
| | |--config.json
| | |--config.ts
| |--routes /* defining the API routes */
| | |--app.routes.ts
| |--tests /* all app tests under this folder */
| |--server.ts /* here we actually serving this app */
| |--white-lists.ts /* domains with allowed access */

take a moment and create the following folders and files specified above.
I’m assuming from this point that you’re using this structure with those folders and files names.

Typescript Dependencies and Configuration

First, initialize NPM into your app (create package.json), then install those packages — so node will be able to compile your typescript code to plain js :

npm init
npm install typescript ts-node --save

Add a tsconfig.json file (your typescript compiler options) at the root of your project, with the following :

{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"pretty": true,
"sourceMap": true,
"target": "es6",
"outDir": "./dist",
"baseUrl": "./server"
},
"include": [
"server/**/*.ts"
],
"exclude": [
"node_modules",
]
}

and on package.json modify the scripts property to :

"scripts": {
"dev": "ts-node ./server/server.ts",
"prod": "npm run tsc"
}

Convenience of nodemon

Nodemon is a great tool that automatically restarting your server when it detected changes in your server directory.
To use it follow these steps :

npm install nodemon --save-dev

Modify the scripts property on package.json to :

"scripts": {
"build": "tsc",
"dev": "nodemon ./dist/server/server.js",
"start": "ts-node ./server/server.ts",
"prod": "npm run build && npm run start"
}

Add nodemon configuration to package.json file using nodemonConfig property, so add this to your package.json file:

{
...
"scripts": {
...
},
"nodemonConfig": {
"ignore": [ "**/*.test.ts", "node_modules" ],
"watch": ["server"],
"exec": "npm start",
"ext": "ts"
}
...
}

With all this in place, npm run dev command will start the server with nodemon watching for changes, npm run start command will start the server without nodemon.
(At this point your server.ts file is empty so there’s no server to be served, test those command after the next section)

Start Serving

First, install the needed dependencies :

npm install express @types/express body-parser --save

Add to server.ts file the following code:

Setting up a server with express is as simple as that.
express() create a new express app, const app will play as your server instance.
in _init() method you’re initializing the middleware your app will use, by calling app.use(some-middleware) (E.g. parsing method for incoming requests).
With app instance you’ve created, you’ll listen to PORT, and any request sent to localhost:PORT will end up at your newly created server.

Serving the app with node watching

Adding Routes to your Server

On app.routes.ts file add the following code:

on server.ts import the appRoutes we exported here, and connect those routes into your app instance with app.use(appRoutes) ;

import * as express from "express";
import * as bodyParser from "body-parser";
import { appRoutes } from './routes/app.routes';
...
...
app.use(appRoutes); // connecting routes
app.listen(process.env.PORT || PORT, () => {
console.log(`server running on port ${PORT}`);
})

an HTTP GET request to localhost:3000/ will execute the callback provided in routes.get(‘/’, callback) [line 12].
Give it a try 👉 run the command npm run dev on your project’s terminal, and open the browser on localhost:3000, you will see “hello from server” displayed on the page, that’s the response returned by the line res.send('hello from server') [line 15].
(BTW, the default action of your browser when you enter a URL is to execute an HTTP GET request to that URL).

hello from server 👋

Modulated Routes

The express framework gives you the ability to modulate your app’s routes setting to separate classes in separate files.
Let’s take those requests URIs (also referred as ‘request routes’) for example :

POST : some.domain.com/user
GET : some.domain.com/user
GET : some.domain.com/user/data
GET : some.domain.com/items

the folders structure, in this case, will be (under the dir server/routes) :

routes
| |--user
| | |--user.routes.ts
| |--items
| | |--items.routes.ts
| |--app.routes.ts

each main prefix* on those request-routes will claim it’s own folder and file, so the logic handling the requests under a main prefix, let’s say /user, will be in user.routes.ts.

The implementation for this example will be something like this:

Routes connection diagram

— “each main prefix on those request-routes will claim its own folder” Feel like needing further explanations :

main prefix —
In the example above the main prefixes was /user and /items , meaning all the request-routes that start with /user will be handled in user.routes.ts (same for /items in items.routes.ts).
Let’s say that /user/data has a lot of logic under it (big chunk of code that’s relevant only to that request-route), then you can refer to /user/data as a main prefix, and separate it from /user prefix to its own folder (then connect it to userRoutes).
Handling sub-route (/data in this example) as a main prefix or not?
those kinds of things are left for your judgment.

Configure Environment Variables

Node apps, like most, work with multiple environments, that can be accessed through proccess.env.NODE_ENV value.
The common environments are “development”, “testing” and “production” (the aliases are “dev”, “test”, “prod”).
To clarify, NODE_ENV value (the current environment) configured by an “entity” from outside the app’s borders (E.g. the developer manually, or some NPM script).

Using the above, we can easily define different behavior in different environments, a little example (only to simplify the concept of different behavior — not a real-life example) :

const curEnv = proccess.env.NODE_ENV; 
let dbUri;
if (curEnv == 'prod') {
dbUri = 'prod.example-db.com'; // database for production
} else if (curEnv == 'test') {
dbUri = 'test.example-db.com'; // database for testing
} else {
dbUri = 'dev.example-db.com'; // database for development
}
db.connect(dbUri); // different db on different environments

What a mass for something as trivial as assigning a value 🙄
Try to avoid the approach of assigning a value to environment-depend variables (like dbUri from this example) by if-else each of your environments.

The strategy commonly used to solve this issue is to store all the environment-depend values in one json file, and in one occasion, assign those values to the global object process.env , and with that, all this messy conditional value assignment we used in the example is gone 👍.

Now, all the environment-depend values, or values that considered sensitive from a security aspect (E.g. external API credentials, secret keys) will be stored on config.json file;

example for config.json file

here you’ll assign all the properties from one of the environments keys (that corresponds to the current environment) on config.json to process.env object.

Side-Note — you’re using require(‘./config.json’) and not the typical import statement.
import statement must be called on the top of a .ts file, before all other definitions, and in this case you must import config.json inside the if block. Way is that ?
config.json containing sensitive data, and will not be stored on git or the server (you’ll deploy to) for this app production, so importing config.json file on production will cause the app to crash, luckily 😏 the condition on the if statement [lines 14–20] will never be satisfied on production and the code inside it won’t be executed 👌.

import config.ts file on server.ts ;

import * as express from "express";
import * as bodyParser from "body-parser";
import './config/config.ts'; // include the '.ts' extension
import { appRoutes } from './routes/app.routes';
class ExpressApp {
....
...

Let’s review

  1. You set up your server folder structure, installed the needed typescript dependencies and created a tsconfig.json file.
  2. You integrated nodemon into your project environment.
  3. You implement your express server and served it.
  4. Added API routes to your express server.
  5. Configured environment variables in a clean way.

You’re done with building the starter express-typescript boilerplate 👏.
On the next part — building your API’s routes.

--

--