Yet another Node.js library based on Express
. What could make you want to use @novice1/app?
I'm glad you asked.
@novice1/app is as fast and minimalist as Express
with new functionalities and its own plugins 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
npm init
and install the dependencies
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
:
{
"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
{
"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
{
"watch": [
"src"
],
"ext": "ts,json",
"ignore": [
"src/**/*.spec.ts"
],
"exec": "ts-node -r dotenv/config ./src/index.ts"
}
tsconfig.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 need a debugging utility.
src/services/debug.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
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
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
import corsOptions from './cors-options';
import helloWorld from './hello-world';
// all routers
export default [
corsOptions,
helloWorld
]
Server
We can now use @novice1/app to register middlewares and routes.
src/app.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
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
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:
npm run dev
You should see something like that in your console:
[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 or 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:
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 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.
Top comments (0)