Skip to content

Node.js Express Backend API โ€‹

HaloLight Node.js backend API is built on Express 5 + TypeScript + Prisma, providing enterprise-grade RESTful API service.

API Documentation: https://halolight-api-node.h7ml.cn/docs

GitHub: https://github.com/halolight/halolight-api-node

Features โ€‹

  • ๐Ÿ” Dual JWT Tokens - Access Token + Refresh Token with auto-renewal
  • ๐Ÿ›ก๏ธ RBAC Permissions - Role-based access control with wildcard matching
  • ๐Ÿ“ก RESTful API - Standardized interface design with OpenAPI documentation
  • ๐Ÿ—„๏ธ Prisma ORM - Type-safe database operations
  • โœ… Data Validation - Zod request validation and error handling
  • ๐Ÿ“Š Logging System - Pino logging with request tracking
  • ๐Ÿณ Docker Support - Containerized deployment
  • ๐ŸŽฏ 12 Business Modules - 90+ RESTful endpoints

Tech Stack โ€‹

TechnologyVersionDescription
Node.js20+JavaScript runtime
Express5.xWeb framework
Prisma6.xDatabase ORM
PostgreSQL16Data storage
Zod3.xData validation
JWT9.xAuthentication
Pino9.xLogging system
Swagger UI5.xAPI documentation

Quick Start โ€‹

Requirements โ€‹

  • Node.js >= 20
  • pnpm >= 9
  • PostgreSQL (optional, defaults to SQLite)

Installation โ€‹

bash
# Clone repository
git clone https://github.com/halolight/halolight-api-node.git
cd halolight-api-node

# Install dependencies
pnpm install

Environment Variables โ€‹

bash
cp .env.example .env
env
# Database
DATABASE_URL="postgresql://user:password@localhost:5432/halolight?schema=public"

# JWT Secrets (must be โ‰ฅ32 characters)
JWT_SECRET="your-super-secret-jwt-key-minimum-32-characters-long"
JWT_EXPIRES_IN=7d
REFRESH_TOKEN_SECRET="your-refresh-secret-key-minimum-32-characters-long"
REFRESH_TOKEN_EXPIRES_IN=30d

# Service Configuration
PORT=3001
NODE_ENV=development

# CORS
CORS_ORIGIN="http://localhost:3000"

Database Initialization โ€‹

bash
# Generate Prisma Client
pnpm db:generate

# Push database changes
pnpm db:push

# Seed database (optional)
pnpm db:seed

Start Service โ€‹

bash
# Development mode
pnpm dev

# Production mode
pnpm build
pnpm start

Visit http://localhost:3001

Project Structure โ€‹

halolight-api-node/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ routes/               # Controllers/Route handlers
โ”‚   โ”‚   โ”œโ”€โ”€ auth.ts          # Authentication routes
โ”‚   โ”‚   โ”œโ”€โ”€ users.ts         # User management
โ”‚   โ”‚   โ”œโ”€โ”€ roles.ts         # Role management
โ”‚   โ”‚   โ”œโ”€โ”€ permissions.ts   # Permission management
โ”‚   โ”‚   โ”œโ”€โ”€ teams.ts         # Team management
โ”‚   โ”‚   โ”œโ”€โ”€ documents.ts     # Document management
โ”‚   โ”‚   โ”œโ”€โ”€ files.ts         # File management
โ”‚   โ”‚   โ”œโ”€โ”€ folders.ts       # Folder management
โ”‚   โ”‚   โ”œโ”€โ”€ calendar.ts      # Calendar events
โ”‚   โ”‚   โ”œโ”€โ”€ notifications.ts # Notification management
โ”‚   โ”‚   โ”œโ”€โ”€ messages.ts      # Message management
โ”‚   โ”‚   โ””โ”€โ”€ dashboard.ts     # Dashboard statistics
โ”‚   โ”œโ”€โ”€ services/            # Business logic layer
โ”‚   โ”œโ”€โ”€ middleware/          # Middleware
โ”‚   โ”‚   โ”œโ”€โ”€ auth.ts          # JWT auth + RBAC
โ”‚   โ”‚   โ”œโ”€โ”€ validate.ts      # Zod request validation
โ”‚   โ”‚   โ””โ”€โ”€ error.ts         # Global error handling
โ”‚   โ”œโ”€โ”€ utils/               # Utility functions
โ”‚   โ”œโ”€โ”€ config/              # Configuration files
โ”‚   โ”‚   โ”œโ”€โ”€ env.ts           # Environment variables
โ”‚   โ”‚   โ””โ”€โ”€ swagger.ts       # Swagger configuration
โ”‚   โ””โ”€โ”€ index.ts             # Application entry
โ”œโ”€โ”€ prisma/                  # Database migrations/Schema
โ”‚   โ””โ”€โ”€ schema.prisma        # Database models (17+ models)
โ”œโ”€โ”€ tests/                   # Test files
โ”œโ”€โ”€ Dockerfile               # Docker configuration
โ”œโ”€โ”€ docker-compose.yml
โ””โ”€โ”€ package.json

