Skip to content

Commit

Permalink
feat(app): Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
a-lebailly committed Oct 27, 2024
0 parents commit 2a97358
Show file tree
Hide file tree
Showing 30 changed files with 3,729 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
PORT=3000
DATABASE_URL="mysql://user:password@localhost:3306/api?schema=public"
19 changes: 19 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"env": {
"node": true,
"es2020": true
},
"parser": "@typescript-eslint/parser",
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"plugins": ["@typescript-eslint"],
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"
},
"rules": {
}
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea/
.env
node_modules/
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npm run lint && npm run format && npm run build && git add .
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
dist/
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80,
"tabWidth": 4,
"semi": true
}
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Alexandre Lebailly

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
218 changes: 218 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
# ExpressJS API Boilerplate

An Express.js API boilerplate using TypeScript, Prisma ORM, ESLint, Prettier, and Husky for code formatting and linting.

## Table of Contents

- [Introduction](#introduction)
- [Features](#features)
- [Project Structure](#project-structure)
- [Prerequisites](#prerequisites)
- [Getting Started](#getting-started)
- [Scripts](#scripts)
- [Configuration](#configuration)
- [Environment Variables](#environment-variables)
- [ESLint](#eslint)
- [Prettier](#prettier)
- [Husky](#husky)
- [Database Schema](#database-schema)
- [Contributing](#contributing)
- [License](#license)

## Introduction
This project serves as a boilerplate for building RESTful APIs using Express.js and TypeScript. It integrates essential tools and libraries to ensure code quality, consistency, and ease of development:
- Prisma ORM: For database modeling and querying.
- ESLint: To identify and fix problematic patterns in the code.
- Prettier: For consistent code formatting.
- Husky: To run scripts before commits (e.g., linting and formatting).

## Features
- TypeScript: Strongly typed programming language that builds on JavaScript.
- Express.js: Fast, unopinionated, minimalist web framework for Node.js.
- Prisma ORM: Next-generation ORM for TypeScript and Node.js.
- ESLint & Prettier: For code linting and formatting.
- Husky: Git hooks to enforce code quality before commits.
- Nodemon: For automatic server restarts during development.

## Project Structure
```text
project-root/
├── src/
│ ├── app.ts # Entry point for the Express application
│ ├── routes/ # API route definitions
│ ├── controllers/ # Request handlers for routes
│ ├── services/ # Business logic for handling requests
│ ├── middlewares/ # Custom middleware functions
│ ├── prisma/
│ │ └── client.ts # Prisma client instance
│ ├── utils/ # Utility functions
│ └── config/ # Configuration files
├── prisma/
│ ├── models/ # Data models
│ ├── baseSchema.prisma # Base Prisma schema file
│ ├── mergeModels.js # Script to merge Prisma schema files
│ └── schema.prisma # Prisma schema file
├── package.json
├── tsconfig.json
├── .eslintrc.json
├── .prettierrc
├── .env.example
└── README.md
```

## Prerequisites
- Node.js: Version 22 or above.
- npm: Comes with Node.js.
- A database instance (MySQL, PostgreSQL, SQLite, SQL Server...).

## Getting Started
### 1. Clone the Repository
```bash
git clone https://github.com/a-lebailly/expressjs-api-boilerplate.git
cd expressjs-api-boilerplate
```

### 2. Configure Environment Variables
Create a `.env` file in the root directory based on `.env.example`:
```bash
cp .env.example .env
```
Update the `.env` file with your database credentials and any other necessary configuration.

### 3. Set Up the Database
Ensure that you have a MySQL database running and accessible.
Create your first model in `prisma/models` before continue.

### 4. Install dependencies and initialize Prisma
Install dependencies and generate the Prisma client and apply migrations:
```bash
npm run init
```
Start Prisma Studio to interact with your database visually:
```bash
npm run prisma:studio
```

### 5. Run the Development Server
```bash
npm run dev
```
The server will start on the port specified in your `.env` file (default is `3000`).

## Scripts
The following scripts are available in the package.json file:
- `npm run init`: Installs dependencies, generates the Prisma client, and applies migrations.
- `npm run build`: Compiles TypeScript code to JavaScript in the dist directory.
- `npm run start`: Runs the compiled JavaScript code from the dist directory.
- `npm run dev`: Starts the development server with hot reloading using nodemon and ts-node.
- `npm run prod`: Builds the project and then starts it.
- `npm run prisma`: Runs Prisma CLI commands.
- `npm run prisma:studio`: Opens Prisma Studio for database management.
- `npm run prisma:generate`: Merges Prisma schema files into a single schema.prisma file and generates the Prisma client.
- `npm run prisma:migrate`: Applies migrations to the database.
- `npm run merge:models`: Merges Prisma schema files into a single schema.prisma file.
- `npm run lint`: Runs ESLint to check for linting errors.
- `npm run lint`:fix: Runs ESLint and automatically fixes fixable issues.
- `npm run format`: Formats code using Prettier.


## Configuration
### Environment Variables
The .env.example file provides a template for the necessary environment variables:

```env
PORT=3000
DATABASE_URL="mysql://root:root@localhost:3306/api?schema=public"
```
- `PORT`: The port on which the server will run.
- `DATABASE_URL`: The connection string for your MySQL database.

### ESLint
ESLint is configured to work with TypeScript and Prettier.
`.eslintrc.json`:

```json
{
"env": {
"node": true,
"es2020": true
},
"parser": "@typescript-eslint/parser",
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"plugins": ["@typescript-eslint"],
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"
},
"rules": {}
}
```
- `parser`: Uses @typescript-eslint/parser to parse TypeScript code.
- `extends`: Extends recommended ESLint rules and integrates Prettier.
- `plugins`: Includes @typescript-eslint plugin for additional TypeScript-specific linting rules.

### Prettier
Prettier is configured for consistent code formatting.
`.prettierrc`:
```json
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80,
"tabWidth": 4,
"semi": true
}
```
- `singleQuote`: Use single quotes instead of double quotes.
- `trailingComma`: Add a trailing comma wherever possible.
- `printWidth`: Wrap lines at 80 characters.
- `tabWidth`: Set tab width to 4 spaces.
- `semi`: Use semicolons at the ends of statements.

### Husky
Husky is used to run scripts before commits to ensure code quality.
Pre-commit Hook (`.husky/pre-commit`):
```bash
npm run lint && npm run format && npm run build && git add .
```
- Linting: Checks for linting errors.
- Formatting: Formats the code.
- Building: Compiles the TypeScript code.
- Adding Changes: Adds any changes made by the formatting or build process back to the commit.

## Database Schema
The Prisma base schema define the connection to the database.
`baseSchema.prisma`:
```prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
```
- `generator client`: Instructs Prisma to generate the client for JavaScript.
- `datasource db`: Specifies the database provider and connection URL.

The merge model script (`mergeModels.js`) is used to merge multiple Prisma schema files into a single `schema.prisma` file. This is useful when working with multiple developers or when splitting the schema into smaller files for better organization.
```bash
npm run merge:models
```

## Contributing
Contributions are welcome! Please follow these steps:
- Fork the repository.
- Create a new branch: `git checkout -b feature/your-feature`.
- Commit your changes: `git commit -m 'Add some feature'`.
- Push to the branch: `git push origin feature/your-feature`.
- Submit a pull request.

## License
Distributed under the MIT License. See `LICENSE` for more information.

18 changes: 18 additions & 0 deletions dist/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = __importDefault(require("express"));
const dotenv_1 = __importDefault(require("dotenv"));
const indexRoutes_1 = __importDefault(require("./routes/indexRoutes"));
dotenv_1.default.config();
const app = (0, express_1.default)();
const PORT = process.env.PORT || 3000;
app.use('/', indexRoutes_1.default);
app.listen(PORT, () => {
console.log('Server running at PORT:', PORT);
}).on('error', (error) => {
console.log('Error: ', error.message);
throw new Error(error.message);
});
14 changes: 14 additions & 0 deletions dist/controllers/indexController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const indexService_1 = __importDefault(require("../services/indexService"));
class IndexController {
index(req, res) {
res.status(200).json({
message: indexService_1.default.sayHelloWorld(),
});
}
}
exports.default = new IndexController();
5 changes: 5 additions & 0 deletions dist/prisma/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const client_1 = require("@prisma/client");
const prisma = new client_1.PrismaClient();
exports.default = prisma;
10 changes: 10 additions & 0 deletions dist/routes/indexRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const indexController_1 = __importDefault(require("../controllers/indexController"));
const router = (0, express_1.Router)();
router.get('/', indexController_1.default.index);
exports.default = router;
8 changes: 8 additions & 0 deletions dist/services/indexService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class IndexService {
sayHelloWorld() {
return 'Hello World !';
}
}
exports.default = new IndexService();
12 changes: 12 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import globals from "globals";
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";


export default [
{files: ["**/*.{js,mjs,cjs,ts}"]},
{files: ["**/*.js"], languageOptions: {sourceType: "commonjs"}},
{languageOptions: { globals: globals.browser }},
pluginJs.configs.recommended,
...tseslint.configs.recommended,
];
Loading

0 comments on commit 2a97358

Please sign in to comment.