Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions templates/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.1",
"dotenv": "^16.4.7"
"dotenv": "^16.4.7",
"express": "^4.17.1"
},
"devDependencies": {
"nodemon": "^3.1.9",
"jest": "^29.7.0",
"nodemon": "^3.1.9",
"supertest": "^7.0.0"
}
}
23 changes: 23 additions & 0 deletions templates/express_mysql/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import express, { json } from "express";
import helmet from "helmet";
import cors from "cors";

import sampleRouter from "./router/sampleRouter.js";
import { appConfig } from "./config/appConfig.js";

export default function (database) {
const app = express();

app.use(helmet());

app.use(cors());
app.use(json());

// Disable the X-Powered-By header to make it harder
// for attackers to find the tech stack.
app.disable("x-powered-by");

app.use(appConfig.router.SAMPLE_PREFIX, sampleRouter(database));

return app;
}
50 changes: 50 additions & 0 deletions templates/express_mysql/app.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import request from "supertest";
import { expect, jest } from "@jest/globals";
import makeApp from "./app.js";

const expectedSamples = [
{ id: 1, name: "sample1" },
{ id: 2, name: "sample2" },
];

const getSamples = jest.fn().mockImplementation(async (req, res) => {
return res.status(200).send({
MESSAGE: "Data fetched successfully.",
DATA: expectedSamples,
});
});

const app = makeApp({ getSamples });

