Live Swagger Docs: 👉 https://document-manager-api.onrender.com/docs/
This is a Document Manager API built with NestJS and Prisma ORM that allows users to upload, manage, and organize documents. The API supports user authentication, file uploads, folder management, role-based access, and more.
- Installation
- Environment Variables
- Steps to Run the Docker Container
- Features
- API Endpoints
- User Model
- DocumentPermission Model
- Folder Model
- Document Model
- Enums
- Exception Handling
- File Upload & Storage
- JWT Authentication
- Testing
- Final Thoughts
git clone https://github.com/BishoySedra/Document_Manager_API.gitNavigate to the project directory and run:
npm install --legacy-peer-depsThe project uses PostgreSQL as the database. Make sure you have a PostgreSQL instance running, and set up the connection string in the .env file.
DATABASE_URL=postgresql://user:password@localhost:5432/your-database Run the Prisma migrations to set up the database as a container by running the following script:
npm run db:dev:restartOnce the database is set up, start the application:
npm run start:devThe application should now be running on http://localhost:3000.
Make sure to configure the following environment variables in your .env file:
DATABASE_URL=postgresql://user:password@localhost:5432/your-database PORT=3000 SALT_ROUNDS=10 JWT_SECRET=your_jwt_secret JWT_ACCESS_EXPIRES_IN=15m JWT_REFRESH_SECRET=your_jwt_refresh_secret JWT_REFRESH_EXPIRES_IN=7d PREFIX_URL="/api/v1" CLOUDINARY_CLOUD_NAME=your_cloud_name CLOUDINARY_API_KEY=your_api_key CLOUDINARY_SECRET_KEY=your_api_secret -
Update the Database URL in
.env:- Replace
localhostin theDATABASE_URLwithdev-db(the container name for the PostgreSQL service provided by Docker).
DATABASE_URL="postgresql://postgres:123@dev-db:5432/document_manager?schema=public" - Replace
-
Run the Docker Container:
-
Use one of the following commands to start the container:
-
To start the container in the background:
npm run app:container:up
-
To restart the container (if necessary):
npm run app:container:restart
-
-
-
Revert Database URL to
localhost:- After successfully running the container, revert the
DATABASE_URLin the.envfile to uselocalhostfor listening to the database service.
DATABASE_URL="postgresql://postgres:123@localhost:5432/document_manager?schema=public" - After successfully running the container, revert the
-
Ensure Correct Port:
- Verify that the
PORTin your.envfile is different from the port thenest-appcontainer is listening to.
- Verify that the
- User Authentication: Supports JWT authentication for users.
- Folder Management: Users can create folders, organize them, and manage subfolders.
- File Upload: Supports uploading of documents (PDF, DOCX, XLSX, TXT, etc.) using Multer and Cloudinary.
- Role-Based Access Control (RBAC): Allows assigning permissions to users for documents (view, edit, download). Admins can manage all users.
- Exception Handling: Custom exceptions with JSON response formatting based on the JSend specification.
- JWT Refresh Tokens: Support for refreshing JWT tokens with the refresh token mechanism.
- User Role Management: Admins can view, update, and delete user profiles with role-based access.
- POST
/auth/register: Registers a new user and returns an access token. - POST
/auth/login: Logs in a user and returns access and refresh tokens. - POST
/auth/refresh: Refreshes the access token using the refresh token. - POST
/auth/logout: Logs the user out by invalidating the refresh token. - GET
/auth/profile/: Retrieves the authenticated user's profile information. - PATCH
/auth/password/: Allows a user to change their password.
- POST
/documents/upload: Upload a new document to the server (requires authentication). - GET
/documents/:id: Retrieve document details by ID. - PATCH
/documents/:id: Update document metadata by ID (e.g., rename, add tags). - DELETE
/documents/:id: Delete a document by ID.
- POST
/folders: Create a new folder (requires authentication). - GET
/folders: List all folders created by the current user. - GET
/folders/:id: Retrieve details of a specific folder. - GET
/folders/:id/documents: Retrieve documents associated with a specific folder. - PATCH
/folders/:id: Update a folder (e.g., rename) by ID. - DELETE
/folders/:id: Delete a folder by ID.
- GET
/users: Retrieve all users (admin-only). - GET
/users/:id: Retrieve a user by ID (admin-only or users can access their own). - PATCH
/users/:id: Update a user profile by ID (admin-only or users can update their own). - DELETE
/users/:id: Delete a user by ID (admin-only).
model User { id String @id @default(uuid()) name String email String @unique password String hashedRt String? role Role @default(USER) folders Folder[] documents Document[] documentPermissions DocumentPermission[] @@map("users") }model DocumentPermission { id String @id @default(uuid()) document Document @relation(fields: [documentId], references: [id]) documentId String user User @relation(fields: [userId], references: [id]) userId String permission Permission @@unique([documentId, userId]) @@map("documentPermissions") }model Folder { id String @id @default(uuid()) name String parentFolder Folder? @relation("FolderToFolder", fields: [parentFolderId], references: [id], onDelete: Cascade, onUpdate: Cascade) parentFolderId String? subFolders Folder[] @relation("FolderToFolder") createdBy User @relation(fields: [createdById], references: [id], onDelete: Cascade, onUpdate: Cascade) createdById String documents Document[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([name, createdById]) @@map("folders") }model Document { id String @id @default(uuid()) title String description String? tags String[] filePath String fileType FileType fileSize Int uploadedBy User @relation(fields: [uploadedById], references: [id]) uploadedById String folder Folder? @relation(fields: [folderId], references: [id]) folderId String? documentPermissions DocumentPermission[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@map("documents") }enum FileType { PDF DOCX DOC CSV XLS XLSX PPT PPTX TXT }enum Permission { VIEW EDIT DOWNLOAD }enum Role { USER ADMIN }The application uses a Global Exception Filter to catch and format all exceptions. The custom exception (CustomException) provides flexibility to send a structured response with a message and body. The exception is formatted as follows:
{ "status": 400, "message": "Unauthorized", "body": null }- Uses Multer to handle local file uploads.
- Uses Cloudinary to store and serve uploaded files.
- Validates file size and supported file types based on the
FileTypeenum.
- Authenticates users using access tokens.
- Supports refresh tokens for session extension.
- Tokens are secured via environment-configured secrets.
The API comes with built-in Swagger UI documentation that allows you to test all endpoints interactively. After starting the application, you can access the Swagger documentation at:
http://localhost:3000/docs - Interactive API testing: Try out endpoints directly from your browser
- Authentication support: Easily authorize using JWT tokens
- Request body templates: Pre-filled request bodies for all endpoints
- Response examples: See expected response formats
- Endpoint grouping: Organized by functionality (Auth, Users, Documents, etc.)
To use the Swagger UI:
- Start your application (
npm run start:dev) - Open
http://localhost:3000/docsin your browser - For protected endpoints:
- First authenticate via the Auth endpoints
- Click the "Authorize" button and enter your JWT token
- Now you can test all protected routes
The Swagger UI provides complete documentation for:
- All request parameters
- Required headers
- Possible response codes
- Response schemas
This API is designed to allow document management with a focus on user access control, file storage, and validation. It also includes JWT authentication and refresh tokens for secure user sessions.