Hapi.js (21.3.2) using TypeScript
Hapi.js (21.3.2) using TypeScript

I got to know about hapi.js (node.js web framework), so i tried to made rest api’s using it with typescript and database as postgresql and for query builder I use knex.js. I am using the hapi.js version 21.3.2
Before Hapi.js I worked on Express, there is similarity between both are web framework of node.js
So in Hapi.js using typescript create tsconfig file, first you have to check for typescript is installed in user system. You can check using tsc -v. If there is typescript installed in your sytem then run the below command.
npx tsc --init
after that changing the path in tsconfig.json, you can give path according to yours. “rootDir”: “./src” “outDir”: “./dist”
npm init -y
through the above command it will create a package.json file where all the package name resides and some scripts like to run the application or to run the migration.
Now we have to install packages like
npm i @hapi/hapi bcrypt dotenv joi jsonwebtoken knex pg uuid
@hapi/hapi : Node.js web framework bcrypt : Used for encrypt the data using hashing technique dotenv : Used to read the variable defined in .env files joi : Used to add validation to req parameters types. jsonwebtoken : Used for Token Management knex : Used for connecting sql database, building query etc. pg : Used for postgresql database models uuid : Used for generating unique identification.
There are some dev dependencies for our application.
npm i -D @types/bcrypt @types/jsonwebtoken @types/node @types/pg @types/uuid nodemon typescript
first thing is we have to create a file with .ts extension in src folder, we can name them as index.ts, server.ts or app.ts as they are entry level file.
/**
* File Name : index.ts
*/
"use strict";
import * as dotenv from "dotenv";
dotenv.config();
import knex from "./db";
import Hapi, { Server } from "@hapi/hapi";
import userRoutes from "./routes/user";
export let server: Server;
export const init = async function (): Promise<Server> {
server = Hapi.server({
port: process.env.PORT || 4000,
host: "localhost",
routes: {
cors: {
credentials: true,
},
},
});
// Add Middleware
server.ext('onRequest', (request, h) => {
console.log('Middleware executed');
return h.continue;
});
// Routes
server.route(userRoutes);
return server;
};
export const start = async function (): Promise<void> {
console.log(`Listening on ${server.settings.host}:${server.settings.port}`);
server.decorate("request", "database", knex);
server.start();
};
process.on("unhandledRejection", (err) => {
console.error("unhandledRejection");
console.error(err);
process.exit(1);
});
init()
.then(() => start())
.catch((err) => console.error("Error While Starting the server",err));
Basically the above code is a boilerplate for hapi.js for creating a server and we can add routes I made the routes folder in that made a user.ts file.
/**
* File Name : routes/users.ts
*/
import { ServerRoute } from "@hapi/hapi";
import Joi from "joi";
import {
doSignUp,
doSignIn,
fetchUserList,
} from "../controllers/userController";
const userRoutes: ServerRoute[] = [
{
method: "POST",
path: "/signup",
handler: doSignUp,
options: {
validate: {
payload: Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string()
.pattern(new RegExp("^[a-zA-Z0-9]{3,30}$"))
.required(),
}),
},
},
},
{
method: "POST",
path: "/signin",
handler: doSignIn,
options: {
validate: {
payload: Joi.object({
username: Joi.string().required(),
password: Joi.string().required(),
}),
},
},
},
{
method: "GET",
path: "/userlist",
handler: fetchUserList,
},
];
export default userRoutes;
/**
* File Name : controllers/userController.ts
*/
import { Request, ResponseToolkit, ResponseObject } from "@hapi/hapi";
import UserRepository from "../repository/userRepository";
import { compareHash, generateSalt, generateHash } from "../utils/bcrypt";
import { createToken, validateToken } from "../utils/jwt";
interface IUser {
username: string;
password: string;
}
const userRepository = new UserRepository();
export async function doSignUp(
request: Request,
h: ResponseToolkit
): Promise<ResponseObject> {
try {
const { username, password } = <IUser>request.payload;
const isExistingUser = await userRepository.findByUsername(username);
if (isExistingUser) {
return h.response("Username Already Taken").code(400);
} else {
const salt = generateSalt(10);
const hash = generateHash(password, salt);
const newUser = await userRepository.create(username, hash);
const token = createToken(newUser.id);
return h
.response({
data: newUser,
message: "User Registered SuccessFully",
status: 201,
})
.code(201)
.header(
"Set-Cookie",
`access_token=${token}; isHttpOnly=false; Path=/`
);
}
} catch (e) {
return h
.response({ message: "Oops!! Something Went Wrong", status: 500 })
.code(500);
}
}
export async function doSignIn(
request: Request,
h: ResponseToolkit
): Promise<ResponseObject> {
try {
const { username, password } = <IUser>request.payload;
const ExistingUser = await userRepository.findByUsername(username);
if (!ExistingUser) {
return h
.response({ message: "Username Not Exists", status: 400 })
.code(400);
} else {
const isMatch = compareHash(password, ExistingUser.password);
if (!isMatch) {
return h
.response({ message: "Password Does Not Match", status: 400 })
.code(400);
}
const token = createToken(ExistingUser.username);
return h
.response({
data: ExistingUser,
message: "User SignIn SuccessFully",
status: 200,
})
.code(200)
.header(
"Set-Cookie",
`access_token=${token}; isHttpOnly=false; Path=/`
);
}
} catch (err) {
console.log("error in doSigin ", err);
return h
.response({ message: "Oops!! Something Went Wrong", status: 500 })
.code(500);
}
}
export async function fetchUserList(request: Request, h: ResponseToolkit) {
try {
const token = request.state.access_token;
if (!token) {
return h
.response({
message: "You are not Authenticated this page",
status: 400,
})
.code(400);
}
const isAuthenticated = validateToken(token);
if (isAuthenticated) {
const users = await userRepository.fetchAllUser();
return h
.response({
data: users,
message: "success",
status: 200,
})
.code(200);
} else {
return h
.response({
message: "Token Expired",
status: 401,
})
.code(401);
}
} catch (e) {
return h
.response({ message: "Oops!! Something Went Wrong", status: 500 })
.code(500);
}
}
/**
* File Name : repository/userRepository.ts
*/
import knex from "../db";
import { v4 as uuidv4 } from "uuid";
import { IUser } from "../utils/constants";
export default class UserRepository {
async findByUsername(username: string): Promise<IUser | undefined> {
const user = await knex<IUser>("users").where({ username }).first();
return user;
}
async create(username: string, hash: string): Promise<IUser> {
const id = uuidv4();
const user: IUser = { id, username, password: hash };
await knex<IUser>("users").insert(user);
return user;
}
async fetchAllUser(): Promise<any | undefined> {
const users = await knex<IUser>("users").select("username");
return users;
}
}
For database connection I use knex and pg used for connecting hapi.js with postgresql. We can create a knex.ts file by this command
npx knex init -x ts
/**
* File Name : knexfile.ts (generated by the above command)
* @type { Object.<string, import("knex").Knex.Config> }
*/
// Update with your config settings.
const config = {
development: {
client: "pg",
connection: <DB_URL>,
migrations: {
directory: "./migrations",
extension: "ts",
},
seeds: { directory: "./seeds" },
},
};
module.exports = config;
/**
* File Name : db.ts
*/
import Knex from "knex";
const config = require('./knexfile')
const HAPI_ENV = "development";
const knexConfig = config[HAPI_ENV];
const knex = Knex(knexConfig);
export default knex;
When all the things are done we have to add some scripts in package.json file.
"scripts": {
"start": "tsc --watch & nodemon dist",
"knex:seed:run": "knex --knexfile src/knexfile.ts seed:run",
"knex:seed:make": "knex --knexfile src/knexfile.ts seed:make",
"knex:migrate:make": "knex --knexfile src/knexfile.ts migrate:make",
"knex:migrate:latest": "knex --knexfile src/knexfile.ts migrate:latest",
"knex:migrate:rollback": "knex --knexfile src/knexfile.ts migrate:rollback"
}
so what does these script do
npm run start :- run typescript compiler and build the code in dist folder and run the javascript code in dist folder npm run knex:seed:run :- seeds we make for postgresql table and then add the data to corresponding table npm run knex:seed:make <seed-name> :- create a seed file for adding some data into a table in postgresql using knex. npm run knex:migrate:make <migration-name> :- create a migration file for creating a table in postgresql using knex. npm run knex:migrate:latest :- run the latest migration file that has not been added to postgresql. npm run knex:migrate:rollback :- rollback the last migration that we have run.
Here is the folder structure i follow

Here is the reference of code on GitHub :- https://github.com/YateshChhabra/boilerplate-hapijs
In this article we learn about how we make a boilerplate for hapi.js and created an endpoints in that. Through knex.js we can build query for getting a database from postgresql. We can check our endpoints in postman tool.
Hope you learn something new. Thanks for reading this article :)
메타데이터
- post_id
- 198774b2c78
- slug
- hapi-js-21-3-2-using-typescript-198774b2c78
- url
- https://medium.com/@yateshchhabra/hapi-js-21-3-2-using-typescript-198774b2c78
- canonical_url
- https://medium.com/@yateshchhabra/hapi-js-21-3-2-using-typescript-198774b2c78
- author_url
- https://medium.com/@yateshchhabra
- status
- ok
- fetched_at
- 2026-07-25 17:51:23