NodeJS Rest API with @novice1/app
Tags: javascript, beginners, node, typescript
Published: 2023-09-11 20:01:23

Yet another Node.js library based on `Express`. What could make you want to use [@novice1/app](https://www.npmjs.com/package/@novice1/app)? 
I'm glad you asked.

[@novice1/app](https://www.npmjs.com/package/@novice1/app) is as fast and minimalist as `Express` with new functionalities and its own [plugins](https://www.npmjs.com/search?q=%40novice1) that give a purpose to its existence.

Through this article and those to come, I'm going to show how it can be used to build small/mid-size Rest APIs, validate data, integrate websockets into your server, generate documentation for Swagger UI and/or Postman, etc ...

## Setup

First we initialize a project by running the command in the terminal

```bash
npm init
```
and install the dependencies

```bash
npm install --save-dev @types/cookie-parser @types/cors @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser dotenv eslint ts-node typescript
npm install @novice1/app @novice1/logger @novice1/routing cookie-parser cors express nodemon tslib
```

Once done, we can add the following scripts in the file _package.json_ that was created with `npm init`:

```json
{
    "scripts": {
        "build": "tsc",
        "dev": "nodemon",
        "lint": "eslint src/**/*.ts",
        "start": "node lib/index.js"
    }
}
```

To use those scripts we have to configure `eslint`, `nodemon` and `typescript`. Here is what I recommend as configuration files at the root of the project (feel free to add more options):

_`.eslintrc.json`_
```json
{
    "env": {
        "browser": true,
        "es6": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended"
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaVersion": 6,
        "sourceType": "module"
    },
    "plugins": [
        "@typescript-eslint"
    ],
    "rules": {
        "quotes": [1, "single"],
        "quote-props": [1, "as-needed"]
    }
}
```

_`nodemon.json`_
```json
{
    "watch": [
        "src"
    ],
    "ext": "ts,json",
    "ignore": [
        "src/**/*.spec.ts"
    ],
    "exec": "ts-node -r dotenv/config ./src/index.ts"
}
```

_`tsconfig.json`_
```json
{
    // see https://www.typescriptlang.org/tsconfig
    "include": ["src"],
    "exclude": ["node_modules"],
    "compilerOptions": {
      "typeRoots": ["node_modules/@types"],
      "module": "CommonJS",
      "lib": ["DOM", "ES6"],
      "target": "ES6",
      "importHelpers": true,
      "declaration": true,
      "sourceMap": true,
      "outDir": "./lib",
      "rootDir": "./src",
      "strict": true,
      "noImplicitReturns": true,
      "noFallthroughCasesInSwitch": true,
      "noUnusedLocals": true,
      "noUnusedParameters": true,
      "moduleResolution": "node",
      "esModuleInterop": true,
      "skipLibCheck": true,
      "forceConsistentCasingInFileNames": true
    }
  }
  
```

Now that we are done with the "basic" setup, we can start programming the application.

## First application

Let's program our **services**, **routes** and http **server**. 

### Services

To help the development process, we <u>need</u> a debugging utility.

_`src/services/debug.ts`_
```ts
import logger from '@novice1/logger';

logger.Debug.enable([
    'route*'
].join('|'));

export const debugRoute = logger.debugger('route');
```
There, we enable all namespaces starting with `'route'` (`route*`) and create the debugger `debugRoute` in namespace `'route'`. That way, we know that everything that `debugRoute` will log will be displayed.

### Routes

Our first route, for testing purposes, will simply render "Hello World!" or "Hello `<name>`!", `<name>` being the value of the url variable `name`.

_`src/routes/hello-world.ts`_
```ts
import routing from '@novice1/routing';
import { debugRoute } from '../services/debug';

// hello-world
export default routing().get({
    path: '/hello-world'
}, (req, res) => {
    debugRoute.extend('hello-world').debug(req.path);
    res.json(`Hello ${req.query.name || 'World'}!`);
})
```

Our second route is optional. It will enable pre-flight request for all requests.

_`src/routes/cors-options.ts`_
```ts
import cors from 'cors';
import routing from '@novice1/routing';

// enable CORS (Cross-origin resource sharing) pre-flight request
export default routing().options({
    path: '*'
}, cors())
```
From that we simply allow cross-origin resource sharing pre-flight without restrictions.

Then we export all routers from one module.

_`src/routes/index.ts`_
```ts
import corsOptions from './cors-options';
import helloWorld from './hello-world';

// all routers
export default [
    corsOptions,
    helloWorld
]
```

### Server

We can now use [@novice1/app](https://www.npmjs.com/package/@novice1/app) to register middlewares and routes.

_`src/app.ts`_
```ts
import { FrameworkApp } from '@novice1/app';
import cookieParser from 'cookie-parser';
import express from 'express';
import cors from 'cors';
import routes from './routes';

// init app
export const app = new FrameworkApp({
    framework: {
        // middlewares for all requests
        middlewares: [
            cookieParser(),
            express.json(),
            express.urlencoded({ extended: true }),
            cors()
        ]
    },
    routers: routes
})
```

And run the server on a specific port:

_`src/index.ts`_
```ts
import logger from '@novice1/logger';
import { app } from './app';

const PORT = process.env.PORT ? parseInt(process.env.PORT) : 8000;

// start server
app.listen(PORT, () => {
    logger.info('Application running on port', PORT)
})
```

## Run the application

### Development

To start the server in development mode, we add environment variables in the file _.env_. 

_`.env`_
```env
PORT=8080
```
This will set the environment variable `PORT` to `8080` when using the dependency `dotenv` (see _`nodemon.json`_ file).

Then we can start the server with the following command in the terminal:
```bash
npm run dev
```

You should see something like that in your console:
```bash
[nodemon] starting `ts-node -r dotenv/config ./src/index.ts`
info : Application running on port 8080
```

Open your favorite browser and go to [http://localhost:8080/hello-world](http://localhost:8080/hello-world) or [http://localhost:8080/hello-world?name=stranger](http://localhost:8080/hello-world?name=stranger) to see the result.

### Production

In a production environment, you should check, build and start the server as:
```bash
npm run lint
npm run build
npm start
```

## What now?
So we have a REST API but really nothing out of what Express can already do. In the next article, we will see how to use [@novice1/validator-joi](https://www.npmjs.com/package/@novice1/validator-joi) to validate request data and give a better structure to our project.

## References
You can see the result of what we have done till now [right here on Github](https://github.com/shygyver/nodejs-tuto/tree/part-01).