API Modules โ€‹

Authentication Endpoints โ€‹

MethodPathDescriptionPermission
POST/api/auth/loginUser loginPublic
POST/api/auth/registerUser registrationPublic
POST/api/auth/refreshRefresh tokenPublic
POST/api/auth/logoutUser logoutAuthenticated
POST/api/auth/forgot-passwordForgot passwordPublic
POST/api/auth/reset-passwordReset passwordPublic

User Management Endpoints โ€‹

MethodPathDescriptionPermission
GET/api/usersGet user listusers:view
GET/api/users/:idGet user detailsusers:view
POST/api/usersCreate userusers:create
PUT/api/users/:idUpdate userusers:update
DELETE/api/users/:idDelete userusers:delete
GET/api/users/meGet current userAuthenticated

Complete Endpoint List โ€‹

Role Management (Roles) - 6 endpoints โ€‹

MethodPathDescription
GET/api/rolesGet role list
GET/api/roles/:idGet role details
POST/api/rolesCreate role
PUT/api/roles/:idUpdate role
DELETE/api/roles/:idDelete role
PUT/api/roles/:id/permissionsAssign permissions

Permission Management (Permissions) - 4 endpoints โ€‹

MethodPathDescription
GET/api/permissionsGet permission list
GET/api/permissions/:idGet permission details
POST/api/permissionsCreate permission
DELETE/api/permissions/:idDelete permission

Team Management (Teams) - 7 endpoints โ€‹

MethodPathDescription
GET/api/teamsGet team list
GET/api/teams/:idGet team details
POST/api/teamsCreate team
PUT/api/teams/:idUpdate team
DELETE/api/teams/:idDelete team
POST/api/teams/:id/membersAdd member
DELETE/api/teams/:id/members/:userIdRemove member

Document Management (Documents) - 11 endpoints โ€‹

MethodPathDescription
GET/api/documentsGet document list
GET/api/documents/:idGet document details
POST/api/documentsCreate document
PUT/api/documents/:idUpdate document
DELETE/api/documents/:idDelete document
POST/api/documents/:id/shareShare document
DELETE/api/documents/:id/share/:shareIdUnshare document
POST/api/documents/:id/tagsAdd tag
DELETE/api/documents/:id/tags/:tagIdRemove tag
POST/api/documents/:id/moveMove document
POST/api/documents/:id/copyCopy document

File Management (Files) - 14 endpoints โ€‹

MethodPathDescription
GET/api/filesGet file list
GET/api/files/:idGet file details
POST/api/files/uploadUpload file
GET/api/files/:id/downloadDownload file
PUT/api/files/:idUpdate file info
DELETE/api/files/:idDelete file
POST/api/files/:id/moveMove file
POST/api/files/:id/copyCopy file
POST/api/files/:id/shareShare file
GET/api/files/:id/versionsGet version history
GET/api/files/storageGet storage info
POST/api/files/batch-deleteBatch delete
POST/api/files/batch-moveBatch move
POST/api/files/searchSearch files

