JavaScript joelf Node.js Okta sponsored Tech

How to Use TypeScript to Build a Node API with Express — SitePoint

Guitar Inventory Demo

Prefer it or not, JavaScript has been serving to builders energy the Web since 1995. In that point, JavaScript utilization has grown from small consumer expertise enhancements to complicated full-stack purposes utilizing Node.js on the server and considered one of many frameworks on the shopper similar to Angular, React, or Vue.

Right now, constructing JavaScript purposes at scale stays a problem. Increasingly groups are turning to TypeScript to complement their JavaScript tasks.

Node.js server purposes can profit from utilizing TypeScript, as properly. The aim of this tutorial is to present you ways to construct a new Node.js software utilizing TypeScript and Express.

The Case for TypeScript

As a net developer, I way back stopped resisting JavaScript, and have grown to recognize its flexibility and ubiquity. Language options added to ES2015 and past have considerably improved its utility and lowered widespread frustrations of writing purposes.

Nevertheless, bigger JavaScript tasks demand instruments corresponding to ESLint to catch widespread errors, and larger self-discipline to saturate the code base with helpful exams. As with any software program undertaking, a wholesome staff tradition that features a peer assessment course of can enhance high quality and guard towards points that may creep into a undertaking.

The first advantages of utilizing TypeScript are to catch extra errors earlier than they go into manufacturing and make it simpler to work with your code base.

TypeScript is just not a totally different language. It’s a versatile superset of JavaScript with methods to describe elective knowledge varieties. All “standard” and legitimate JavaScript can also be legitimate TypeScript. You’ll be able to dial in as a lot or little as you want.

As quickly as you add the TypeScript compiler or a TypeScript plugin to your favourite code editor, there are instant security and productiveness advantages. TypeScript can provide you with a warning to misspelled features and properties, detect passing the improper forms of arguments or the incorrect variety of arguments to features, and supply smarter autocomplete recommendations.

Build a Guitar Stock Software with TypeScript and Node.js

Amongst guitar gamers, there’s a joke everybody ought to perceive.

Q: “How many guitars do you need?”

A: “n + 1. Always one more.”

On this tutorial, you’re going to create a new Node.js software to hold monitor of a listing of guitars. In a nutshell, this tutorial makes use of Node.js with Express, EJS, and PostgreSQL on the backend, Vue, Materialize, and Axios on the frontend, Okta for account registration and authorization, and TypeScript to govern the JavaScripts!

Create Your Node.js Challenge

Open up a terminal (Mac/Linux) or a command immediate (Home windows) and sort the next command:

node –version

Should you get an error, or the model of Node.js you’ve is lower than model eight, you’ll want to set up Node.js. On Mac or Linux, I like to recommend you first set up nvm and use nvm to set up Node.js. On Home windows, I like to recommend you employ Chocolatey.

After making certain you’ve a current model of Node.js put in, create a folder in your undertaking.

mkdir guitar-inventory
cd guitar-inventory

Use npm to initialize a package deal.json file.

npm init -y

Howdy, world!

On this pattern software, Express is used to serve net pages and implement an API. Dependencies are put in utilizing npm. Add Express to your challenge with the next command.

npm set up categorical

Subsequent, open the venture in your editor of selection.

Should you don’t have already got a favourite code editor, I exploit and advocate Visible Studio Code. VS Code has distinctive help for JavaScript and Node.js, resembling sensible code completion and debugging, and there’s a huge library of free extensions contributed by the group.

Create a folder named src. On this folder, create a file named index.js. Open the file and add the next JavaScript.

const categorical = require( “express” );
const app = categorical();
const port = 8080; // default port to pay attention

// outline a route handler for the default house web page
app.get( “/”, ( req, res ) =>
res.ship( “Hello world!” );
);

// begin the Express server
app.pay attention( port, () =>
console.log( `server began at http://localhost:$ port ` );
);

Subsequent, replace package deal.json to instruct npm on how to run your software. Change the primary property worth to level to src/index.js, and add a begin script to the scripts object.

“main”: “src/index.js”,
“scripts”:
“start”: “node .”,
“test”: “echo “Error: no check specified” && exit 1″
,

Now, from the terminal or command line, you possibly can launch the appliance.

npm run begin

If all goes nicely, you need to see this message written to the console.

server began at http://localhost:8080

Launch your browser and navigate to http://localhost:8080. You need to see the textual content “Hello world!”

Hello World

Notice: To cease the online software, you’ll be able to return to the terminal or command immediate and press CTRL+C.

Set Up Your Node.js Venture to Use TypeScript

Step one is to add the TypeScript compiler. You possibly can set up the compiler as a developer dependency utilizing the –save-dev flag.

npm set up –save-dev typescript

The subsequent step is to add a tsconfig.json file. This file instructs TypeScript how to compile (transpile) your TypeScript code into plain JavaScript.

Create a file named tsconfig.json within the root folder of your venture, and add the next configuration.


