Backend API Documentation

NestJS Backend API for GoGrab Space platform - Architecture, Best Practices, and Common Issues

Backend API Documentation

The GoGrab Space Backend API is built with NestJS and MongoDB. This documentation covers architecture, best practices, and common issues to avoid.

📁 Documentation Structure

🏗️ Architecture Overview

apis/backend-api/
├── src/
│   ├── app/
│   │   ├── spaces/              # Space management (SpacesController, SpaceModulesController)
│   │   ├── global-modules/      # Global module registry
│   │   ├── backend-auth/        # Authentication (JWT)
│   │   ├── analytics/           # User analytics
│   │   └── common/              # Shared utilities
│   └── main.ts                  # App bootstrap

⚡ Quick Links

🔴 Critical Rules

1. MongoDB Upsert Operations

NEVER include the same field in both $set and $setOnInsert:

// ❌ WRONG - Causes MongoDB conflict
await collection.updateOne(
  { _id: docId },
  {
    $set: {
      ...module,        // Contains createdAt!
      updatedAt: now
    },
    $setOnInsert: {
      createdAt: now    // CONFLICT! Same field in $set
    }
  },
  { upsert: true }
);

// ✅ CORRECT - createdAt only in $setOnInsert
await collection.updateOne(
  { _id: docId },
  {
    $set: {
      // All fields EXCEPT createdAt
      name: dto.name,
      updatedAt: now,
      // ...other fields
    },
    $setOnInsert: {
      createdAt: now    // Only set on insert
    }
  },
  { upsert: true }
);

See MongoDB Best Practices for more details.

2. ValidationPipe Configuration

The global ValidationPipe must allow flexible DTOs:

// main.ts
app.useGlobalPipes(
  new ValidationPipe({
    whitelist: false,           // DON'T strip unknown properties
    forbidNonWhitelisted: false, // DON'T throw on unknown properties
    transform: true,
    transformOptions: {
      enableImplicitConversion: true
    }
  })
);

Why? Our DTOs are TypeScript interfaces (not classes with decorators), so whitelist: true would strip all properties!

3. Controller Registration

All controllers must be registered in their module:

// spaces.module.ts
@Module({
  controllers: [SpacesController, SpaceModulesController], // Both registered!
  providers: [SpacesService, SpaceModulesService],
})
export class SpacesModule {}

📊 Related Documentation