Skip to main content
Releases represent albums, EPs, singles, and other music collections in your catalog. Each release contains tracks, metadata, and can be associated with artists and labels.

Overview

The release management system enables you to:

Create releases

Add albums, EPs, and singles with comprehensive metadata

Catalog management

Track UPCs, catalog numbers, and production years

Artist collaboration

Link multiple artists to releases for collaborations

Label organization

Associate releases with record labels

Release entity structure

The Release entity contains the following fields:
@Entity('releases')
@Unique([
  'title',
  'releaseDate',
  'productionYear',
  'userId',
  'labelId',
  'version',
])
export class Release extends AbstractEntity {
  // TITLE
  @Column({ name: 'title', nullable: false })
  title!: string;

  // COVER ART
  @Column({ name: 'cover_art', nullable: true })
  coverArt: string;

  // UPC
  @Column({ name: 'upc', nullable: true })
  upc: string;

  // RELEASE DATE
  @Column({ name: 'release_date', nullable: false })
  releaseDate: string;

  // VERSION
  @Column({ name: 'version', nullable: true })
  version: string;

  // PRODUCTION YEAR
  @Column({ name: 'production_year', nullable: false })
  productionYear: number;

  // CATALOG NUMBER
  @Column({ name: 'catalog_number', nullable: true })
  catalogNumber: string;

  // LABEL ID
  @Column({ name: 'label_id', nullable: true })
  labelId: string;

  // USER ID
  @Column({ name: 'user_id', nullable: false })
  userId: string;

  // Relations
  @ManyToOne(() => Label, (label) => label.releases, {
    onDelete: 'CASCADE',
    onUpdate: 'CASCADE',
  })
  label: Label;

  @ManyToOne(() => User, (user) => user.releases, {
    onDelete: 'CASCADE',
    onUpdate: 'CASCADE',
  })
  user: User;

  @OneToMany(() => ReleaseArtist, (releaseArtist) => releaseArtist.release)
  artists: ReleaseArtist[];

  @OneToMany(() => Track, (track) => track.release)
  tracks: Track[];
}

Core fields

FieldTypeRequiredDescription
idUUIDYesUnique identifier (inherited from AbstractEntity)
titlestringYesRelease title
coverArtstringNoURL or path to cover artwork
upcstringNoUniversal Product Code for distribution
releaseDatestringYesISO formatted release date
versionstringNoVersion identifier (e.g., ‘original’, ‘deluxe’, ‘remaster’)
productionYearnumberYesYear the release was produced
catalogNumberstringNoAuto-generated catalog number
labelIdUUIDNoReference to associated label
userIdUUIDYesReference to the owning user
createdAttimestampYesRecord creation time (inherited)
updatedAttimestampYesLast update time (inherited)

Unique constraint

A release is uniquely identified by the combination of title, releaseDate, productionYear, userId, labelId, and version. You cannot create duplicate releases with the same values for all these fields.
This prevents accidental duplicates while allowing:
  • Same title on different dates
  • Different versions (original, deluxe, etc.) of the same release
  • Same title under different labels

Relationships

A release can be associated with a label. When the label is deleted, all its releases are cascade deleted.
@ManyToOne(() => Label, (label) => label.releases, {
  onDelete: 'CASCADE',
  onUpdate: 'CASCADE',
})
Each release is owned by a user. When the user is deleted, all their releases are cascade deleted.
@ManyToOne(() => User, (user) => user.releases, {
  onDelete: 'CASCADE',
  onUpdate: 'CASCADE',
})
Releases connect to artists through the ReleaseArtist join entity, enabling many-to-many relationships for collaborations.
@OneToMany(() => ReleaseArtist, (releaseArtist) => releaseArtist.release)
artists: ReleaseArtist[];
A release contains multiple tracks. This is a one-to-many relationship.
@OneToMany(() => Track, (track) => track.release)
tracks: Track[];

Common operations

Create a release

Create a new release in your catalog:
POST /releases

{
  "title": "Summer Nights EP",
  "upc": "123456789012",
  "releaseDate": "2024-06-15",
  "version": "original",
  "productionYear": 2024,
  "labelId": "550e8400-e29b-41d4-a716-446655440000"
}
The system automatically:
  • Formats the releaseDate to ISO format using moment.js
  • Generates a catalogNumber based on the production year
  • Sets the userId from the authenticated user’s context
  • Defaults version to 'original' if not provided
