- High-level architectural overview
- Component Breakdown
- Key Function Documentation
- Usage Examples / Onboarding Guide
- Configuration and Environment Setup
- API Documentation
- Diagrams
- Development Notes / TODOs
Folder/File | Description |
---|---|
📁 .vscode/ | IDE-specific configuration (VS Code) |
📁 Doc/ | Project documentation |
📁 src/ | Main source code folder |
├── 📁 bin/ | Contains executable files |
│ └── www | Entry point to start the server |
├── 📁 config/ | Configuration files |
│ ├── app.js | Class that manages the application |
│ ├── config.js | Global configuration |
│ └── database.js | Database connection |
├── 📁 core/ | Core of the application |
│ ├── 📁 entities/ | Data models |
│ │ ├── Post.js | Post model |
│ │ ├── RevokedToken.js | Revoked token model |
│ │ └── User.js | User model |
│ ├── 📁 ports/ | Interfaces for data persistence |
│ │ ├── postRepositoryInterface.js | Post repository interface |
│ │ └── userRepositoryInterface.js | User repository interface |
│ └── 📁 usecases/ | Business logic and use cases |
│ ├── 📁 post-use-cases/ | Use cases specific to posts |
│ │ ├── create-post.js | Create a post |
│ │ ├── find-posts.js | Find posts |
│ │ ├── find-one-post.js | Find a specific post |
│ │ ├── delete-post.js | Delete a post |
│ │ └── update-post.js | Update a post |
│ ├── 📁 user-use-cases/ | Use cases specific to users |
│ ├── login-user-with-credential.js | User login |
│ ├── logout-user.js | User logout |
│ └── register-user-with-credential.js | User registration |
├── 📁 infrastructure/ | Infrastructure layer and API |
│ ├── 📁 adapters/ | Adapters for different implementations |
│ ├── 📁 api/ | API endpoints exposure |
│ │ ├── 📁 controllers/ | Controllers that handle requests |
│ │ │ ├── postController.js | Post management |
│ │ │ └── userController.js | User management |
│ │ ├── 📁 middlewares/ | API middlewares |
│ │ │ ├── apiKeyRequired.js | API key middleware |
│ │ │ ├── authMiddleware.js | Authentication middleware |
│ │ │ ├── loggerMiddleware.js | Logs middleware |
│ │ │ ├── loginRequired.js | Authentication verification |
│ │ │ └── validators.js | Request validation |
│ │ ├── 📁 protected_routes/ | Protected routes requiring a JWT token |
│ │ │ ├── protectedposts.js | Protected post routes |
│ │ │ └── protectedUserRoute.js | Protected user routes |
│ │ └── 📁 routes/ | Public routes |
│ │ ├── freeposts.js | Public post routes |
│ │ └── userroutes.js | Public user routes |
│ └── 📁 repositories/ | Data persistence layer |
│ ├── mongoosePostRepository.js | Post implementation with Mongoose |
│ └── MongooseUserRepository.js | User implementation with Mongoose |
└── 📁 utils/ | Utility functions |
└── generateTokenSecret.js | JWT secret generation |
📄 .env.example | Example environment file |
📄 index.js | Main entry point |
📄 package.json | Project dependencies and scripts |
📄 package-lock.json | Dependency version lock |
📄 posts.json | Mock data for testing |
📄 readme.md | This documentation file |
📄 swaggerConfig.js | Swagger configuration for documentation |
This project implements a RESTful API using a layered architecture similar to Clean Architecture principles. The main layers are:
src/config
: Contains application configuration, including database connections (database.js
), server settings (app.js
), and environment variable management (config.js
).src/core
: Represents the core business logic of the application.entities
: Defines the Mongoose schemas for data structures likeUser
,Post
, andRevokedToken
.usecases
: Contains the specific business operations (e.g., creating a post, registering a user).ports
: Defines interfaces for repositories, decoupling the core logic from specific data storage implementations.
src/infrastructure
: Provides concrete implementations for external concerns.api
: Handles HTTP requests and responses.controllers
: Receives requests, calls appropriate use cases, and sends responses.middlewares
: Handles cross-cutting concerns like authentication, validation, and logging.routes
: Defines the API endpoints.
repositories
: Implements the repository interfaces defined insrc/core/ports
using Mongoose for MongoDB interaction.
src/middlewares
: Contains additional, potentially global, middlewares (thoughapiKeyRequired.js
is currently unused).src/utils
: Utility scripts, such asgenerateTokenSecret.js
.
A typical request to a protected endpoint flows through the system as follows:
- API Route: The request hits an endpoint defined in
src/infrastructure/api/routes
orsrc/infrastructure/api/protected_routes
. - Middleware(s):
loggerMiddleware.js
: Logs the incoming request.authMiddleware.js
(authorize
): Validates the JWT, checks if it's revoked, and populatesreq.user
.loginRequired.js
: Ensuresreq.user
is present.validators.js
(if applicable): Validates input parameters.
- Controller: The corresponding controller in
src/infrastructure/api/controllers
handles the request. - Use Case: The controller instantiates and calls a specific use case from
src/core/usecases
. - Repository: The use case interacts with a repository (e.g.,
MongoosePostRepository
) via its interface defined insrc/core/ports
. - Database: The repository performs CRUD operations on the MongoDB database.
- The response flows back through the layers to the client.
-
User.js
:- Purpose: Defines the Mongoose schema for users.
- Fields:
firstName
(String, required, trim, minlength: 2, maxlength: 50)lastName
(String, required, trim, minlength: 2, maxlength: 50)username
(String, unique, required, trim, minlength: 3, maxlength: 50)email
(String, required, unique, lowercase, trim, minlength: 2, maxlength: 50, email format validation)apiKey
(String, unique, default: null) - Note: API key generation logic has TODOs and might not be fully functional as intended.hashedPassword
(String)created_at
(Date, default: Date.now)refreshToken
(String)- Virtuals:
fullname
: ReturnsfirstName
+lastName
.- Methods:
comparePassword(password)
: Compares a given password with the storedhashedPassword
.setAPIKey()
: Generates and sets an API key. Currently usesbcrypt.hashSync
with a TODO to usebcrypt.hash
.
-
Post.js
:- Purpose: Defines the Mongoose schema for posts.
- Fields:
title
(String, required)content
(String, required)created_at
(Date, default: Date.now)author
(String, required)
- Methods:
toLocalString()
: Returns thecreated_at
date as a local string (Note: likely meanttoLocaleString()
).
-
RevokedToken.js
:- Purpose: Defines the Mongoose schema for storing revoked JWTs, used during logout.
- Fields:
token
(String, required, unique)revokedAt
(Date, default: Date.no
create-post.js
(CreatePostUseCase): Handles the creation of new posts.delete-post.js
(DeletePostUseCase): Handles the deletion of posts by their ID.find-one-post.js
(FindOnePostUseCase): Handles finding a single post by its ID.find-posts.js
(FindPostsUseCase): Handles finding all posts.update-post.js
(UpdatePostUseCase): Handles updating an existing post by its ID.
login-user-with-credential.js
(loginUserWithCredentialsUseCase): Handles user login with email and password, issuing a JWT.logout-user.js
(logoutUserUseCase): Handles user logout by adding the current token to theRevokedToken
list.register-user-with-credential.js
(registerUserWithCredentialsUseCase): Handles new user registration, including password hashing and API key generation.
postRepositoryInterface.js
: Defines the contract for post repository operations (e.g.,savePost
,findAllPosts
,findPostById
,updatePost
,deletePost
). This interface allows the core use cases to remain independent of the database technology used.userRepositoryInterface.js
: Defines the contract for user repository operations (e.g.,saveUser
,findByEmail
,findById
). This ensures that user-related use cases are decoupled from the specific data access implementation.
The use of repository interfaces is a key aspect of Clean Architecture, promoting separation of concerns and testability by allowing data access logic to be swapped or mocked.
-
MongooseUserRepository.js
:- Purpose: Implements the
UserRepositoryInterface
using Mongoose. - Function: Interacts with the MongoDB
users
collection to perform operations like saving new users (including API key generation viauser.setAPIKey()
) and finding users by email or ID.
- Purpose: Implements the
-
mongoosePostRepository.js
:- Purpose: Implements the
PostRepositoryInterface
using Mongoose. - Function: Interacts with the MongoDB
posts
collection to perform CRUD operations (Create, Read, Update, Delete) on posts.
- Purpose: Implements the
-
userController.js
:- Purpose: Handles HTTP requests related to user authentication and management.
- Main Actions:
register
: HandlesPOST /auth/register
. Validates input, callsRegisterUserWithCredentialsUseCase
to create a new user.login
: HandlesPOST /auth/login
. Validates input, callsloginUserWithCredentialsUseCase
to authenticate the user and issue a JWT.logout
: HandlesPOST /auth/logout
(protected). CallslogoutUserUseCase
to invalidate the user's JWT.
-
postController.js
:- Purpose: Handles HTTP requests related to blog posts.
- Main Actions:
findPosts
: HandlesGET /posts
. CallsFindPostsUseCase
to retrieve all posts.createPost
: HandlesPOST /posts/create
(protected). Validates input, callsCreatePostUseCase
to create a new post.findOnePost
: HandlesGET /posts/:id
. CallsFindOnePostUseCase
to retrieve a specific post by its ID.findOnePostAndUpdate
: HandlesPATCH /posts/update/:id
(protected). CallsUpdatePostUseCase
to update an existing post.findOnePostAndDelete
: HandlesDELETE /posts/delete/:id
(protected). CallsDeletePostUseCase
to delete a post.
-
authMiddleware.js
(authorize
):- Role: Core authentication middleware for protected routes.
- Functionality:
- Extracts the JWT from the
Authorization: Bearer <token>
header. - Checks if the token exists in the
RevokedToken
collection in the database. If found, rejects the request. - Verifies the JWT's signature and expiration using
jsonwebtoken
and theTOKEN_SECRET
. - If valid, populates
req.user
with the decoded token payload. - Calls
next()
to pass control to the next middleware or route handler. If any step fails, it sends an appropriate error response (e.g., 401 Unauthorized).
- Extracts the JWT from the
-
loginRequired.js
:- Role: Authorization middleware that checks if a user is authenticated.
- Functionality: Verifies that
req.user
has been populated (typically byauthMiddleware.js
). Ifreq.user
is not present, it returns a 401 Unauthorized error, indicating that the endpoint requires a logged-in user.
-
validators.js
:- Role: Provides input validation logic using
express-validator
. - Functionality: Exports various validation chains (e.g.,
validateLogin
,validateRegister
,validatePostId
,validateCreatePost
,validateUpdatePost
) that check request bodies and parameters for correctness (e.g., email format, required fields, ID format).
- Role: Provides input validation logic using
-
loggerMiddleware.js
:- Role: Logs incoming HTTP requests.
- Functionality: Prints the timestamp, HTTP method, and original URL of each request to the console.
-
src/middlewares/apiKeyRequired.js
:- Status: Currently commented out and non-functional.
- Intended Role (if active): This middleware was likely intended to protect certain routes by requiring a valid API key. However, its implementation is empty, and it's not actively used in any routes.
The application defines public routes and protected routes. Protected routes require authentication (a valid JWT) processed by the authorize
and loginRequired
middlewares.
userroutes.js
:GET /
: Displays a welcome message (currently includes HTML).POST /auth/register
: Allows new users to register. (UsesvalidateRegister
middleware).POST /auth/login
: Allows existing users to log in and receive a JWT. (UsesvalidateLogin
middleware).
freeposts.js
:GET /posts
: Retrieves a list of all posts.GET /posts/:id
: Retrieves a single post by its ID. (UsesvalidatePostId
middleware).
These routes are mounted under the authorize
middleware in src/config/app.js
, meaning all requests to them will first go through JWT validation.
protectedUserRoute.js
:POST /auth/logout
: Allows authenticated users to log out, revoking their current JWT. (UsesloginRequired
middleware).
protectedposts.js
:POST /posts/create
: Allows authenticated users to create a new post. (UsesloginRequired
,validateCreatePost
middlewares).PATCH /posts/update/:id
: Allows authenticated users to update an existing post by its ID. (UsesloginRequired
,validatePostId
,validateUpdatePost
middlewares).DELETE /posts/delete/:id
: Allows authenticated users to delete an existing post by its ID. (UsesloginRequired
,validatePostId
middlewares).
In userController.js
, the register
method demonstrates how a controller uses a use case:
// src/infrastructure/api/controllers/userController.js
// ...
const RegisterUserWithCredentials = require("../../../core/usecases/user-use-cases/register-user-with-credential");
// ...
class UserController {
constructor() {
this.registerUserUseCase = new RegisterUserWithCredentials();
// ...
}
register = async (req, res) => {
// ... input validation ...
try {
// 1. Controller instantiates and calls the use case
const user = await this.registerUserUseCase.registerUser(req.body);
// ...
res.status(201).json({ message: "User registered successfully", user: userObj });
} catch (error) {
res.status(500).json({ message: error.message });
}
};
// ...
}
Here, userController.register
calls this.registerUserUseCase.registerUser(req.body)
, passing the request data to the core business logic layer.
- Token Extraction: The
authorize
middleware retrieves the token from theAuthorization: Bearer <token>
header. - Revocation Check: It queries the
RevokedToken
collection in MongoDB to see if the extracted token has been previously revoked (e.g., by a logout operation). If revoked, access is denied. - Signature Verification: If not revoked,
jwt.verify(token, config.tokenSecret)
is used to check:- The token's signature against the application's secret key.
- The token's expiration time.
- User Population: If verification is successful, the decoded payload of the JWT (containing user information like email, ID) is attached to
req.user
. - Next Step:
next()
is called, allowing the request to proceed to the next middleware or the actual route handler. If any check fails, a 401 Unauthorized response is sent.
Create a new user account:
-
Endpoint:
POST /auth/register
-
Method:
POST
-
Headers:
Content-Type: application/json
-
Body (JSON):
{ "firstName": "John", "lastName": "Doe", "username": "johndoe23", "email": "john.doe23@example.com", "password": "Password123!" }
-
Success Response (201 Created):
{ "message": "User registered successfully", "user": { "firstName": "John", "lastName": "Doe", "username": "johndoe23", "email": "john.doe23@example.com", "_id": "someUserId", "created_at": "2023-01-01T00:00:00.000Z", "apiKey": "someApiKey" // Note: apiKey structure and reliability depend on current implementation } }
Log in to obtain a JWT:
-
Endpoint:
POST /auth/login
-
Method:
POST
-
Headers:
Content-Type: application/json
-
Body (JSON):
{ "email": "john.doe23@example.com", "password": "Password123!" }
-
Success Response (200 OK):
{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpvaG4uZG9lMjNAZXhhbXBsZS5jb20iLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwiX2lkIjoic29tZVVzZXJJZCIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoxNzAwMDAwOTAwfQ.someSignature" }
(The token is a JSON Web Token valid for 15 minutes)
Use the obtained JWT to access protected endpoints:
-
Endpoint:
POST /posts/create
-
Method:
POST
-
Headers:
Content-Type: application/json
Authorization: Bearer <your_jwt_token_here>
(Replace<your_jwt_token_here>
with the token from the login response)
-
Body (JSON):
{ "title": "My First Protected Post", "content": "This content is created by an authenticated user.", "author": "John Doe" // Author field might be derived from req.user in a future enhancement }
-
Success Response (201 Created):
{ "_id": "somePostId", "title": "My First Protected Post", "content": "This content is created by an authenticated user.", "author": "John Doe", "created_at": "2023-01-01T00:05:00.000Z" }
-
Environment Variables:
- The project uses a
.env
file to manage environment-specific variables. - An example file,
.env.example
, is provided. Copy it to.env
and fill in your actual configuration values:bash cp .env.example .env
- Key variables in
.env
:PORT
: The port the server will listen on (e.g.,5000
).TOKEN_SECRET
: A secret key for signing and verifying JWTs. You can generate one usingnode src/utils/generateTokenSecret.js
.MONGODB_URI
: The connection string for your local MongoDB instance (e.g.,mongodb://127.0.0.1:27017/yourdbname
).MONGODB_ATLAS_URI
: (Optional) Connection string for a MongoDB Atlas instance.
- The project uses a
-
Dependencies:
-
Install project dependencies using npm:
npm install
-
Key dependencies (from
package.json
): -express
: Web framework. -mongoose
: MongoDB ODM. -bcrypt
: Password hashing. -jsonwebtoken
: JWT creation and verification. -express-validator
: Input validation. -dotenv
: Environment variable loading. -express-jsdoc-swagger
: API documentation generation. -
Running the Application:
- The primary script to start the application is defined in
package.json
:js "scripts": { "start": "nodemon src/bin/www" }
-
Start the server using:
npm start
-
This command uses
nodemon
to runsrc/bin/www
, which initializes the database connection and starts the Express server.nodemon
will automatically restart the server on file changes.
-
- The primary script to start the application is defined in
The API documentation is generated using express-jsdoc-swagger
and is available by navigating to /api/docs
in your browser when the server is running.
Example: http://localhost:5000/api/docs
(if PORT
is 5000).
The Swagger UI allows you to explore endpoints, view request/response schemas, and test API calls directly.
sequenceDiagram
participant Client
participant Server as API Gateway/LB
participant ExpressApp as Express Application
participant AuthMiddleware as authorize()
participant LoginRequiredMiddleware as loginRequired()
participant PostController
participant CreatePostUseCase
participant PostRepository
participant Database
Client->>Server: POST /posts/create (with JWT)
Server->>ExpressApp: Request
ExpressApp->>AuthMiddleware: execute(req, res, next)
AuthMiddleware-->>Database: Check RevokedToken
AuthMiddleware->>AuthMiddleware: Verify JWT
AuthMiddleware->>ExpressApp: req.user populated
ExpressApp->>LoginRequiredMiddleware: execute(req, res, next)
LoginRequiredMiddleware->>ExpressApp: next()
ExpressApp->>PostController: createPost(req, res)
PostController->>CreatePostUseCase: createPost(req.body)
CreatePostUseCase->>PostRepository: savePost(postData)
PostRepository->>Database: Insert post
Database-->>PostRepository: Saved post
PostRepository-->>CreatePostUseCase: Saved post
CreatePostUseCase-->>PostController: Saved post
PostController-->>ExpressApp: res.status(201).json(post)
ExpressApp-->>Client: HTTP 201 Response
flowchart TD
A[Client] --> B[Express App / Routes]
B --> C{Middleware}
C -->|Authentication/Validation| D[Controllers]
D --> E[Use Cases/Core Logic]
E --> F[Ports/Interfaces]
F -->|Implemented by| G[Repositories/Infrastructure]
G --> H[Database/MongoDB]
E -->|Domain Objects| I[Entities/Core Logic]
X[Config] --> B
X --> E
X --> G
- General TODOs:
- Multiple
console.log
statements are present throughout the codebase (e.g.,User.js
,userController.js
,postController.js
,authMiddleware.js
,loginRequired.js
,www
). These should be removed or replaced with a proper logger for production.
- Multiple
- User Routes (
src/infrastructure/api/routes/userroutes.js
):- The root
GET /
route (userRoutes.get("/", ...
) currently sends back HTML (res.send('<h1> Hello API !</h1> <a href="/api/docs">Documentation</a>');
). A//TODO: Remove Html
comment is present because REST API should not send HTML.
- The root
- Database (
src/config/database.js
):- The
connectDB
function includes aconsole.error("❌ Erreur : MONGODB_URI non définie dans .env");
which is good for initial setup but might be part of a more robust config validation.
- The
- Server Start (
src/bin/www
):console.log("Starting server...");
console.log(🌍 Running in ${config.NODE_ENV || "Development"} mode);
console.log(📡 MongoDB URL: ${config.mongoURI ? "Ok Defined" : "Not set"});
These are informational but should ideally be handled by a structured logger.