“compilerOptions”:
“module”: “commonjs”,
“esModuleInterop”: true,
“target”: “es6”,
“noImplicitAny”: true,
“moduleResolution”: “node”,
“sourceMap”: true,
“outDir”: “dist”,
“baseUrl”: “.”,
“paths”:
“*”: [
“node_modules/*”
]

,
“include”: [
“src/**/*”
]

Based mostly on this tsconfig.json file, the TypeScript compiler will (try to) compile any information ending with .ts it finds within the src folder, and retailer the leads to a folder named dist. Node.js makes use of the CommonJS module system, so the worth for the module setting is commonjs. Additionally, the goal model of JavaScript is ES6 (ES2015), which is suitable with trendy variations of Node.js.

It’s additionally a nice concept to add tslint and create a tslint.json file that instructs TypeScript how to lint your code. In case you’re not acquainted with linting, it’s a code evaluation software to provide you with a warning to potential issues in your code past syntax points.

Set up tslint as a developer dependency.

npm set up –save-dev typescript tslint

Subsequent, create a new file within the root folder named tslint.json file and add the next configuration.


“defaultSeverity”: “error”,
“extends”: [
“tslint:recommended”
],
“jsRules”: ,
“rules”:
“trailing-comma”: [ false ]
,
“rulesDirectory”: []

Subsequent, replace your package deal.json to change foremost to level to the brand new dist folder created by the TypeScript compiler. Additionally, add a couple of scripts to execute TSLint and the TypeScript compiler simply earlier than beginning the Node.js server.

“main”: “dist/index.js”,
“scripts”:
“prebuild”: “tslint -c tslint.json -p tsconfig.json –fix”,
“build”: “tsc”,
“prestart”: “npm run build”,
“start”: “node .”,
“test”: “echo “Error: no check specified” && exit 1″
,

Lastly, change the extension of the src/index.js file from .js to .ts, the TypeScript extension, and run the beginning script.

npm run begin

Observe: You’ll be able to run TSLint and the TypeScript compiler with out beginning the Node.js server utilizing npm run construct.

TypeScript errors

Oh no! Instantly, you may even see some errors logged to the console like these.

ERROR: /Customers/reverentgeek/Tasks/guitar-inventory/src/index.ts[12, 5]: Calls to ‘console.log’ will not be allowed.

src/index.ts:1:17 – error TS2580: Can’t discover identify ‘require’. Do you want to set up sort definitions for node? Attempt `npm i @varieties/node`.

1 const categorical = require( “express” );
~~~~~~~

src/index.ts:6:17 – error TS7006: Parameter ‘req’ implicitly has an ‘any’ sort.

