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
Postgres
database - 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.ts
Initiate node app
Install node if needed. This will also install npm
After installation initiate node application:
npm init -y
Setup GIT repository
- Install git if needed
- Initiate a git repository
git init
- Create
.gitignore
file
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 master
Add 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"
},
build
will convert typescript to javascript and store the files under the./dist
folder (set as theoutdir
intsconfig.json
)start
will run build and start the application by usingnode
to run the./dist/server.js
file- Running
npm start
logs "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-prettier
In 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
}
}
' > .eslintrc
echo '{
"semi": true,
"singleQuote": true
}' > .prettierrc
In 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 lint
to run both prettier and eslin. Prettier will automatically correct mistakes while eslint will show a list of errors and warnings.
npm run lint
git 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-watch
tsc-watch
will 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 start
logs:Server started at: localhost:3000
. - opening the browser at
localhost:3000
the page displays:Server is working.
npm run lint
git 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 lint
git 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/utilities
Run:
npx jasmine init
- Creates
spec
directory andjasmine.json
configuration 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.ts
With 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.ts
With 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 test
builds the project and succesfully runs the one spec we created
npm run lint
git 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 test
will showmorgan
logging the http request of our spec
npm run lint
git 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-parser
To 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 lint
git 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 .env
With 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
.env
file - we will set up the test database manually
- the
ENV
variable to set the current environment (development or test)
It will keep sensitive information local.
echo ".env" >> .gitignore
Store you environment variables in the .env
file:
YOUR_VARIABLE=myvariable
If 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.ts
With 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 lint
git 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-migrate
globally (-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.json
file 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.json
file 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 lint
git 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/jsonwebtoken
TOKEN_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
.env
file
## for bcrypt ##
#extra string added to passwords before hashing
BCRYPT_PASSWORD=secretBcryptPass
# number of times password will be hashed
SALT_ROUNDS=10
SALT_ROUNDS
is the number of time the password will be hashed.BCYPT_PASSWORD
is 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.yaml
Add content:
services:
postgres:
image: postgres
ports:
- '5555:5432'
env_file:
- .env
volumes:
- 'postgres:/var/lib/postgresql/data'
volumes:
postgres:
npm run lint
git add .
git commit -m 'chore: Add docker-compose.yml file to project'