Skip to content

Commit

Permalink
Fix eslint
Browse files Browse the repository at this point in the history
  • Loading branch information
cham11ng committed Mar 28, 2024
1 parent 3bbadfb commit 3847665
Show file tree
Hide file tree
Showing 21 changed files with 101 additions and 80 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
43 changes: 19 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</a>
</p>

Starter for Node.js Express API in Typescript with jsonwebtoken, joi, Knex, Objection.js and many other popular tools.
Starter for Node.js, Express API in Typescript and PostgreSQL with jsonwebtoken, joi, Knex, Objection.js and many other popular tools.

## Requirements

Expand All @@ -16,8 +16,6 @@ Starter for Node.js Express API in Typescript with jsonwebtoken, joi, Knex, Obje

## Getting Started

Clone the repository, install the dependencies.

```bash
# Clone repository
$ git clone git@github.com:cham11ng/typescript-api-starter.git <application-name>
Expand All @@ -27,6 +25,9 @@ $ cd <application-name>
# Update database credentials
$ cp .env.example .env

# Install dependencies
$ yarn install

$ yarn migrate
```

Expand All @@ -53,60 +54,54 @@ $ yarn dev
<a href="https://imgur.com/gallery/4rhTo"><img src="https://i.imgur.com/GpcDbLB.gif" /></a>
</p>

**Using Docker**

Make a copy of `.env.docker` and save as `.env`.
### Using Docker

```bash
# Make a copy of `.env.docker` and save as `.env`.
$ cp .env.docker .env
```

Install dependencies and run the application locally.

```bash
$ docker compose up -d postgres

$ docker compose up -d api

$ docker compose exec api sh yarn migrate # Make sure server is started checking logs before running this command
# Make sure server is started checking logs before running this command
$ docker compose exec api sh yarn migrate
```

View logs of the container.

```bash
$ docker compose logs -f
```
# View logs of the container.
$ docker compose logs -f api

To stop the services.

```bash
# To stop the services.
$ docker compose stop api postgres
```

## Generating Migrations and Seeds

To create migration use `make:migration` and seed use `make:seeder`:

```bash
# To create migration use `make:migration`
$ yarn make:migration create_{table_name}_table

# To create seed use `make:seeder`
$ yarn make:seeder {table_name}_table_seeder
```

Example,

```bash
# Example
$ yarn make:migration create_posts_table

$ yarn make:seeder post_table_seeder
```

Modify migration and seeder file as per the requirement. Then finally:

```bash
$ yarn migrate # to migrate
# to migrate
$ yarn migrate

$ yarn seed # to seed
# to seed
$ yarn seed
```