Folder Management (Folders) - 5 endpoints โ€‹

MethodPathDescription
GET/api/foldersGet folder tree
POST/api/foldersCreate folder
PUT/api/folders/:idUpdate folder
DELETE/api/folders/:idDelete folder
POST/api/folders/:id/moveMove folder

Calendar Management (Calendar) - 9 endpoints โ€‹

MethodPathDescription
GET/api/calendar/eventsGet event list
GET/api/calendar/events/:idGet event details
POST/api/calendar/eventsCreate event
PUT/api/calendar/events/:idUpdate event
DELETE/api/calendar/events/:idDelete event
POST/api/calendar/events/:id/attendeesAdd attendee
DELETE/api/calendar/events/:id/attendees/:userIdRemove attendee
POST/api/calendar/events/:id/reminderSet reminder
GET/api/calendar/events/upcomingGet upcoming events

Notification Management (Notifications) - 5 endpoints โ€‹

MethodPathDescription
GET/api/notificationsGet notification list
GET/api/notifications/:idGet notification details
PUT/api/notifications/:id/readMark as read
PUT/api/notifications/read-allMark all as read
DELETE/api/notifications/:idDelete notification

Message Management (Messages) - 5 endpoints โ€‹

MethodPathDescription
GET/api/messages/conversationsGet conversation list
GET/api/messages/conversations/:idGet conversation details
POST/api/messagesSend message
PUT/api/messages/:id/readMark as read
DELETE/api/messages/:idDelete message

Dashboard (Dashboard) - 9 endpoints โ€‹

MethodPathDescription
GET/api/dashboard/statsStatistics data
GET/api/dashboard/visitsVisit trends
GET/api/dashboard/salesSales data
GET/api/dashboard/piePie chart data
GET/api/dashboard/tasksTo-do tasks
GET/api/dashboard/calendarToday's events
GET/api/dashboard/activitiesRecent activities
GET/api/dashboard/notificationsUnread notifications
GET/api/dashboard/usersUser statistics

Authentication Mechanism โ€‹

Dual JWT Tokens โ€‹

Access Token:  7 days validity, used for API requests
Refresh Token: 30 days validity, used to refresh Access Token

Request Headers โ€‹

http
Authorization: Bearer <access_token>

Refresh Flow โ€‹

typescript
// POST /api/auth/refresh
const response = await fetch('/api/auth/refresh', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ refreshToken: 'your_refresh_token' })
});

const { accessToken, refreshToken } = await response.json();
// Update locally stored tokens
localStorage.setItem('accessToken', accessToken);
localStorage.setItem('refreshToken', refreshToken);

Permission System โ€‹

Role Definitions โ€‹

RoleDescriptionPermissions
super_adminSuper Administrator* (all permissions)
adminAdministratorusers:*, documents:*, files:*, teams:*
userRegular Userdocuments:view, files:view, calendar:*
guestGuestdashboard:view

Permission Format โ€‹

{resource}:{action}

Examples:
- users:view      # View users
- users:create    # Create users
- users:*         # All user operations
- *               # All permissions

Permission Validation Example โ€‹

typescript
// Using permission middleware in routes
import { requireAuth, requirePermission } from './middleware/auth';

// Requires authentication
router.get('/api/users/me', requireAuth, getUserProfile);

// Requires specific permission
router.post('/api/users', requireAuth, requirePermission('users:create'), createUser);

// Requires one of multiple permissions
router.put('/api/users/:id', requireAuth, requirePermission(['users:update', 'users:*']), updateUser);

Error Handling โ€‹

Error Response Format โ€‹

json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      { "field": "email", "message": "Invalid email format" }
    ]
  }
}

Error Codes โ€‹

Status CodeError CodeDescription
400VALIDATION_ERRORValidation failed
401UNAUTHORIZEDUnauthorized
403FORBIDDENForbidden
404NOT_FOUNDResource not found
409CONFLICTResource conflict
500INTERNAL_ERRORInternal server error