Response:
{
  "message": "Release created successfully",
  "data": {
    "id": "...",
    "title": "Summer Nights EP",
    "catalogNumber": "CAT-2024-001",
    "releaseDate": "2024-06-15T00:00:00.000Z",
    "productionYear": 2024,
    "version": "original"
  }
}

Duplicate detection

Before creating a release, the system checks for duplicates:
const releaseExists = await this.releaseService.checkIfReleaseExists({
  labelId: labelId as string,
  productionYear,
  releaseDate: formattedReleaseDate,
  title,
  userId: user.id,
  version: version as string,
});
If a duplicate is found:
{
  "statusCode": 409,
  "message": "Release already exists",
  "data": {
    "id": "existing-release-id"
  }
}

Fetch releases

Retrieve releases with pagination and filtering:
GET /releases?page=0&size=10&labelId=<uuid>&userId=<uuid>
Query parameters:
  • page - Page number (default: 0)
  • size - Items per page (default: 10)
  • labelId - Filter by label (optional)
  • userId - Filter by user (optional for admins)
Regular users automatically see only their own releases:
GET /releases?page=0&size=10

// Filtered by:
{
  userId: user.id
}

Get release by ID

Retrieve a specific release with all related data:
GET /releases/:id
Response:
{
  "message": "Release fetched successfully",
  "data": {
    "id": "...",
    "title": "Summer Nights EP",
    "coverArt": "https://...",
    "releaseDate": "2024-06-15T00:00:00.000Z",
    "productionYear": 2024,
    "catalogNumber": "CAT-2024-001",
    "label": { /* label data */ },
    "user": { /* user data */ },
    "artists": [ /* artist data */ ],
    "tracks": [ /* track data */ ]
  }
}

Catalog number generation

Catalog numbers are automatically generated based on the production year:
import { generateCatalogNumber } from '../../helpers/strings.helper';

const catalogNumber = generateCatalogNumber(productionYear);
// Example output: "CAT-2024-001"
The catalog number format typically includes:
  • A prefix (e.g., “CAT”)
  • The production year
  • A sequential number
Catalog numbers are assigned automatically during release creation. You don’t need to provide them manually.

Version management

Releases support version identifiers for different editions:

Original

Default version for new releases

Deluxe

Extended editions with bonus content

Remaster

Remastered versions of existing releases
Versions allow you to:
  • Create multiple editions of the same release
  • Track different formats (vinyl, digital, CD)
  • Manage reissues and remasters
// Create original version
POST /releases
{
  "title": "Greatest Hits",
  "version": "original",
  "releaseDate": "2024-01-15",
  "productionYear": 2024
}

// Create deluxe version
POST /releases
{
  "title": "Greatest Hits",
  "version": "deluxe",
  "releaseDate": "2024-03-15",
  "productionYear": 2024
}

Use cases

Album release

Create a full-length album with metadata:
const album = await fetch('/releases', {
  method: 'POST',
  body: JSON.stringify({
    title: 'Midnight Dreams',
    upc: '123456789012',
    releaseDate: '2024-08-20',
    productionYear: 2024,
    version: 'original',
    labelId: myLabelId
  })
});

// Add tracks to the album
await addTracksToRelease(album.data.id, tracks);

EP release

Create an extended play release:
const ep = await fetch('/releases', {
  method: 'POST',
  body: JSON.stringify({
    title: 'Summer Vibes EP',
    releaseDate: '2024-06-01',
    productionYear: 2024,
    labelId: myLabelId
  })
});

Single release

Quick single release without label:
const single = await fetch('/releases', {
  method: 'POST',
  body: JSON.stringify({
    title: 'Hit Single',
    releaseDate: '2024-05-15',
    productionYear: 2024
    // labelId is optional for independent releases
  })
});

Collaboration release

Create a release with multiple artists:
// 1. Create the release
const collab = await createRelease({
  title: 'Epic Collaboration',
  releaseDate: '2024-07-01',
  productionYear: 2024
});

// 2. Link artists
await createReleaseArtist({
  releaseId: collab.id,
  artistId: artist1.id
});

await createReleaseArtist({
  releaseId: collab.id,
  artistId: artist2.id
});

Artists

Link artists to releases for proper attribution

Labels

Organize releases under labels

Lyrics

Add lyrics to tracks in your releases

Roles & Permissions

Control release management access

Build docs developers (and LLMs) love