## Setting up REST Client
Expand All @@ -120,7 +115,7 @@ Create a file or add following lines in `.vscode` > `settings.json` and switch a
"refreshToken": "foo",
"accessToken": "bar",
"email": "sgr.raee@gmail.com",
"password": "secret"
"password": "secret"
},
"local": {
"host": "localhost",
Expand Down
12 changes: 2 additions & 10 deletions src/controllers/home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,9 @@ import config from '../config/config';

const { name, version } = config;

/**
* Handle / GET request, responds API information.
*
* @param {Request} req
* @param _
* @param {Response} res
* @returns {void}
*/
export function index(_: Request, res: Response): void {
export const index = (_: Request, res: Response): void => {
res.status(StatusCodes.OK).json({
name,
version
});
}
};
23 changes: 4 additions & 19 deletions src/controllers/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,7 @@ import * as userService from '../services/userService';

const { messages } = config;

/**
* Handle /users GET request.
*
* @param {Request} req
* @param _
* @param {Response} res
* @param {NextFunction} next
*/
export async function index(_: Request, res: Response, next: NextFunction): Promise<void> {
export const index = async (_: Request, res: Response, next: NextFunction): Promise<void> => {
try {
const response = await userService.fetchAll();

Expand All @@ -27,16 +19,9 @@ export async function index(_: Request, res: Response, next: NextFunction): Prom
} catch (err) {
next(err);
}
}
};

/**
* Handle /users POST request.
*
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
export async function store(req: Request, res: Response, next: NextFunction): Promise<void> {
export const store = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try {
const userPayload = req.body as UserPayload;

Expand All @@ -50,4 +35,4 @@ export async function store(req: Request, res: Response, next: NextFunction): Pr
} catch (err) {
next(err);
}
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import Table from '../../resources/enums/Table';
/**
* Add user_roles table.
*
* @param {Knex} knex
* @param {Knex} knex - knex instance.
* @returns {Promise<void>}
*/
export function up(knex: Knex): Promise<void> {
return knex.schema
Expand Down Expand Up @@ -38,7 +39,8 @@ export function up(knex: Knex): Promise<void> {
/**
* Drop user_roles table.
*
* @param {Knex} knex
* @param {Knex} knex - knex instance.
* @returns {Knex.SchemaBuilder}
*/
export function down(knex: Knex): Knex.SchemaBuilder {
return knex.schema.dropTable(Table.USER_ROLES);
Expand Down
8 changes: 6 additions & 2 deletions src/database/migrations/20180130005620_create_users_table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { Knex } from 'knex';
import Table from '../../resources/enums/Table';

/**
* Add users table.
*
* @param knex
* @param {Knex} knex - knex instance.
* @returns {Knex.SchemaBuilder}
*/
export function up(knex: Knex): Knex.SchemaBuilder {
return knex.schema.createTable(Table.USERS, (table) => {
Expand All @@ -19,8 +21,10 @@ export function up(knex: Knex): Knex.SchemaBuilder {
}

/**
* Drop users table.
*
* @param knex
* @param {Knex} knex - knex instance.
* @returns {Knex.SchemaBuilder}
*/
export function down(knex: Knex): Knex.SchemaBuilder {
return knex.schema.dropTable(Table.USERS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { Knex } from 'knex';
import Table from '../../resources/enums/Table';

/**
* Add user_sessions table.
*
* @param knex
* @param knex - knex instance.
* @returns {Knex.SchemaBuilder}
*/
export function up(knex: Knex): Knex.SchemaBuilder {
return knex.schema.createTable(Table.USER_SESSIONS, (table) => {
Expand All @@ -19,8 +21,10 @@ export function up(knex: Knex): Knex.SchemaBuilder {
}

/**
* Drop user_sessions table.
*
* @param knex
* @param knex - knex instance.
* @returns {Knex.SchemaBuilder}
*/
export function down(knex: Knex): Knex.SchemaBuilder {
return knex.schema.dropTable(Table.USER_SESSIONS);
Expand Down
6 changes: 4 additions & 2 deletions src/database/seeds/user_table_seeder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import Table from '../../resources/enums/Table';
import * as bcrypt from '../../utils/bcrypt';

/**
* Seed users table.
*
* @param knex
* @param {Knex} knex - knex instance.
* @returns {Promise<number[] | number[][]>}
*/
export function seed(knex: Knex): Promise<any> {
export function seed(knex: Knex): Promise<number[] | number[][]> {
return knex(Table.USERS).then(async () => {
return Promise.all([
knex(Table.USERS).insert([
Expand Down
5 changes: 5 additions & 0 deletions src/domain/misc/ResponseData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import JWTPayload from './JWTPayload';

type ResponseData = Request & { data: JWTPayload };

export default ResponseData;
3 changes: 2 additions & 1 deletion src/middlewares/authenticate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NextFunction, Request, Response } from 'express';
import { JsonWebTokenError } from 'jsonwebtoken';

import config from '../config/config';
import ResponseData from '../domain/misc/ResponseData';
import BadRequestError from '../exceptions/BadRequestError';
import UnauthorizedError from '../exceptions/UnauthorizedError';
import { tokenErrorMessageMap } from '../resources/constants/maps';
Expand All @@ -20,7 +21,7 @@ const authenticate = async (req: Request, res: Response, next: NextFunction): Pr
}

logger.log('info', 'JWT: Verifying token - %s', res.locals.accessToken);
const response: any = jwt.verifyAccessToken(res.locals.accessToken);
const response = jwt.verifyAccessToken(res.locals.accessToken) as ResponseData;

res.locals.loggedInPayload = response.data;

Expand Down
4 changes: 3 additions & 1 deletion src/middlewares/genericErrorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import { getReasonPhrase, StatusCodes } from 'http-status-codes';
import APIResponseInterface from '../domain/responses/APIResponse';
import logger from '../utils/logger';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const buildError = (err: any): APIResponseInterface<{ code: number; message: string; data?: any }> => {
if (err.isJoi) {
return {
code: StatusCodes.BAD_REQUEST,
message: getReasonPhrase(StatusCodes.BAD_REQUEST),
data:
err.details &&
// eslint-disable-next-line @typescript-eslint/no-explicit-any
err.details.map((error: any) => ({
param: error.path.join('.'),
message: error.message
Expand Down Expand Up @@ -39,10 +41,10 @@ export const buildError = (err: any): APIResponseInterface<{ code: number; messa
};

const genericErrorHandler = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
err: any,
_: Request,
res: Response,
// TODO: Remove this.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
__: NextFunction
): void => {
Expand Down
5 changes: 3 additions & 2 deletions src/middlewares/rateLimitHandler.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { rateLimit } from 'express-rate-limit';

// write express rateLimit best practices
const rateLimitMiddleware = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP, please try again after 15 minutes',
headers: true
headers: true,
legacyHeaders: false,
standardHeaders: true
});

export default rateLimitMiddleware;
2 changes: 1 addition & 1 deletion src/middlewares/transactionHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const transactionHandler = (req: Request, _: Response, next: NextFunction): void
const transactionId = req.headers['transactionId'] || randomUUID();

// Set the TransactionId inside the store
context.getStore().set('transactionId', transactionId);
context.getStore()?.set('transactionId', transactionId);

next();
});
Expand Down
4 changes: 3 additions & 1 deletion src/middlewares/validateRefreshToken.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NextFunction, Request, Response } from 'express';

import config from '../config/config';
import ResponseData from '../domain/misc/ResponseData';
import BadRequestError from '../exceptions/BadRequestError';
import UnauthorizedError from '../exceptions/UnauthorizedError';
import { tokenErrorMessageMap } from '../resources/constants/maps';
Expand All @@ -19,13 +20,14 @@ const validateRefreshToken = async (req: Request, res: Response, next: NextFunct
}

logger.log('info', 'JWT: Verifying token - %s', res.locals.refreshToken);
const response: any = jwt.verifyRefreshToken(res.locals.refreshToken);
const response = jwt.verifyRefreshToken(res.locals.refreshToken) as ResponseData;

res.locals.jwtPayload = response.data;

logger.log('debug', 'JWT: Authentication verified -', res.locals.jwtPayload);

next();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
const tokenErrorMessage = tokenErrorMessageMap[err.name as JWTErrorType];
logger.log('error', 'JWT: Authentication failed - %s', err.message);
Expand Down
12 changes: 12 additions & 0 deletions src/resources/stubs/migration.stub
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import * as Knex from 'knex';

/**
* Add :table table.
*
* @param {Knex} knex - knex instance.
* @returns {Promise<Knex.SchemaBuilder>}
*/
export function up(knex: Knex): Knex.SchemaBuilder {
return knex.schema.createTable('table_name', (table) => {
table.increments('id').primary();
Expand All @@ -8,6 +14,12 @@ export function up(knex: Knex): Knex.SchemaBuilder {
});
}

/**
* Drop :table table.
*
* @param {Knex} knex - knex instance.
* @returns {Knex.SchemaBuilder}
*/
export function down(knex: Knex): Knex.SchemaBuilder {
return knex.schema.dropTable('table_name');
}
8 changes: 7 additions & 1 deletion src/resources/stubs/seed.stub
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import * as Knex from 'knex';

export function seed(knex: Knex): Knex.SchemaBuilder {
/**
* Seed :table table

* @param {Knex} knex - knex instance.
* @returns {Promise<number[] | number[][]>}
*/
export function seed(knex: Knex): Promise<number[] | number[][]> {
return knex('table_name')
.del()
.then(() => {
Expand Down
Loading

0 comments on commit 3847665

Please sign in to comment.