6 app.get( “/”, ( req, res ) =>
~~~

The 2 commonest errors you may even see are syntax errors and lacking sort info. TSLint considers utilizing console.log to be a problem for manufacturing code. One of the best answer is to exchange makes use of of console.log with a logging framework akin to winston. For now, add the next remark to src/index.ts to disable the rule.

app.pay attention( port, () =>
// tslint:disable-next-line:no-console
console.log( `server began at http://localhost:$ port ` );
);

TypeScript prefers to use the import module syntax over require, so that you’ll begin by altering the primary line in src/index.ts from:

const categorical = require( “express” );

to:

import categorical from “express”;

Getting the best varieties

To help TypeScript builders, library authors and group contributors publish companion libraries referred to as TypeScript declaration information. Declaration information are revealed to the DefinitelyTyped open supply repository, or typically discovered within the unique JavaScript library itself.

Replace your undertaking in order that TypeScript can use the sort declarations for Node.js and Express.

npm set up –save-dev @varieties/node @varieties/categorical

Subsequent, rerun the beginning script and confirm there are not any extra errors.

npm run begin

Build a Higher Consumer Interface with Materialize and EJS

Your Node.js software is off to a nice begin, however maybe not the most effective wanting, but. This step provides Materialize, a trendy CSS framework based mostly on Google’s Materials Design, and Embedded JavaScript Templates (EJS), an HTML template language for Express. Materialize and EJS are a good basis for a a lot better UI.

First, set up EJS as a dependency.

npm set up ejs

Subsequent, make a new folder beneath /src named views. Within the /src/views folder, create a file named index.ejs. Add the next code to /src/views/index.ejs.

<!DOCTYPE html>
<html>
<head>
<meta charset=”utf-8″ />
<meta http-equiv=”X-UA-Compatible” content material=”IE=edge”>
<title>Guitar Stock</title>
<meta identify=”viewport” content material=”width=device-width, initial-scale=1″>
<hyperlink rel=”stylesheet” href=”https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css”>
<hyperlink rel=”stylesheet” href=”https://fonts.googleapis.com/icon?family=Material+Icons”>
</head>
<physique>
<div class=”container”>
<h1 class=”header”>Guitar Stock</h1>
<a class=”btn” href=”http://www.sitepoint.com/guitars”><i class=”material-icons right”>arrow_forward</i>Get began!</a>
</div>
</physique>
</html>

Replace /src/index.ts with the next code.

import categorical from “express”;
import path from “path”;
const app = categorical();
const port = 8080; // default port to pay attention

// Configure Express to use EJS
app.set( “views”, path.be a part of( __dirname, “views” ) );
app.set( “view engine”, “ejs” );

// outline a route handler for the default house web page
app.get( “/”, ( req, res ) =>
// render the index template
res.render( “index” );
);

// begin the categorical server
app.pay attention( port, () =>
// tslint:disable-next-line:no-console
console.log( `server began at http://localhost:$ port ` );
);

Add an asset construct script for Typescript

The TypeScript compiler does the work of producing the JavaScript information and copies them to the dist folder. Nevertheless, it doesn’t copy the opposite varieties of information the venture wants to run, such because the EJS view templates. To perform this, create a construct script that copies all the opposite information to the dist folder.

Set up the wanted modules and TypeScript declarations utilizing these instructions.

npm set up –save-dev ts-node shelljs fs-extra nodemon rimraf npm-run-all
npm set up –save-dev @varieties/fs-extra @varieties/shelljs

Right here is a fast overview of the modules you simply put in.

  1. ts-node. Use to run TypeScript information instantly.
  2. shelljs. Use to execute shell instructions reminiscent of to copy information and take away directories.
  3. fs-extra. A module that extends the Node.js file system (fs) module with options reminiscent of studying and writing JSON information.
  4. rimraf. Use to recursively take away folders.
  5. npm-run-all. Use to execute a number of npm scripts sequentially or in parallel.
  6. nodemon. A useful device for operating Node.js in a improvement setting. Nodemon watches information for modifications and routinely restarts the Node.js software when modifications are detected. No extra stopping and restarting Node.js!

Make a new folder within the root of the undertaking named instruments. Create a file within the instruments folder named copyAssets.ts. Copy the next code into this file.

import * as shell from “shelljs”;

// Copy all of the view templates
shell.cp( “-R”, “src/views”, “dist/” );

Replace npm scripts

Replace the scripts in package deal.json to the next code.

“scripts”:
“clean”: “rimraf dist/*”,
“copy-assets”: “ts-node tools/copyAssets”,
“lint”: “tslint -c tslint.json -p tsconfig.json –fix”,
“tsc”: “tsc”,
“build”: “npm-run-all clean lint tsc copy-assets”,
“dev:start”: “npm-run-all build start”,
“dev”: “nodemon –watch src -e ts,ejs –exec npm run dev:start”,
“start”: “node .”,
“test”: “echo “Error: no check specified” && exit 1″
,

Observe: In case you are not acquainted with utilizing npm scripts, they are often very highly effective and helpful to any Node.js undertaking. Scripts may be chained collectively in a number of methods. A method to chain scripts collectively is to use the pre and publish prefixes. For instance, when you’ve got one script labeled begin and one other labeled prestart, executing npm run begin on the terminal will first run prestart, and solely after it efficiently finishes does begin run.

Now run the appliance and navigate to http://localhost:8080.

npm run dev

Guitar Inventory home page

The house web page is beginning to look higher! In fact, the Get Began button leads to a disappointing error message. No worries! The repair for that’s coming quickly!

A Higher Approach to Handle Configuration Settings in Node.js

Node.js purposes sometimes use setting variables for configuration. Nevertheless, managing setting variables could be a chore. A well-liked module for managing software configuration knowledge is dotenv.

Set up dotenv as a undertaking dependency.

npm set up dotenv
npm set up –save-dev @varieties/dotenv

Create a file named .env within the root folder of the undertaking, and add the next code.

# Set to manufacturing when deploying to manufacturing
NODE_ENV=improvement

# Node.js server configuration
SERVER_PORT=8080

Notice: When utilizing a supply management system comparable to git, don’t add the .env file to supply management. Every setting requires a customized .env file. It is strongly recommended you doc the values anticipated within the .env file within the venture README or a separate .env.pattern file.

Now, replace src/index.ts to use dotenv to configure the appliance server port worth.

import dotenv from “dotenv”;
import categorical from “express”;
import path from “path”;

// initialize configuration
dotenv.config();

// port is now out there to the Node.js runtime
// as if it have been an setting variable
const port = course of.env.SERVER_PORT;

const app = categorical();

// Configure Express to use EJS
app.set( “views”, path.be a part of( __dirname, “views” ) );
app.set( “view engine”, “ejs” );

// outline a route handler for the default house web page
app.get( “/”, ( req, res ) =>
// render the index template
res.render( “index” );
);

// begin the categorical server
app.pay attention( port, () =>
// tslint:disable-next-line:no-console
console.log( `server began at http://localhost:$ port ` );
);

You’ll use the .env for far more configuration info because the venture grows.

Simply Add Authentication to Node and Express

Including consumer registration and login (authentication) to any software is just not a trivial process. The excellent news is Okta makes this step very straightforward. To start, create a free developer account with Okta. First, navigate to developer.okta.com and click on the Create Free Account button, or click on the Signal Up button.

Sign up for free account

After creating your account, click on the Purposes hyperlink on the prime, after which click on Add Software.

Create application

Subsequent, select a Net Software and click on Subsequent.

Create a web application

Enter a identify in your software, similar to Guitar Stock. Confirm the port quantity is identical as configured in your native net software. Then, click on Achieved to end creating the appliance.

Application settings

Copy and paste the next code into your .env file.

# Okta configuration
OKTA_ORG_URL=https://yourOktaDomain
OKTA_CLIENT_ID=yourClientId
OKTA_CLIENT_SECRET=yourClientSecret

Within the Okta software console, click on in your new software’s Common tab, and discover close to the underside of the web page a part titled “Client Credentials.” Copy the Shopper ID and Shopper secret values and paste them into your .env file to exchange yourClientId and yourClientSecret, respectively.

Client credentials

Allow self-service registration

One of many nice options of Okta is permitting customers of your software to join an account. By default, this function is disabled, however you possibly can simply allow it. First, click on on the Customers menu and choose Registration.

User registration

  1. Click on on the Edit button.
  2. Change Self-service registration to Enabled.
  3. Click on the Save button on the backside of the shape.

Enable self-registration

Safe your Node.js software

The final step to securing your Node.js software is to configure Express to use the Okta OpenId Join (OIDC) middleware.

npm set up @okta/oidc-middleware express-session
npm set up –save-dev @varieties/express-session

Subsequent, replace your .env file to add a HOST_URL and SESSION_SECRET worth. You could change the SESSION_SECRET worth to any string you would like.

# Node.js server configuration
SERVER_PORT=8080
HOST_URL=http://localhost:8080
SESSION_SECRET=MySuperCoolAndAwesomeSecretForSigningSessionCookies

Create a folder beneath src named middleware. Add a file to the src/middleware folder named sessionAuth.ts. Add the next code to src/middleware/sessionAuth.ts.

import ExpressOIDC from “@okta/oidc-middleware”;
import session from “express-session”;

export const register = ( app: any ) =>
// Create the OIDC shopper
const oidc = new ExpressOIDC(
client_id: course of.env.OKTA_CLIENT_ID,
client_secret: course of.env.OKTA_CLIENT_SECRET,
issuer: `$ course of.env.OKTA_ORG_URL /oauth2/default`,
redirect_uri: `$ course of.env.HOST_URL /authorization-code/callback`,
scope: “openid profile”
);

// Configure Express to use authentication periods
app.use( session(
resave: true,
saveUninitialized: false,
secret: course of.env.SESSION_SECRET
) );

// Configure Express to use the OIDC shopper router
app.use( oidc.router );

// add the OIDC shopper to the app.locals
app.locals.oidc = oidc;
;

At this level, in case you are utilizing a code editor like VS Code, you may even see TypeScript complaining concerning the @okta/oidc-middleware module. On the time of this writing, this module doesn’t but have an official TypeScript declaration file. For now, create a file within the src folder named international.d.ts and add the next code.

declare module “@okta/oidc-middleware”;

Refactor routes

As the appliance grows, you’ll add many extra routes. It’s a good concept to outline all of the routes in a single space of the undertaking. Make a new folder beneath src named routes. Add a new file to src/routes named index.ts. Then, add the next code to this new file.

import * as categorical from “express”;

export const register = ( app: categorical.Software ) =>
const oidc = app.locals.oidc;

// outline a route handler for the default house web page
app.get( “/”, ( req: any, res ) =>
res.render( “index” );
);

// outline a safe route handler for the login web page that redirects to /guitars
app.get( “/login”, oidc.ensureAuthenticated(), ( req, res ) =>
res.redirect( “/guitars” );
);

// outline a route to deal with logout
app.get( “/logout”, ( req: any, res ) =>
req.logout();
res.redirect( “/” );
);

// outline a safe route handler for the guitars web page
app.get( “/guitars”, oidc.ensureAuthenticated(), ( req: any, res ) =>
res.render( “guitars” );
);
;

Subsequent, replace src/index.ts to use the sessionAuth and routes modules you created.

import dotenv from “dotenv”;
import categorical from “express”;
import path from “path”;
import * as sessionAuth from “./middleware/sessionAuth”;
import * as routes from “./routes”;

// initialize configuration
dotenv.config();

// port is now obtainable to the Node.js runtime
// as if it have been an surroundings variable
const port = course of.env.SERVER_PORT;

const app = categorical();

// Configure Express to use EJS
app.set( “views”, path.be a part of( __dirname, “views” ) );
app.set( “view engine”, “ejs” );

// Configure session auth
sessionAuth.register( app );

// Configure routes
routes.register( app );

// begin the categorical server
app.pay attention( port, () =>
// tslint:disable-next-line:no-console
console.log( `server began at http://localhost:$ port ` );
);

Subsequent, create a new file for the guitar record view template at src/views/guitars.ejs and enter the next HTML.

<!DOCTYPE html>
<html>
<head>
<meta charset=”utf-8″ />
<meta http-equiv=”X-UA-Compatible” content material=”IE=edge”>
<title>Guitar Stock</title>
<meta identify=”viewport” content material=”width=device-width, initial-scale=1″>
<hyperlink rel=”stylesheet” href=”https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css”>
<hyperlink rel=”stylesheet” href=”https://fonts.googleapis.com/icon?family=Material+Icons”>
</head>
<physique>
<div class=”container”>
<h1 class=”header”>Guitar Stock</h1>
<p>Your future listing of guitars!</p>
</div>
</physique>
</html>

Lastly, run the appliance.

npm run dev

Notice: To confirm authentication is working as anticipated, open a new browser or use a personal/incognito browser window.

Click on the Get Began button. If every thing goes properly, log in with your Okta account, and Okta ought to mechanically redirect you again to the “Guitar List” web page!

Okta login

With authentication working, you’ll be able to reap the benefits of the consumer profile info returned from Okta. The OIDC middleware routinely attaches a userContext object and an isAuthenticated() perform to each request. This userContext has a userinfo property that incorporates info that appears like the next object.


sub: ’00abc12defg3hij4k5l6′,
identify: ‘First Final’,
locale: ‘en-US’,
preferred_username: ‘account@firm.com’,
given_name: ‘First’,
family_name: ‘Final’,
zoneinfo: ‘America/Los_Angeles’,
updated_at: 1539283620

Step one is get the consumer profile object and cross it to the views as knowledge. Replace the src/routes/index.ts with the next code.

import * as categorical from “express”;

export const register = ( app: categorical.Software ) =>
const oidc = app.locals.oidc;

// outline a route handler for the default residence web page
app.get( “/”, ( req: any, res ) =>
const consumer = req.userContext ? req.userContext.userinfo : null;
res.render( “index”, isAuthenticated: req.isAuthenticated(), consumer );
);

// outline a safe route handler for the login web page that redirects to /guitars
app.get( “/login”, oidc.ensureAuthenticated(), ( req, res ) =>
res.redirect( “/guitars” );
);

// outline a route to deal with logout
app.get( “/logout”, ( req: any, res ) =>
req.logout();
res.redirect( “/” );
);

// outline a safe route handler for the guitars web page
app.get( “/guitars”, oidc.ensureAuthenticated(), ( req: any, res ) =>
const consumer = req.userContext ? req.userContext.userinfo : null;
res.render( “guitars”, isAuthenticated: req.isAuthenticated(), consumer );
);
;

Make a new folder beneath src/views named partials. Create a new file on this folder named nav.ejs. Add the next code to src/views/partials/nav.ejs.

<nav>
<div class=”nav-wrapper”>
<a href=”/” class=”brand-logo”><% if ( consumer ) %><%= consumer.identify %>’s <% %>Guitar Stock</a>
<ul id=”nav-mobile” class=”right hide-on-med-and-down”>
<li><a href=”http://www.sitepoint.com/guitars”>My Guitars</a></li>
<% if ( isAuthenticated ) %>
<li><a href=”http://www.sitepoint.com/logout”>Logout</a></li>
<% %>
<% if ( !isAuthenticated ) %>
<li><a href=”http://www.sitepoint.com/login”>Login</a></li>
<% %>
</ul>
</div>
</nav>

Modify the src/views/index.ejs and src/views/guitars.ejs information. Instantly following the <physique> tag, insert the next code.

<physique>
<% embrace partials/nav %>

With these modifications in place, your software now has a navigation menu on the prime that modifications based mostly on the login standing of the consumer.

Navigation

Create an API with Node and PostgreSQL

The subsequent step is to add the API to the Guitar Stock software. Nevertheless, earlier than shifting on, you want a method to retailer knowledge.

Create a PostgreSQL database

This tutorial makes use of PostgreSQL. To make issues simpler, use Docker to arrange an occasion of PostgreSQL. In case you don’t have already got Docker put in, you’ll be able to comply with the set up information.

After you have Docker put in, run the next command to obtain the newest PostgreSQL container.

docker pull postgres:newest

Now, run this command to create an occasion of a PostgreSQL database server. Be happy to change the administrator password worth.

docker run -d –name guitar-db -p 5432:5432 -e ‘POSTGRES_PASSWORD=p@ssw0rd42’ postgres

Observe: If you have already got PostgreSQL put in regionally, you will want to change the -p parameter to map port 5432 to a totally different port that doesn’t battle with your present occasion of PostgreSQL.

Right here is a fast rationalization of the earlier Docker parameters.

  • -d – This launches the container in daemon mode, so it runs within the background.
  • -name – This provides your Docker container a pleasant identify, which is beneficial for stopping and beginning containers.
  • -p – This maps the host (your pc) port 5432 to the container’s port 5432. PostgreSQL, by default, listens for connections on TCP port 5432.
  • -e – This units an surroundings variable within the container. On this instance, the administrator password is p@ssw0rd42. You’ll be able to change this worth to any password you want.
  • postgres – This last parameter tells Docker to use the postgres picture.

Observe: For those who restart your pc, may have to restart the Docker container. You are able to do that utilizing the docker begin guitar-db command.

Set up the PostgreSQL shopper module and sort declarations utilizing the next instructions.

npm set up pg pg-promise
npm set up –save-dev @varieties/pg

Database configuration settings

Add the next settings to the top of the .env file.

# Postgres configuration
PGHOST=localhost
PGUSER=postgres
PGDATABASE=postgres
PGPASSWORD=p@ssw0rd42
PGPORT=5432

Notice: For those who modified the database administrator password, be certain to exchange the default p@ssw0rd42 with that password on this file.

Add a database construct script

You want a construct script to initialize the PostgreSQL database. This script ought to learn in a .pgsql file and execute the SQL instructions towards the native database.

Within the instruments folder, create two information: initdb.ts and initdb.pgsql. Copy and paste the next code into initdb.ts.

import dotenv from “dotenv”;
import fs from “fs-extra”;
import Shopper from “pg”;

const init = async () =>
// learn setting variables
dotenv.config();
// create an occasion of the PostgreSQL shopper
const shopper = new Shopper();
attempt
// join to the native database server
await shopper.join();
// learn the contents of the initdb.pgsql file
const sql = await fs.readFile( “./tools/initdb.pgsql”, encoding: “UTF-8” );
// cut up the file into separate statements
const statements = sql.cut up( /;s*$/m );
for ( const assertion of statements )
if ( assertion.size > three )
// execute every of the statements
await shopper.question( assertion );


catch ( err )
console.log( err );
throw err;
lastly
// shut the database shopper
await shopper.finish();

;

init().then( () =>
console.log( “finished” );
).catch( () =>
console.log( “finished with errors” );
);

Subsequent, copy and paste the next code into initdb.pgsql.

— Drops guitars desk
DROP TABLE IF EXISTS guitars;

— Creates guitars desk
CREATE TABLE IF NOT EXISTS guitars (
id INT NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY
, user_id varchar(50) NOT NULL
, model varchar(50) NOT NULL
, mannequin varchar(50) NOT NULL
, yr smallint NULL
, shade varchar(50) NULL
);

Subsequent, add a new script to package deal.json.

“initdb”: “ts-node tools/initdb”,

Now, go to the terminal and run the brand new script.

npm run initdb

You need to see the message completed on the console. A brand new desk named guitars is now in your database! Any time you need to reset your database, simply rerun the script.

Add API routes in Node.js

To finish the API, you want to add new routes to Express to create, question, replace, and delete guitars. First, create a new file beneath src/routes named api.ts. Add the next code to this file.

import * as categorical from “express”;
import pgPromise from “pg-promise”;

export const register = ( app: categorical.Software ) =>
const oidc = app.locals.oidc;
const port = parseInt( course of.env.PGPORT || “5432”, 10 );
const config =
database: course of.env.PGDATABASE || “postgres”,
host: course of.env.PGHOST || “localhost”,
port,
consumer: course of.env.PGUSER || “postgres”
;

const pgp = pgPromise();
const db = pgp( config );

app.get( `/api/guitars/all`, oidc.ensureAuthenticated(), async ( req: any, res ) =>
attempt
const userId = req.userContext.userinfo.sub;
const guitars = await db.any( `
SELECT
id
, model
, mannequin
, yr
, shade
FROM guitars
WHERE user_id = $[userId]
ORDER BY yr, model, mannequin`, userId );
return res.json( guitars );
catch ( err )
// tslint:disable-next-line:no-console
console.error(err);
res.json( );

);

app.get( `/api/guitars/complete`, oidc.ensureAuthenticated(), async ( req: any, res ) =>
attempt
const userId = req.userContext.userinfo.sub;
const complete = await db.one( `
SELECT rely“standard” AS complete
FROM guitars
WHERE user_id = $[userId]`, userId , ( knowledge: complete: quantity ) =>
return
complete: +knowledge.complete
;
);
return res.json( complete );
catch ( err )
// tslint:disable-next-line:no-console
console.error(err);
res.json( );

);

app.get( `/api/guitars/discover/:search`, oidc.ensureAuthenticated(), async ( req: any, res ) =>
attempt
const userId = req.userContext.userinfo.sub;
const guitars = await db.any( `
SELECT
id
, model
, mannequin
, yr
, colour
FROM guitars
WHERE user_id = $[userId]
AND ( model ILIKE $[search] OR mannequin ILIKE $[search] )`,
userId, search: `%$ req.params.search %` );
return res.json( guitars );
catch ( err )
// tslint:disable-next-line:no-console
console.error(err);
res.json( );

);

app.submit( `/api/guitars/add`, oidc.ensureAuthenticated(), async ( req: any, res ) =>
attempt
const userId = req.userContext.userinfo.sub;
const id = await db.one( `
INSERT INTO guitars( user_id, model, mannequin, yr, shade )
VALUES( $[userId], $[brand], $[model], $[year], $[color] )
RETURNING id;`,
userId, …req.physique );
return res.json( id );
catch ( err )
// tslint:disable-next-line:no-console
console.error(err);
res.json( );

);

app.submit( `/api/guitars/replace`, oidc.ensureAuthenticated(), async ( req: any, res ) =>
attempt
const userId = req.userContext.userinfo.sub;
const id = await db.one( `
UPDATE guitars
SET model = $[brand]
, mannequin = $[model]
, yr = $[year]
, colour = $[color]
WHERE
id = $[id]
AND user_id = $[userId]
RETURNING
id;`,
userId, …req.physique );
return res.json( id );
catch ( err )
// tslint:disable-next-line:no-console
console.error(err);
res.json( );

);

app.delete( `/api/guitars/take away/:id`, oidc.ensureAuthenticated(), async ( req: any, res ) =>
attempt
const userId = req.userContext.userinfo.sub;
const id = await db.end result( `
DELETE
FROM guitars
WHERE user_id = $[userId]
AND id = $[id]`,
userId, id: req.params.id , ( r ) => r.rowCount );
return res.json( id );
catch ( err )
// tslint:disable-next-line:no-console
console.error(err);
res.json( );

);
;

Replace src/routes/index.ts to embrace the brand new api module.

import * as categorical from “express”;
import * as api from “./api”;

export const register = ( app: categorical.Software ) =>
const oidc = app.locals.oidc;

// outline a route handler for the default house web page
app.get( “/”, ( req: any, res ) =>
const consumer = req.userContext ? req.userContext.userinfo : null;
res.render( “index”, isAuthenticated: req.isAuthenticated(), consumer );
);

// outline a safe route handler for the login web page that redirects to /guitars
app.get( “/login”, oidc.ensureAuthenticated(), ( req, res ) =>
res.redirect( “/guitars” );
);

// outline a route to deal with logout
app.get( “/logout”, ( req: any, res ) =>
req.logout();
res.redirect( “/” );
);

// outline a safe route handler for the guitars web page
app.get( “/guitars”, oidc.ensureAuthenticated(), ( req: any, res ) =>
const consumer = req.userContext ? req.userContext.userinfo : null;
res.render( “guitars”, isAuthenticated: req.isAuthenticated(), consumer );
);

api.register( app );
;

Lastly, replace src/index.ts to add a new configuration choice instantly following the road to create the Express software. This code allows Express to parse incoming JSON knowledge.

const app = categorical();

// Configure Express to parse incoming JSON knowledge
app.use( categorical.json() );

Replace the Consumer Interface with Vue, Axios, and Parcel

The API is prepared. To finish the appliance, you want to add some code to the frontend to eat the API. You possibly can benefit from TypeScript with frontend code, as properly.

This remaining step of the venture makes use of Vue for frontend rendering, Axios for making HTTP calls to the backend API, and Parcel to each transpile TypeScript and bundle all of the dependencies collectively into a single JavaScript file.

First, set up new dependencies on the console utilizing the next instructions.

npm set up axios vue materialize-css
npm set up –save-dev parcel-bundler @varieties/axios @varieties/materialize-css @varieties/vue

Make a new folder beneath src named public. Make a new folder beneath src/public named js. Create a file underneath src/public/js named foremost.ts and add the next code.

import axios from “axios”;
import * as M from “materialize-css”;
import Vue from “vue”;

// tslint:disable-next-line no-unused-expression
new Vue(
computed:
hazGuitars(): boolean
return this.isLoading === false && this.guitars.size > zero;
,
noGuitars(): boolean
return this.isLoading === false && this.guitars.size === zero;

,
knowledge()
return
model: “”,
colour: “”,
guitars: [],
isLoading: true,
mannequin: “”,
selectedGuitar: “”,
selectedGuitarId: zero,
yr: “”
;
,
el: “#app”,
strategies:
addGuitar()
const guitar =
model: this.model,
colour: this.shade,
mannequin: this.mannequin,
yr: this.yr
;
axios
.submit( “/api/guitars/add”, guitar )
.then( () =>
this.$refs.yr.focus();
this.model = “”;
this.shade = “”;
this.mannequin = “”;
this.yr = “”;
this.loadGuitars();
)
.catch( ( err: any ) =>
// tslint:disable-next-line:no-console
console.log( err );
);
,
confirmDeleteGuitar( id: string )
const guitar = this.guitars.discover( ( g ) => g.id === id );
this.selectedGuitar = `$ guitar.yr $ guitar.model $ guitar.mannequin `;
this.selectedGuitarId = guitar.id;
const dc = this.$refs.deleteConfirm;
const modal = M.Modal.init( dc );
modal.open();
,
deleteGuitar( id: string )
axios
.delete( `/api/guitars/take away/$ id ` )
.then( this.loadGuitars )
.catch( ( err: any ) =>
// tslint:disable-next-line:no-console
console.log( err );
);
,
loadGuitars()
axios
.get( “/api/guitars/all” )
.then( ( res: any ) =>
this.isLoading = false;
this.guitars = res.knowledge;
)
.catch( ( err: any ) =>
// tslint:disable-next-line:no-console
console.log( err );
);

,
mounted()
return this.loadGuitars();

);

Replace tsconfig.json to exclude the src/public folder from the backend Node.js construct course of.


“compilerOptions”:
“module”: “commonjs”,
“esModuleInterop”: true,
“target”: “es6”,
“noImplicitAny”: true,
“moduleResolution”: “node”,
“sourceMap”: true,
“outDir”: “dist”,
“baseUrl”: “.”,
“paths”:
“*”: [
“node_modules/*”
]

,
“include”: [
“src/**/*”
],
“exclude”: [
“src/public”
]

