Clean install

This page provides a Step by step guide to creating a clean Enso project.

You can also use the quickstart to achieve the same goal.


Step by step

Install packages

Create a new directory and initialise the project. Then install the following package dependencies.

# init new project
yarn init

# add deps
yarn add @ensojs/framework typescript ts-node tslint-config-standard dotenv-safe @types/node

Setup TypeScript

Configure the typescript compiler

In the project root vi tsconfig.json.

{
  "compilerOptions": {
    "module": "commonjs",
    "alwaysStrict": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "target": "es6",
    "lib": [
      "es6",
      "es7"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}

Configure the linter

In the project root vi tslint.json. Enso adopts the Standard JS style.

{
  "extends": [
    "tslint-config-standard"
  ]
}

Prepare the directory structure

mkdir -p src/config

Setup a HTTP server

Note

A HTTPServer is a thin abstraction around the excellent koa library

src/App.ts

// src/App.ts
import { HTTPServer } from '@ensojs/framework'

export class App extends HTTPServer {
  applyMiddleware (): void {}
}

src/server.ts

This manages an instance of your App.

// src/server.ts
import 'reflect-metadata'

import { App } from './App'
import { env } from './config/env'
import { container } from './config/container'

const debug = require('debug')('server')

const server = async () => {
  try {
    debug('============================================')
    debug('> Starting server...')
    debug('============================================')

    const app = new App(env)
    await app.build(container)
    await app.start()

    debug('')
    debug('✔ [nodejs] %s', process.version)
    debug('')
    debug(
      '✔ API server listening on port %d in [%s] mode',
      env.PORT,
      env.ENVIRONMENT
    )
  } catch (e) {
    debug(e)
    process.exit(1)
  }
}

// start
server()

If at this point we attempt to start the server, we will see some errors given we are yet to define an environment and a container.

# Attempt to start server
ts-node src/server.ts

Setup your environment

Enso uses dotenv-safe. Your must have an ENVIRONMENT and PORT defined.

src/.env.example

# file: src/.env.example
#=========================================
# Settings
#=========================================
ENVIRONMENT="development"
PORT=5000

Then copy the settings locally and fill in the relevant values.

cp src/.env.example src/.env

Important

Remember to not commit your “.env” to your repository and add it to your “.gitignore” file

src/config/env.ts

Let’s explicitly bind our environment to an interface.

import dotenv from 'dotenv-safe'
import { IEnvironmentConfig } from '@ensojs/framework'

dotenv.config({
  allowEmptyValues: false
})

export const env: IEnvironmentConfig = {
  ENVIRONMENT: process.env.ENVIRONMENT!,
  PORT: parseInt(process.env.PORT!)
}

Setup a container

Eventually our controllers and other services will be registered here.

src/config/container.ts

import { Container } from 'inversify'

export const container = new Container()

Utilse debugging

The debug package heavily used within the NodeJS ecosystem. Take some time to learn it.

# Attempt to start server again...
ts-node src/server.ts

There are now no error messages, however there are still some issues that need to be fixed. Append the DEBUG=* flag when starting the server to investigate further.

# Start the server with debug
DEBUG=* ts-node src/server.ts

From the messages we can see that we are missing a controller which brings us to the next step.

Info

Currently the inversify bridge Enso depends on requires a controller to be setup inversify-koa. We aim to resolve this in the future.

So for now…

Continue setup with an IndexController

Our controller will respond with its package name and version number.

src/IndexController.ts

// file: src/IndexController.ts
import { injectable } from 'inversify'
import { interfaces, controller, httpGet } from 'inversify-koa'
import Router from 'koa-router'

import pkg from '../package.json'

@injectable()
@controller('/')
export class IndexController implements interfaces.Controller {
  /**
   * GET /
   */
  @httpGet('')
  async getIndex (ctx: Router.IRouterContext) {
    ctx.status = 200
    ctx.body = {
      name: pkg.name,
      version: pkg.version,
      env: process.env.NODE_ENV,
      up: process.uptime(),
      message: 'ok'
    }
  }
}

Edit the container file and register our IndexController.

// file: src/config/container.ts
import { Container } from 'inversify'
import { interfaces } from 'inversify-koa'import { TYPE } from '@ensojs/framework'import { IndexController } from '../IndexController'
export const container = new Container()

// controllerscontainer.bind<interfaces.Controller>(TYPE.Controller).to(IndexController).whenTargetNamed('IndexController')

Now our server should be up and running.

# start the server
DEBUG=* ts-node src/server.ts

# server ============================================ +0ms
# server > Starting server... +1ms
# server ============================================ +0ms
# enso:HTTPServer listRegisteredControllers() +0ms
# enso:HTTPServer  => IndexController +2ms
# ...
# server ✔ [nodejs] v10.15.3 +0ms
# server ✔ API server listening on port 5000 in [development] mode +0ms

A simple requests to curl localhost:5000 should display

{
   "name":"@enso/standalone",
   "version":"1.0.0",
   "up":5.031,
   "message":"ok"
}

Congratulations, you now have a skeleton HTTP server that:

  • works with TypeScript
  • configured with koa
  • supports dependency injection with inversify
  • can be introspected with debug

Next

  • Learn more about the Concepts behind Enso
  • Start adding some functionality with some Recipes