Common Commands โ€‹

bash
# Development
pnpm dev              # Start dev server (hot reload)
pnpm build            # TypeScript compilation
pnpm start            # Start production server

# Testing
pnpm test             # Run tests
pnpm test:coverage    # Test coverage

# Database
pnpm db:generate      # Generate Prisma Client
pnpm db:push          # Push database changes
pnpm db:migrate       # Run migrations
pnpm db:studio        # Prisma Studio (database GUI)
pnpm db:seed          # Seed database

# Code Quality
pnpm lint             # ESLint check
pnpm lint:fix         # Auto fix
pnpm format           # Prettier formatting

Deployment โ€‹

Docker โ€‹

bash
docker build -t halolight-api-node .
docker run -p 3001:3001 halolight-api-node

Docker Compose โ€‹

bash
docker-compose up -d
yaml
# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "3001:3001"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=${DATABASE_URL}
      - JWT_SECRET=${JWT_SECRET}
      - REFRESH_TOKEN_SECRET=${REFRESH_TOKEN_SECRET}
    restart: unless-stopped
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: halolight
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

volumes:
  postgres_data:

Production Configuration โ€‹

env
NODE_ENV=production
PORT=3001
DATABASE_URL=postgresql://user:pass@host:5432/halolight
JWT_SECRET=your-production-secret-minimum-32-characters
REFRESH_TOKEN_SECRET=your-refresh-secret-minimum-32-characters
CORS_ORIGIN=https://yourdomain.com

Testing โ€‹

Running Tests โ€‹

bash
# Run all tests
pnpm test

# Run test coverage
pnpm test:coverage

# Watch mode
pnpm test:watch

Test Examples โ€‹

typescript
// tests/auth.test.ts
import request from 'supertest';
import app from '../src/index';

describe('Authentication', () => {
  it('should login successfully', async () => {
    const response = await request(app)
      .post('/api/auth/login')
      .send({
        email: 'admin@halolight.h7ml.cn',
        password: 'password123'
      });

    expect(response.status).toBe(200);
    expect(response.body.success).toBe(true);
    expect(response.body.data).toHaveProperty('accessToken');
    expect(response.body.data).toHaveProperty('refreshToken');
  });

  it('should reject invalid credentials', async () => {
    const response = await request(app)
      .post('/api/auth/login')
      .send({
        email: 'admin@halolight.h7ml.cn',
        password: 'wrongpassword'
      });

    expect(response.status).toBe(401);
    expect(response.body.success).toBe(false);
  });
});

Performance Metrics โ€‹

Benchmarks โ€‹

MetricValueCondition
Request Throughput~8,000 req/sSingle core, simple queries
Average Response Time<10msLocal database, no complex queries
Memory Usage~80MBBase memory after startup
CPU Usage<5%Idle state

Performance Optimization Tips โ€‹

  • Use connection pooling for database connections
  • Enable database indexes to optimize queries
  • Implement caching strategy (Redis)
  • Use CDN for static assets
  • Enable Gzip compression

Observability โ€‹

Logging System โ€‹

typescript
// Using Pino logging
import pino from 'pino';

const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  transport: {
    target: 'pino-pretty',
    options: {
      colorize: true,
      translateTime: 'yyyy-mm-dd HH:MM:ss',
      ignore: 'pid,hostname'
    }
  }
});

// Log requests
app.use((req, res, next) => {
  logger.info({
    method: req.method,
    url: req.url,
    ip: req.ip
  }, 'Incoming request');
  next();
});

Health Check โ€‹

typescript
// GET /health
app.get('/health', async (req, res) => {
  const health = {
    status: 'healthy',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    database: 'connected'
  };

  try {
    // Check database connection
    await prisma.$queryRaw`SELECT 1`;
  } catch (error) {
    health.status = 'unhealthy';
    health.database = 'disconnected';
    return res.status(503).json(health);
  }

  res.json(health);
});