Create a new tsconfig.json file beneath src/public/js and add the next code. This TypeScript configuration is to compile primary.ts to be used within the browser.


“compilerOptions”:
“lib”: [
“es6”,
“dom”
],
“noImplicitAny”: true,
“allowJs”: true,
“target”: “es5”,
“strict”: true,
“module”: “es6”,
“moduleResolution”: “node”,
“outDir”: “../../../dist/public/js”,
“sourceMap”: true

Subsequent, replace src/index.ts to configure Express to serve static information from the general public folder. Add this line after the code that configures Express to use EJS.


// Configure Express to use EJS
app.set( “views”, path.be a part of( __dirname, “views” ) );
app.set( “view engine”, “ejs” );

// Configure Express to serve static information within the public folder
app.use( categorical.static( path.be a part of( __dirname, “public” ) ) );

Replace src/views/guitars.ejs to add the Vue software template and a reference to the js/primary.js file.

<!DOCTYPE html>
<html>
<head>
<meta charset=”utf-8″ />
<meta http-equiv=”X-UA-Compatible” content material=”IE=edge”>
<title>Guitar Stock</title>
<meta identify=”viewport” content material=”width=device-width, initial-scale=1″>
<hyperlink rel=”stylesheet” href=”https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css”>
<hyperlink rel=”stylesheet” href=”https://fonts.googleapis.com/icon?family=Material+Icons”>
</head>
<physique>
<% embrace partials/nav %>
<div class=”container”>
<div id=”app”>
<div class=”row” id=”guitarList”>
<h3>Guitar record</h3>
<desk v-if=”hazGuitars”>
<thead>
<tr>
<th>Yr</th>
<th>Model</th>
<th>Mannequin</th>
<th>Colour</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for=”guitar in guitars”>
<td></td>
<td></td>
<td></td>
<td></td>
<td>
<button id=”guitarDelete” @click on=”confirmDeleteGuitar(guitar.id)” class=”btn-small”><i class=”material-icons right”>delete</i>Delete</button>
</td>
</tr>
</tbody>
</desk>
<p v-if=”noGuitars”>No guitars but!</p>
</div>
<div class=”row” id=”guitarEdit”>
<h3>Add a guitar</h3>
<type class=”col s12″ @submit.forestall=”addGuitar”>
<div class=”row”>
<div class=”input-field col s6″>
<enter v-model=”year” ref=”year” placeholder=”2005″ id=”year” sort=”text” class=”validate”>
<label for=”brand”>Yr</label>
</div>
<div class=”input-field col s6″>
<enter v-model=”brand” ref=”brand” placeholder=”Paul Reed Smith” id=”brand” sort=”text” class=”validate”>
<label for=”brand”>Model</label>
</div>
</div>
<div class=”row”>
<div class=”input-field col s6″>
<enter v-model=”model” ref=”model” placeholder=”Custom 24″ id=”model” sort=”text” class=”validate”>
<label for=”model”>Mannequin</label>
</div>
<div class=”input-field col s6″>
<enter v-model=”color” ref=”color” placeholder=”Whale Blue” id=”color” sort=”text” class=”validate”>
<label for=”model”>Shade</label>
</div>
</div>
<button id=”guitarEditSubmit” class=”btn” sort=”submit”><i class=”material-icons right”>ship</i>Submit</button>
</type>
</div>
<div id=”deleteConfirm” ref=”deleteConfirm” class=”modal”>
<div class=”modal-content”>
<h4>Affirm delete</h4>
<p>Delete ?</p>
</div>
<div class=”modal-footer”>
<button @click on=”deleteGuitar(selectedGuitarId)” class=”modal-close btn-flat”>Okay</button>
<button class=”modal-close btn-flat”>Cancel</button>
</div>
</div>
</div>
</div>
<script src=”http://www.sitepoint.com/js/main.js”></script></physique>
</html>