describe("API Endpoints", () => {
it("should return success message on GET /api/sample/test", async () => {
const res = await request(app).get("/api/sample/test");
expect(res.statusCode).toBe(200);
expect(res.body).toEqual({
MESSAGE: "It's Working. 👍🏻",
});
});

it("should return all samples on GET /api/sample/all", async () => {
const res = await request(app).get("/api/sample/all");
expect(getSamples.mock.calls.length).toBe(1);
expect(getSamples).toHaveBeenCalledWith(
expect.any(Object),
expect.any(Object),
);
expect(res.statusCode).toBe(200);
expect(res.body).toHaveProperty(
"MESSAGE",
"Data fetched successfully.",
);
expect(res.body).toHaveProperty("DATA");
expect(Array.isArray(res.body.DATA)).toBe(true);
res.body.DATA.forEach((item) => {
expect(item).toHaveProperty("id");
expect(item).toHaveProperty("name");
expect(typeof item.id).toBe("number");
expect(typeof item.name).toBe("string");
});
expect(res.body.DATA).toEqual(expect.arrayContaining(expectedSamples));
});
});
1 change: 1 addition & 0 deletions templates/express_mysql/connection/normalConnection.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const connectToDb = () => {
let db = null;
try {
db = createConnection(appConfig.db);
db.connect();
return db;
} catch (err) {
const timeStamp = new Date().toLocaleString();
Expand Down
40 changes: 8 additions & 32 deletions templates/express_mysql/controller/sampleController.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,11 @@
import { appendFileSync } from "fs";
import db from "../connection/poolConnection.js";

export async function test(_, res) {
return res.status(200).send({
MESSAGE: "It's Working. 👍🏻",
});
}

export async function getAllSamples(_, res) {
const db_conn = await db.promise().getConnection();
try {
await db_conn.query("LOCK TABLES sample_table READ");
const [data] = await db_conn.query("SELECT * FROM sample_table");
export default {
async test(_, res) {
return res.status(200).send({
MESSAGE: "Data fetched successfully.",
DATA: data,
MESSAGE: "It's Working. 👍🏻",
});
} catch {
const timeStamp = new Date().toLocaleString();
const errMessage = `[ERROR]: ${timeStamp} - ${err.message}`;
console.error(errMessage);
appendFileSync(
"./logs/controller/sampleController.log",
`${errMessage}\n`,
);
},

return res.status(500).send({
MESSAGE: "Something went wrong. Please try again later.",
});
} finally {
await db_conn.query("UNLOCK TABLES");
db_conn.release();
}
}
async getAllSamples(_, res, database) {
await database.getSamples(_, res);
},
};
32 changes: 32 additions & 0 deletions templates/express_mysql/db/database.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { appendFileSync } from "fs";
import db from "../connection/poolConnection.js";

export default {
async getSamples(req, res) {
const db_conn = await db.promise().getConnection();

try {
await db_conn.query("LOCK TABLES sample_table READ");
const [data] = await db_conn.query("SELECT * FROM sample_table");
return res.status(200).send({
MESSAGE: "Data fetched successfully.",
DATA: data,
});
} catch (err) {
const timeStamp = new Date().toLocaleString();
const errMessage = `[ERROR]: ${timeStamp} - ${err.message}`;
console.error(errMessage);
appendFileSync(
"./logs/controller/sampleController.log",
`${errMessage}\n`,
);

return res.status(500).send({
MESSAGE: "Something went wrong. Please try again later.",
});
} finally {
await db_conn.query("UNLOCK TABLES");
db_conn.release();
}
},
};
49 changes: 20 additions & 29 deletions templates/express_mysql/db/reInitDb.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,27 @@
import { readFile, appendFileSync } from "fs";
import { readFile } from "fs/promises";
import { appConfig } from "../config/appConfig.js";

const reInitDb = (db) => {
const reInitDb = async (db) => {
try {
readFile("./db/reInitDb.sql", "utf8", (err, data) => {
if (err) {
const timeStamp = new Date().toLocaleString();
const errMessage = `[ERROR]: ${timeStamp} - reInitDb - ${err.message}`;
console.error(errMessage);
} else {
db.query(data, (err, _) => {
if (err) {
const timeStamp = new Date().toLocaleString();
const errMessage = `[ERROR]: ${timeStamp} - reInitDb - ${err.message}`;
console.error(errMessage);

if (err.message.includes("Unknown database")) {
console.warn(
`[HINT]: Database '${appConfig.db.database}' does not exist. Please create it by running the following command in your MySQL shell: 'CREATE DATABASE ${appConfig.db.database}'.`,
);
}
} else {
console.info("[INFO]: Database re-initialized.");
}
});
}
});
const data = await readFile("./db/reInitDb.sql", "utf8");
await db.promise().query(data);
console.info("[INFO]: Database re-initialized.");
} catch (err) {
const timeStamp = new Date().toLocaleString();
const errMessage = `[ERROR]: ${timeStamp} - ${err.message}`;
console.error(errMessage);
appendFileSync("./logs/db/reInitDb.log", `${errMessage}\n`);
console.error(
`[ERROR]: ${new Date().toLocaleString()} - reInitDb - ${err.message}`,
);
if (err.message.includes("Unknown database")) {
console.warn(
`[HINT]: Database '${appConfig.db.database}' does not exist. Please create it by running the following command in your MySQL shell: 'CREATE DATABASE ${appConfig.db.database}'.`,
);
}
} finally {
return new Promise((resolve, reject) => {
db.end((err) => {
if (err) reject(err);
else resolve();
});
});
}
};

Expand Down
27 changes: 6 additions & 21 deletions templates/express_mysql/index.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,18 @@
import "dotenv/config.js";

import express, { json } from "express";
import helmet from "helmet";
import { appendFileSync } from "fs";
import cors from "cors";
import "dotenv/config.js";

import makeApp from "./app.js";
import database from "./db/database.js";
import { initLog } from "./logs/initLog.js";
import sampleRouter from "./router/sampleRouter.js";
import { appConfig } from "./config/appConfig.js";
import connectToDb from "./connection/normalConnection.js";
import reInitDb from "./db/reInitDb.js";
import { appConfig } from "./config/appConfig.js";

const app = express();

// Helmet sets HTTP headers for security.
app.use(helmet());

app.use(cors());
app.use(json());

// Disable the X-Powered-By header to make it harder
// for attackers to find the tech stack.
app.disable("x-powered-by");

app.use(appConfig.router.SAMPLE_PREFIX, sampleRouter);
const app = makeApp(database);

initLog();
const db = connectToDb();
reInitDb(db);
await reInitDb(db);

app.listen(appConfig.PORT, (err) => {
if (err) {
Expand Down
7 changes: 5 additions & 2 deletions templates/express_mysql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
"dev": "nodemon index.js",
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
},
"author": "quick_start_express",
"license": "ISC",
Expand All @@ -18,7 +19,9 @@
"mysql2": "^3.11.3"
},
"devDependencies": {
"nodemon": "^3.1.9"
"jest": "^29.7.0",
"nodemon": "^3.1.9",
"supertest": "^7.0.0"
},
"type": "module"
}
15 changes: 10 additions & 5 deletions templates/express_mysql/router/sampleRouter.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { test, getAllSamples } from "../controller/sampleController.js";
import { Router } from "express";

const sampleRouter = Router();
import sampleController from "../controller/sampleController.js";

sampleRouter.get("/test", test);
sampleRouter.get("/all", getAllSamples);
export default function (database) {
const router = Router();

export default sampleRouter;
router.get("/test", (req, res) => sampleController.test(req, res));
router.get("/all", (req, res) =>
sampleController.getAllSamples(req, res, database),
);

return router;
}
Loading