Step by step instructions to set up the basic node application that we will use to develop the store app project. The application will make use of the following tools:
- node for runtime environment
- npm for managing node packages
- GIT for version control
- PostgreSQL for database
- typescript for typed javascipt
- prettier for code formatting
- eslint for code check
- Express as server framework
- tsc-watch for development to restart server after each change in code
- Jasmine for testing
- morgan for logging HTTP requests
- body-parser for parsing HTTP request bodies
- dotenv for handling enviromental variables
- node-postgres to handle the
Postgresdatabase - db-migrate and db-migrate-pg to handle migrations
- jwt for authentication
- bcrypt for password encyption
- docker for running postgresql in a container
mkdir storeApp
cd storeApp
mkdir src
mkdir dist
mkdir src/models
mkdir src/handlers
mkdir src/tests
touch src/server.ts
echo "console.log('Hello World')" > src/server.tsInitiate node app
Install node if needed. This will also install npm
After installation initiate node application:
npm init -ySetup GIT repository
- Install git if needed
- Initiate a git repository
git init- Create
.gitignorefile
echo '
node_modules
dist
' > .gitignore- Make initial commit
git add .
git commit -m 'feat: Initial commit'- Setup remote repository
git remote add origin <remote_repo_URL>
git push origin masterAdd typescript
- Install
npm i --save-dev typescript ts-node @types/node- Add configuration files
Add typescript configuration file to project root directory
npx tsc --init- creates
tsconfig.json - use settings below in
tsconfig.json:
{
"compilerOptions": {
/* Language and Environment */
"target": "ES2020",
"lib": ["ES2015", "DOM"],
/* Modules */
"module": "commonjs",
"rootDir": "./src",
/* Emit */
"outDir": "./dist",
/* Interop Constraints */
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
/* Type Checking */
"strict": true,
"noImplicitAny": true,
"skipLibCheck": true,
},
}- Add scripts to
package.json
"scripts": {
"start": "npm run build && node dist/server.js",
"build": "npx tsc"
},buildwill convert typescript to javascript and store the files under the./distfolder (set as theoutdirintsconfig.json)startwill run build and start the application by usingnodeto run the./dist/server.jsfile- Running
npm startlogs "Hello World".
- Commit changes
git add .
git commit -m 'chore: Add typescript to project'npm i --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
npm i --save-dev prettier eslint-config-prettier eslint-plugin-prettierIn project root directory:
echo '{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint",
"prettier"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
"no-console": "off",
"prettier/prettier": 2 // Means error
}
}
' > .eslintrcecho '{
"semi": true,
"singleQuote": true
}' > .prettierrcIn package.json under scripts add:
"scripts": {
"prettier": "prettier --config .prettierrc \"src/**/*{js,ts,tsx}\" --write",
"eslint": "eslint \"src/**/*.{js,ts}\"",
"lint": "npm run prettier && npm run eslint",
},- run
npm run lintto run both prettier and eslin. Prettier will automatically correct mistakes while eslint will show a list of errors and warnings.
npm run lintgit add .
git commit -m 'chore: Add Eslint and prettier to project'Add Express
npm i express
npm i --save-dev @types/express
npm i --save-dev tsc-watchtsc-watchwill restart the server every time we save changes
Replace corrent content of the src/server.ts file with:
import express from 'express'
// create the app express objects
// it enables us to use express methods
const app = express();
const port = 3000; //can be any number > 1024
// middlewares
// map incoming requests to an endpoint
app.get('/', (req, res) => {
res.send('server is working');
});
// start the server
app.listen(port, () => {
console.log(`Server started at http://localhost:${port}`);
});
export default app;- running
npm run startlogs:Server started at: localhost:3000. - opening the browser at
localhost:3000the page displays:Server is working.
npm run lintgit add .
git commit -m 'chore: Add Express to project'Add tsc-watch
tsc-watch will restart the server after every change in the application.
npm i tsc-watch"watch": "tsc-watch --esModuleInterop src/server.ts --outDir ./dist --onSuccess 'node ./dist/server.js'",
"devStart": "npm run watch",The script devStart will be used to start the application during development as it uses tsc-watch to restart the server after every change in the application.
npm run lintgit add .
git commit -m 'chore: Add tsc-watch to project'Add Jasmine for testing
# install jasmine and jasmine-spec-reporter
npm i jasmine jasmine-spec-reporter
# install type definitions for jasmine
npm i --save-dev @types/jasmine
# install supertest for api endpoints testing
npm i --save-dev supertest
# install type definitions for supertest
npm i --save-dev @types/supertest- jasmine-spec-reporter is a real time console spec reporter for
jasmine - supertest is a module for testing HTTP
# directory for jasmine-spec-reporter
mkdir src/tests/helpers
# directory for model tests
mkdir src/tests/models
# directory for API endpoint tests
mkdir src/tests/routes
# directory for utility tests
mkdir src/tests/utilitiesRun:
npx jasmine init- Creates
specdirectory andjasmine.jsonconfiguration file. - In configuration file set:
{
"spec_dir": "dist/tests",
"spec_files": [
"**/*[sS]pec.js"
],
"helpers": [
"helpers/**/*.js"
],
"stopSpecOnExpectationFailure": false,
"random": false
}Create file:
# file for jasmine-spec-reporter configuration
touch src/tests/helpers/reporter.tsWith content:
import { DisplayProcessor, SpecReporter, StacktraceOption } from 'jasmine-spec-reporter'
import SuiteInfo = jasmine.SuiteInfo
class CustomProcessor extends DisplayProcessor {
public displayJasmineStarted(info: SuiteInfo, log: string): string {
return `TypeScript ${log}`
}
}
jasmine.getEnv().clearReporters()
jasmine.getEnv().addReporter(
new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.NONE,
},
customProcessors: [CustomProcessor],
})
)Create spec file:
# file for first spec
touch src/tests/server_spec.tsWith content:
import { agent as request } from 'supertest';
import app from '../server';
describe('GET /', () => {
it('responds with: server is working.', (done) => {
request(app)
.get('/')
.expect(200)
.expect('Content-Type', 'text/html; charset=utf-8')
.then((response) => {
expect(response.text).toBe('server is working.');
done();
})
.catch((Error) => {
Error ? done.fail(Error) : done();
});
});
});"jasmine": "jasmine",
"test": "npm run build && npm run jasmine",- running
npm run testbuilds the project and succesfully runs the one spec we created
npm run lintgit add .
git commit -m 'chore: Add Jasmine to project'Add morgan
HTTP request logger middleware for node.js
# install morgan
npm i morgan
# install types for morgan
npm i --save-dev @types/morgan// import morgan, an HTTP request logger middleware
import morgan from 'morgan';
/*After the `app` is declared under `middlewares` include:*/
// log HTTP requests wiht morgan('dev' specifies the format of the output)
app.use(morgan('dev'));- running
npm run testwill showmorganlogging the http request of our spec
npm run lintgit add .
git commit -m 'chore: Add morgan to project'Add body-parser
Node.js body parsing middleware.
Parse incoming request bodies in a middleware. It will populate the req.body with the parsed body.
npm i body-parserTo use body-parser edit the server.ts file:
// import bodyParser, an HTTP request body parser middleware
import bodyParser from 'body-parser';
////after declaring the `app` under `middlewares` include
// parse the HTTP request body
app.use(bodyParser.json());npm run lintgit add .
git commit -m 'chore: Add body-parser to project'Add dotenv
Dotenv is a zero-dependency module that loads environment variables from a .env file into process.env. It gives us a way to store environment variables separate from code
npm i dotenv.env file will store the environment variables
touch .envWith content:
# for setting environment
ENV=to_be_set
# for postgres database
POSTGRES_HOST=to_be_set
POSTGRES_PORT=to_be_set
POSTGRES_DB=to_be_set
POSTGRES_DB_TEST=to_be_set
POSTGRES_USER=to_be_set
POSTGRES_PASSWORD=to_be_set
# for JSON Web Token
TOKEN=to_be_set
# for bcrypt
BCRYPT_PASSWORD=to_be_set
SALT_ROUNDS=to_be_set- docker will set up our psql database using the database name, and user info from the
.envfile - we will set up the test database manually
- the
ENVvariable to set the current environment (development or test)
It will keep sensitive information local.
echo ".env" >> .gitignoreStore you environment variables in the .env file:
YOUR_VARIABLE=myvariableIf you want to use the environment variables stored in the .env file:
//Initialize environment variables in your program
dotenv.config()dotenv.config() will create a javascript object called process.env which will have all the kyes and values set in the .env file.
//environment variables can be accessed like:
const myVariable = process.env.VARIABLE_KEY
// or using object destructuring
const { YOUR_VARIABLE } = process.env;git add .
git commit -m 'chore: Add dotenv to project'Add node-postgres
node-postgres is a collection of node.js modules for interfacing with a PostgreSQL database.
npm i pg
npm i --save-dev @types/pg Create file to handle connection.
touch src/database.tsWith content:
/* this file connects the application to the postgres database */
// import dotenv, to process environment variables stored in the `.env` file
import dotenv from 'dotenv';
// import pool, to connect to the database
import { Pool } from 'pg';
// intialize environment variables
dotenv.config();
// get environmental variables
const {
POSTGRES_HOST,
POSTGRES_PORT,
POSTGRES_DB,
POSTGRES_DB_TEST,
POSTGRES_USER, POSTGRES_PASSWORD,
ENV
} = process.env;
// declare client
const client = new Pool({
host: POSTGRES_HOST,
port: parseInt(POSTGRES_PORT as string),
database: ENV == "dev" ? POSTGRES_DB : POSTGRES_DB_TEST,
user: POSTGRES_USER,
password: POSTGRES_PASSWORD,
});
export default client;npm run lintgit add .
git commit -m 'chore: Add node-postgres to project'Add db-migrate
db-migrate is a database migrations framework for nodejs. Migrations are documents used to build the database and track changes to its schema over time.
npm install -g db-migrate
npm install db-migrate-pg- installing
db-migrateglobally (-g) allows us to use the terminal commands it provides - db-migrate-pg is a postgres driver for db-migrate
Create database.json file in project root directory
touch database.json- The
database.jsonfile specifies what database we want to run migrations on. It supports the concept of environments.You can pass the -e or --env option to db-migrate to select the environment you want to run migrations against. - You can also specify environment variables in your config file by using a special notation.
- Set up the
database.jsonfile like below:
{
"dev": {
"driver": "pg",
"host":{"ENV": "POSTGRES_HOST"},
"port": { "ENV": "POSTGRES_PORT"},
"database": {"ENV": "POSTGRES_DB"},
"user": {"ENV": "POSTGRES_USER"},
"password": {"ENV": "POSTGRES_PASSWORD"}
},
"test": {
"driver": "pg",
"host": {"ENV": "POSTGRES_HOST"},
"port": { "ENV": "POSTGRES_PORT"},
"database": {"ENV": "POSTGRES_DB_TEST"},
"user": {"ENV": "POSTGRES_USER"},
"password": {"ENV": "POSTGRES_PASSWORD"}
}
}npm run lintgit add .
git commit -m 'chore: Add db-migrate to project'Add JWT (JSON Web Token)
JWT will be used for authentication.
# install jwt
npm i jsonwebtoken
# install typescript types for jwt
npm i --save-dev @types/jsonwebtokenTOKEN_SECRET=verySecretToken- Create a token:
jwt.sign(<objectToIncludeInToken>, <TOKEN_SECRET>) - Check a token:
jwt.verify()
git add .
git commit -m 'chore: Add jwt to project'Add bcrypt
Used for password encription
npm i bcrypt
npm i --save-dev @types/bcrypt- make sure the the following enviromental variables are included in
.envfile
## for bcrypt ##
#extra string added to passwords before hashing
BCRYPT_PASSWORD=secretBcryptPass
# number of times password will be hashed
SALT_ROUNDS=10SALT_ROUNDSis the number of time the password will be hashed.BCYPT_PASSWORDis the extra string used in the peppering step.
git add .
git commit -m 'chore: Add bcypt to project'Add docker
The application will use Postgres run in a docker container. To do that we need docker
1. Install docker
- docker compose is installed with
docker
This docker-compose.yaml file will configure the container we are running.
touch docker-compose.yamlAdd content:
services:
postgres:
image: postgres
ports:
- '5555:5432'
env_file:
- .env
volumes:
- 'postgres:/var/lib/postgresql/data'
volumes:
postgres:npm run lintgit add .
git commit -m 'chore: Add docker-compose.yml file to project'