Skip to content
Merged
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
68 changes: 68 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Run Jest Tests with MySQL

on:
push:
branches:
- test
pull_request:
branches: test

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- uses: mirromutth/mysql-action@v1.1
with:
host port: 3800 # Optional, default value is 3306. The port of host
container port: 3307 # Optional, default value is 3306. The port of container
character set server: "utf8" # Optional, default value is 'utf8mb4'. The '--character-set-server' option for mysqld
collation server: "utf8_general_ci" # Optional, default value is 'utf8mb4_general_ci'. The '--collation-server' option for mysqld
mysql version: "5.7" # Optional, default value is "latest". The version of the MySQL
mysql database: "testing" # Optional, default value is "test". The specified database which will be create
mysql root password: "passwordRoot" # Required if "mysql user" is empty, default is empty. The root superuser password

- name: Create a .env
run: |
cat <<EOF > .env
NODE_ENV=development
PORT=3000
CORS_ORIGIN=*
JWT_SECRET=this_is_a_secret_key
BCRYPT_SALT_ROUNDS=10
DB_HOST=127.0.0.1
DB_PORT=3800
DB_USER=root
DB_PASSWORD=passwordRoot
DB_NAME=testing
DB_DIALECT=mysql
EOF

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install mysql client
run: sudo apt-get update && sudo apt-get install -y mysql-client

- name: Wait for MySQL to be ready
run: |
for i in {1..30}; do
if mysqladmin ping -h127.0.0.1 -P3800 -ppasswordRoot --silent; then
break
fi
sleep 2
done

- name: Apply DB structure
run: |
mysql -h127.0.0.1 -P3800 -uroot -ppasswordRoot testing < demo/db/structure.demo.sql

- name: Install dependencies
run: npm install

- name: Run Jest tests
run: npm test
116 changes: 116 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# web-api-ts

It's a boilerplate about web api to facilitate create with express and sequelize on node, but on **Typescript**

## Getting Started

install all dependencies, after execute on your terminal `npm start` (If this your first time or change code on typescript, execute `npm run build` to transpile typescript to javascript) or if you're developing, execute `npm run start:dev`.

## Roles

This project uses a role-based access control (RBAC) system. The available roles are:

| Role | Description |
|--------|------------------------------------|
| Admin | System administrator. |
| User | Standard authenticated user. |
| Guest | Guest user (not authenticated). |

You can add more roles by extending the role classes in [`src/classes/role.class.ts`](src/classes/role.class.ts) and updating the logic in your middleware or controllers as needed.
Each role is represented by a class, making it easy to customize or add new roles according to your application's requirements.

## Endpoints

These endpoints are provided as examples. If you want to add more, simply create new route and controller files following the existing project structure.

> **Note:** All endpoints start with `/api/`, except for the `/health` endpoint. The `/health` endpoint is special and is used to check the status of the backend server.

| Method | Path | Payload | Response | Access |
| ------ | ------------ | --------------------------------------- | --------- | ----------- |
| POST | /user/login | `username`, `password` | `token` | Public |
| POST | /user/signup | `username`, `email`, `password` | `token` | Public |
| POST | /product | `name`, `description`, `stock`, `price` | `id` | Admin |
| PUT | /product/:id | None | `message` | Admin |
| DELETE | /product/:id | None | `message` | Admin |
| GET | /product/all | None | `{}` | User/Admin |

## Project Architecture

The project follows a modular structure to keep code organized and maintainable. Below is an overview of the main folders and their purposes:

```
src/
├── server.ts # Entry point to start the server
├── app.ts # Main Express app setup
├── routes/ # API route definitions
│ └── product.route.ts
├── controllers/ # Request handlers
│ └── product.controller.ts
├── respository/ # logic for interacting with the models
│ └── product.repository.ts
├── models/ # Sequelize models and relations
│ └── product.model.ts
├── middlewares/ # Custom Express middlewares
│ └── role.middleware.ts
├── classes/ # Contains utility classes and shared logic used across the project
│ └── role.class.ts
├── __tests__/ # Automated tests
│ └── product.test.ts
└── config/ # Configuration files
```

> **Note:** Some files in the project use the `.type.ts` extension (e.g., `product.route.type.ts`). These files are used to define TypeScript types and interfaces related to their respective modules.

This structure helps to separate concerns and makes the project easier to scale and maintain.

## Dependencies

This project uses several key dependencies to provide essential functionality:

- **express**: Fast, unopinionated, minimalist web framework for Node.js.
- **sequelize**: Promise-based Node.js ORM for Postgres, MySQL, MariaDB, SQLite, and Microsoft SQL Server.
- **bcrypt**: Library to help you hash passwords securely.
- **jest**: Delightful JavaScript testing framework for writing and running tests.
- **jsonwebtoken**: Library to generate and verify JSON Web Tokens for authentication and authorization.

## Database

This project is configured by default to use **MySQL** as the main database for development and production.
However, since Sequelize is used as the ORM, you can easily switch to other supported databases such as PostgreSQL, MariaDB, SQLite, or Microsoft SQL Server by updating your environment variables in the `.env` file.

**Default (development and production):**
- MySQL

**To use another database:**
1. Install the appropriate database driver (e.g., `pg` for PostgreSQL, `sqlite3` for SQLite).
2. Update the database variables in your `.env` file:
```env
DB_HOST=localhost
DB_USER=your_user
DB_PASSWORD=your_password
DB_NAME=your_database
DB_DIALECT=mysql # or postgres, mariadb, sqlite, mssql
```
3. Restart the application.

> **Note:** Make sure your database server is running and accessible.

## Environment Variables

Create a `.env` file in the root directory of the project with the following content, and adjust the values according to your environment:

```env
# Application variables
PORT=3000
NODE_ENV=development
CORS_ORIGINS=origins or *
JWT_SECRET=key secret
BCRYPTO_SALT_ROUNDS=10

# Database variables
DB_HOST=localhost
DB_USER=username
DB_PASSWORD=password
DB_NAME=databasename
DB_DIALECT=mysql
```
24 changes: 24 additions & 0 deletions demo/db/structure.demo.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- Active: 1744844169817@@127.0.0.1@3306@demo
CREATE TABLE IF NOT EXISTS Users (
id INT NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
email VARCHAR(254) NOT NULL,
password VARCHAR(100) NOT NULL,
inactive DATETIME DEFAULT NULL,
PRIMARY KEY(id)
);
CREATE UNIQUE INDEX idx_users_email ON Users(email);
CREATE UNIQUE INDEX idx_users_username ON Users(username);

CREATE TABLE IF NOT EXISTS Products (
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
name VARCHAR(100) NOT NULL,
description VARCHAR(255) NOT NULL,
stock INT NOT NULL DEFAULT 0,
price DECIMAL(10, 2) NOT NULL,
inactive DATETIME DEFAULT NULL,
PRIMARY KEY(id),
FOREIGN KEY(user_id) REFERENCES Users(id) ON DELETE CASCADE
);
CREATE INDEX idx_products_name ON Products(name);
12 changes: 12 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const { createDefaultPreset } = require("ts-jest");

const tsJestTransformCfg = createDefaultPreset().transform;

/** @type {import("jest").Config} **/
module.exports = {
setupFilesAfterEnv: ["./jest.setup.ts"],
testEnvironment: "node",
transform: {
...tsJestTransformCfg,
},
};
11 changes: 11 additions & 0 deletions jest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import database from "./src/db/connection";
import { beforeAll, afterAll } from "@jest/globals";

beforeAll(async () => {
await database.authenticate();
await database.sync();
});

afterAll(async () => {
await database.close();
});
Loading