Skip to content

BrandingService

reference
2 min readUpdated

Kind: Service

Source: atloria-monorepo/apps/api/src/branding/branding.service.ts

Project branding — identity + colors + support email. One row per project. Everything is nullable; the frontend falls back to project name + Atloria palette when a field is blank.

BrandingService manages per-project branding settings, including identity fields, color palette values, and support email configuration. It exposes internal read/update operations and a public-safe read path, while allowing all branding fields to remain nullable so clients can fall back to the project name and default Atloria palette.

Methods

MethodSignatureReturnsDescription
getget(projectId: string, user: JwtPayload)unknownGet the current branding for a project.
updateupdate(projectId: string, dto: UpdateBrandingDto, user: JwtPayload)unknownUpsert the project's branding.
getPublicgetPublic(projectId: string)unknownPublic read — used by the /public/p/:slugId endpoint to return branding alongside project info.

Dependencies

  • PrismaService
  • AuditService (optional)
  • PlanService (optional)

Where it refuses work

  • BrandingService stops the work with NotFoundException when !project — “Project not found”.
  • BrandingService stops the work with ForbiddenException when project.organizationId !== user.organizationId — “You do not have access to this project”.
  • BrandingService stops the work with BadRequestException when dto.primaryColor && !HEX_COLOR_REGEX.test(dto.primaryColor).
  • BrandingService stops the work with BadRequestException when dto.accentColor && !HEX_COLOR_REGEX.test(dto.accentColor).
  • BrandingService stops the work with BadRequestException when dto.supportEmail && !EMAIL_REGEX.test(dto.supportEmail).
  • BrandingService stops the work with BadRequestException when dto.theme && !THEME_PRESETS.includes(dto.theme as (typeof THEME_PRESETS)[number]).

When something fails

  • BrandingService handles failure in 3 places: it turns it into a return value in 2, and lets it reach the caller in 1.

Diagram

mermaid
sequenceDiagram
  participant Client
  participant BrandingController
  participant BrandingService
  participant BrandingRepository
  participant Project

  Client->>BrandingController: GET /projects/:projectId/branding
  BrandingController->>BrandingService: get(projectId)
  BrandingService->>BrandingRepository: find branding for project
  BrandingRepository-->>BrandingService: Branding row or null
  BrandingService-->>BrandingController: Branding settings
  BrandingController-->>Client: Branding response

  Client->>BrandingController: PATCH /projects/:projectId/branding
  BrandingController->>BrandingService: update(projectId, dto)
  BrandingService->>BrandingRepository: create or update branding row
  BrandingRepository-->>BrandingService: Updated branding
  BrandingService-->>BrandingController: Updated settings
  BrandingController-->>Client: Updated branding

  Client->>BrandingController: GET public branding
  BrandingController->>BrandingService: getPublic(projectId)
  BrandingService->>Project: resolve public project context
  BrandingService-->>BrandingController: Public branding fields
  BrandingController-->>Client: Public branding response

Usage

ts
import { Injectable } from '@nestjs/common';
import { BrandingService } from './branding.service';

@Injectable()
export class ProjectSettingsService {
  constructor(private readonly brandingService: BrandingService) {}

  async updateProjectBranding(projectId: string) {
    return this.brandingService.update(projectId, {
      name: 'Acme Portal',
      primaryColor: '#2563EB',
      secondaryColor: '#0F172A',
      supportEmail: 'support@acme.example',
    });
  }

  async getPublicProjectBranding(projectId: string) {
    const branding = await this.brandingService.getPublic(projectId);

    return {
      name: branding?.name ?? 'Project',
      primaryColor: branding?.primaryColor ?? '#6366F1',
      supportEmail: branding?.supportEmail ?? null,
    };
  }
}

AI Coding Instructions

  • Keep branding scoped to a single project; there should be at most one branding record per project.
  • Preserve nullable branding fields during reads and updates—do not replace missing values with defaults in persistence logic.
  • Apply frontend or response-layer fallbacks for blank values using the project name and Atloria palette.
  • Use getPublic() for unauthenticated or externally visible project experiences so only public-safe branding data is exposed.
  • When extending branding fields, update the DTOs, persistence schema, and public response mapping consistently.

Relationships

  • DEPENDS_ON → PrismaService
  • DEPENDS_ON → AuditService
  • DEPENDS_ON → PlanService

Referenced By

  • BrandingController (DEPENDS_ON)
  • BrandingModule (MODULE_PROVIDES)
  • BrandingModule (MODULE_EXPORTS)
  • DocVersionExportService (DEPENDS_ON)
  • PublicProjectController (DEPENDS_ON)

Was this page helpful?

Download as PDF