Monitoring Metrics โ€‹

typescript
// Prometheus metrics integration
import promClient from 'prom-client';

const register = new promClient.Registry();

// Default metrics
promClient.collectDefaultMetrics({ register });

// Custom metrics
const httpRequestDuration = new promClient.Histogram({
  name: 'http_request_duration_seconds',
  help: 'Duration of HTTP requests in seconds',
  labelNames: ['method', 'route', 'status_code'],
  registers: [register]
});

// Expose metrics endpoint
app.get('/metrics', async (req, res) => {
  res.set('Content-Type', register.contentType);
  res.send(await register.metrics());
});

FAQ โ€‹

Q: How to share database across multiple services? โ€‹

A: Configure the same DATABASE_URL and ensure identical Prisma Schema.

env
# All services use the same database connection
DATABASE_URL="postgresql://user:pass@shared-db:5432/halolight"
bash
# Ensure Schema consistency
pnpm db:push

Q: How to auto-refresh JWT tokens when expired? โ€‹

A: Detect 401 errors in frontend interceptor and automatically call refresh endpoint.

typescript
// Axios interceptor example
axios.interceptors.response.use(
  response => response,
  async error => {
    const originalRequest = error.config;

    if (error.response?.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;

      try {
        const refreshToken = localStorage.getItem('refreshToken');
        const { data } = await axios.post('/api/auth/refresh', { refreshToken });

        localStorage.setItem('accessToken', data.accessToken);
        localStorage.setItem('refreshToken', data.refreshToken);

        originalRequest.headers['Authorization'] = `Bearer ${data.accessToken}`;
        return axios(originalRequest);
      } catch (err) {
        // Refresh failed, redirect to login
        window.location.href = '/login';
        return Promise.reject(err);
      }
    }

    return Promise.reject(error);
  }
);

Q: How to implement file upload restrictions? โ€‹

A: Use multer middleware to configure file size and type limits.

typescript
import multer from 'multer';

const upload = multer({
  limits: {
    fileSize: 10 * 1024 * 1024, // 10MB
  },
  fileFilter: (req, file, cb) => {
    const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
    if (allowedTypes.includes(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error('Unsupported file type'));
    }
  }
});

router.post('/api/files/upload', upload.single('file'), uploadFile);

Q: How to enable HTTPS? โ€‹

A: Use Nginx reverse proxy or configure SSL certificates in Express.

typescript
// Enable HTTPS in Express
import https from 'https';
import fs from 'fs';

const options = {
  key: fs.readFileSync('path/to/private-key.pem'),
  cert: fs.readFileSync('path/to/certificate.pem')
};

https.createServer(options, app).listen(443, () => {
  console.log('HTTPS server running on port 443');
});

Development Tools โ€‹

  • Prisma Studio - Visual database management tool (pnpm db:studio)
  • Postman - API testing tool (can import Swagger docs)
  • VSCode Extension Pack - ESLint + Prettier + TypeScript
  • Docker Desktop - Container management
  • pgAdmin - PostgreSQL database management

VSCode Configuration โ€‹

json
// .vscode/settings.json
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "typescript.tsdk": "node_modules/typescript/lib"
}

Comparison with Other Backends โ€‹

FeatureExpressNestJSFastifyKoa
LanguageJavaScript/TypeScriptTypeScriptJavaScript/TypeScriptJavaScript/TypeScript
Performanceโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญ
Learning Curveโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญ
Ecosystemโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญ
Built-in Featuresโญโญโญโญโญโญโญโญโญโญโญโญโญ
Community Supportโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญ

Why Choose Express? โ€‹

  • Mature and Stable - Over 10 years of production validation
  • High Flexibility - Minimalist framework with free middleware composition
  • Rich Ecosystem - Massive third-party plugins and tools
  • Low Learning Curve - Simple and easy to understand for quick start
  • Active Community - Abundant documentation and Q&A resources