Lastly, replace package deal.json to add a new parcel script, replace the construct script, and add a new alias part for Vue. The alias part factors Parcel to the right Vue file to bundle with src/public/js/important.ts.

“scripts”:
“clean”: “rimraf dist/*”,
“copy-assets”: “ts-node tools/copyAssets”,
“lint”: “tslint -c tslint.json -p tsconfig.json –fix”,
“tsc”: “tsc”,
“parcel”: “parcel build src/public/js/main.ts -d dist/public/js”,
“build”: “npm-run-all clean lint tsc copy-assets parcel”,
“dev:start”: “npm-run-all build start”,
“dev”: “nodemon –watch src -e ts,ejs –exec npm run dev:start”,
“start”: “node .”,
“initdb”: “ts-node tools/initdb”,
“test”: “echo “Error: no check specified” && exit 1″
,
“alias”:
“vue”: “./node_modules/vue/dist/vue.common.js”
,

Now, restart the construct and take your new net software for a spin!

npm run dev

Guitar Inventory

Study Extra About Node and TypeScript

This tutorial solely scratches the floor of what you are able to do with Node.js and TypeScript. Under are extra assets to discover.

You will discover the finished Guitar Stock undertaking on GitHub.

Comply with us for extra nice content material and updates from our workforce! You’ll find us on Twitter, Fb, and LinkedIn. Questions? Hit us up within the feedback under.