or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-objects.mdbucket-operations.mdclient-setup.mdindex.mdnotifications.mdobject-operations.mdpresigned-operations.mdtypes-and-errors.md

advanced-objects.mddocs/

0

# Advanced Object Operations

1

2

This document covers advanced object features including multipart uploads, object composition, S3 Select queries, object retention, legal hold, and sophisticated copy operations with conditions.

3

4

## Copy Operations

5

6

### CopySourceOptions Class

7

8

```typescript { .api }

9

import { CopySourceOptions } from 'minio'

10

11

class CopySourceOptions {

12

constructor(options: ICopySourceOptions)

13

14

// Methods

15

validate(): boolean

16

getHeaders(): RequestHeaders

17

}

18

19

interface ICopySourceOptions {

20

Bucket: string // Source bucket name

21

Object: string // Source object name

22

VersionID?: string // Source version ID

23

MatchETag?: string // Copy only if ETag matches

24

NoMatchETag?: string // Copy only if ETag doesn't match

25

MatchModifiedSince?: string | null // Copy if modified since date

26

MatchUnmodifiedSince?: string | null // Copy if unmodified since date

27

MatchRange?: boolean // Enable byte range matching

28

Start?: number // Start byte for range

29

End?: number // End byte for range

30

Encryption?: Encryption // Source object encryption

31

}

32

```

33

34

### CopyDestinationOptions Class

35

36

```typescript { .api }

37

import { CopyDestinationOptions } from 'minio'

38

39

class CopyDestinationOptions {

40

constructor(options: ICopyDestinationOptions)

41

42

// Methods

43

validate(): boolean

44

getHeaders(): RequestHeaders

45

}

46

47

interface ICopyDestinationOptions {

48

Bucket: string // Destination bucket name

49

Object: string // Destination object name

50

Encryption?: Encryption // Destination object encryption

51

UserMetadata?: ObjectMetaData // Custom metadata

52

UserTags?: Record<string, string> | string // Object tags

53

LegalHold?: 'on' | 'off' // Legal hold status

54

RetainUntilDate?: string // Retention until date

55

Mode?: RETENTION_MODES // Retention mode

56

MetadataDirective?: 'COPY' | 'REPLACE' // Metadata handling

57

Headers?: Record<string, string> // Additional headers

58

}

59

```

60

61

### Advanced Copy Examples

62

63

```javascript { .api }

64

import {

65

CopySourceOptions,

66

CopyDestinationOptions,

67

RETENTION_MODES,

68

ENCRYPTION_TYPES

69

} from 'minio'

70

71

// Copy with conditions

72

const source = new CopySourceOptions({

73

Bucket: 'source-bucket',

74

Object: 'source-file.pdf',

75

MatchETag: '"abc123def456"', // Only copy if ETag matches

76

MatchModifiedSince: '2023-01-01T00:00:00Z' // Only copy if modified after date

77

})

78

79

const dest = new CopyDestinationOptions({

80

Bucket: 'dest-bucket',

81

Object: 'copied-file.pdf',

82

MetadataDirective: 'REPLACE', // Replace metadata

83

UserMetadata: {

84

'x-amz-meta-copied-by': 'system',

85

'x-amz-meta-copy-date': new Date().toISOString()

86

},

87

UserTags: {

88

'source-bucket': 'source-bucket',

89

'copy-reason': 'backup'

90

}

91

})

92

93

const result = await client.copyObject(source, dest)

94

95

// Copy with encryption

96

const encryptedSource = new CopySourceOptions({

97

Bucket: 'encrypted-bucket',

98

Object: 'secret-file.txt',

99

Encryption: {

100

type: ENCRYPTION_TYPES.SSEC,

101

SSECustomerKey: 'source-encryption-key-32-chars',

102

SSECustomerKeyMD5: 'md5-hash-of-source-key'

103

}

104

})

105

106

const encryptedDest = new CopyDestinationOptions({

107

Bucket: 'dest-bucket',

108

Object: 'copied-secret.txt',

109

Encryption: {

110

type: ENCRYPTION_TYPES.SSEC,

111

SSECustomerKey: 'dest-encryption-key-32-chars!!',

112

SSECustomerKeyMD5: 'md5-hash-of-dest-key'

113

}

114

})

115

116

await client.copyObject(encryptedSource, encryptedDest)

117

118

// Copy partial object (byte range)

119

const partialSource = new CopySourceOptions({

120

Bucket: 'large-files',

121

Object: 'huge-file.dat',

122

MatchRange: true,

123

Start: 1024, // Start at byte 1024

124

End: 2047 // End at byte 2047 (1024 bytes total)

125

})

126

127

const partialDest = new CopyDestinationOptions({

128

Bucket: 'excerpts',

129

Object: 'file-excerpt.dat'

130

})

131

132

await client.copyObject(partialSource, partialDest)

133

```

134

135

## Object Composition

136

137

### Compose Multiple Objects

138

139

```javascript { .api }

140

const result = await client.composeObject(destObjConfig, sourceObjList)

141

142

// Parameters

143

destObjConfig: CopyDestinationOptions // Destination configuration

144

sourceObjList: CopySourceOptions[] // Array of source objects

145

146

// Returns: Promise<CopyObjectResult>

147

```

148

149

#### CopyObjectResult Interface

150

151

```typescript { .api }

152

interface CopyObjectResult {

153

etag: string // Composed object ETag

154

lastModified: Date // Last modified timestamp

155

versionId?: string // Version ID if versioning enabled

156

}

157

```

158

159

#### Composition Examples

160

161

```javascript { .api }

162

// Compose multiple log files into one

163

const sources = [

164

new CopySourceOptions({ Bucket: 'logs', Object: 'app-2023-01.log' }),

165

new CopySourceOptions({ Bucket: 'logs', Object: 'app-2023-02.log' }),

166

new CopySourceOptions({ Bucket: 'logs', Object: 'app-2023-03.log' })

167

]

168

169

const destination = new CopyDestinationOptions({

170

Bucket: 'archives',

171

Object: 'app-q1-2023.log',

172

UserMetadata: {

173

'x-amz-meta-composed-from': '3-monthly-logs',

174

'x-amz-meta-quarter': 'Q1-2023'

175

}

176

})

177

178

const result = await client.composeObject(destination, sources)

179

console.log('Composed object ETag:', result.etag)

180

181

// Compose with partial objects

182

const partialSources = [

183

new CopySourceOptions({

184

Bucket: 'data',

185

Object: 'file1.dat',

186

MatchRange: true,

187

Start: 0,

188

End: 1023 // First 1024 bytes

189

}),

190

new CopySourceOptions({

191

Bucket: 'data',

192

Object: 'file2.dat',

193

MatchRange: true,

194

Start: 1024,

195

End: 2047 // Second 1024 bytes

196

})

197

]

198

199

const composedDest = new CopyDestinationOptions({

200

Bucket: 'combined',

201

Object: 'merged-data.dat'

202

})

203

204

await client.composeObject(composedDest, partialSources)

205

```

206

207

## S3 Select Operations

208

209

### Select Object Content

210

211

```javascript { .api }

212

const results = await client.selectObjectContent(bucketName, objectName, selectOpts)

213

214

// Parameters

215

bucketName: string // Bucket name

216

objectName: string // Object name

217

selectOpts: SelectOptions // Query options

218

219

// Returns: Promise<SelectResults | undefined>

220

```

221

222

#### SelectOptions Interface

223

224

```typescript { .api }

225

interface SelectOptions {

226

expression: string // SQL-like query expression

227

expressionType: 'SQL' // Query language (currently only SQL)

228

inputSerialization: { // Input format configuration

229

CSV?: {

230

FileHeaderInfo?: 'USE' | 'IGNORE' | 'NONE'

231

RecordDelimiter?: string // Record separator (default: \n)

232

FieldDelimiter?: string // Field separator (default: ,)

233

QuoteCharacter?: string // Quote character (default: ")

234

QuoteEscapeCharacter?: string // Quote escape (default: ")

235

Comments?: string // Comment prefix

236

AllowQuotedRecordDelimiter?: boolean

237

}

238

JSON?: {

239

Type: 'DOCUMENT' | 'LINES' // JSON format type

240

}

241

Parquet?: {} // Parquet format (empty object)

242

CompressionType?: 'NONE' | 'GZIP' | 'BZIP2' // Compression

243

}

244

outputSerialization: { // Output format configuration

245

CSV?: {

246

RecordDelimiter?: string // Record separator

247

FieldDelimiter?: string // Field separator

248

QuoteCharacter?: string // Quote character

249

QuoteEscapeCharacter?: string // Quote escape

250

QuoteFields?: 'ALWAYS' | 'ASNEEDED' // When to quote

251

}

252

JSON?: {

253

RecordDelimiter?: string // Record separator

254

}

255

}

256

requestProgress?: boolean // Include progress information

257

}

258

```

259

260

#### SelectResults Class

261

262

```typescript { .api }

263

class SelectResults {

264

// Methods

265

setStats(stats: string): void

266

getStats(): string

267

setProgress(progress: unknown): void

268

getProgress(): unknown

269

setResponse(response: unknown): void

270

getResponse(): unknown

271

setRecords(records: unknown): void

272

getRecords(): unknown

273

}

274

```

275

276

#### S3 Select Examples

277

278

```javascript { .api }

279

// Query CSV file

280

const csvSelectOptions = {

281

expression: 'SELECT name, age FROM s3object WHERE age > 25',

282

expressionType: 'SQL',

283

inputSerialization: {

284

CSV: {

285

FileHeaderInfo: 'USE', // First row contains headers

286

FieldDelimiter: ',',

287

RecordDelimiter: '\n'

288

},

289

CompressionType: 'NONE'

290

},

291

outputSerialization: {

292

CSV: {

293

FieldDelimiter: ',',

294

RecordDelimiter: '\n'

295

}

296

},

297

requestProgress: true

298

}

299

300

const results = await client.selectObjectContent('data-bucket', 'users.csv', csvSelectOptions)

301

if (results) {

302

console.log('Query results:', results.getRecords())

303

console.log('Query stats:', results.getStats())

304

}

305

306

// Query JSON Lines file

307

const jsonSelectOptions = {

308

expression: 'SELECT * FROM s3object[*] WHERE status = "active"',

309

expressionType: 'SQL',

310

inputSerialization: {

311

JSON: { Type: 'LINES' },

312

CompressionType: 'GZIP'

313

},

314

outputSerialization: {

315

JSON: { RecordDelimiter: '\n' }

316

}

317

}

318

319

const jsonResults = await client.selectObjectContent('logs', 'events.jsonl.gz', jsonSelectOptions)

320

321

// Query with aggregation

322

const aggregateQuery = {

323

expression: 'SELECT category, COUNT(*) as count, AVG(price) as avg_price FROM s3object GROUP BY category',

324

expressionType: 'SQL',

325

inputSerialization: {

326

CSV: {

327

FileHeaderInfo: 'USE',

328

FieldDelimiter: ','

329

}

330

},

331

outputSerialization: {

332

CSV: { FieldDelimiter: ',' }

333

}

334

}

335

336

const aggregateResults = await client.selectObjectContent('sales', 'products.csv', aggregateQuery)

337

```

338

339

## Object Retention and Legal Hold

340

341

### Object Retention

342

343

#### Get Object Retention

344

345

```javascript { .api }

346

const retention = await client.getObjectRetention(bucketName, objectName, getOpts?)

347

348

// Parameters

349

bucketName: string // Bucket name

350

objectName: string // Object name

351

getOpts?: GetObjectRetentionOpts // Options

352

353

// Returns: Promise<ObjectRetentionInfo | null>

354

```

355

356

#### Set Object Retention

357

358

```javascript { .api }

359

await client.putObjectRetention(bucketName, objectName, retentionOpts?)

360

361

// Parameters

362

bucketName: string // Bucket name

363

objectName: string // Object name

364

retentionOpts?: Retention // Retention configuration

365

366

// Returns: Promise<void>

367

```

368

369

#### Retention Types

370

371

```typescript { .api }

372

interface GetObjectRetentionOpts {

373

versionId?: string // Specific version ID

374

}

375

376

interface ObjectRetentionInfo {

377

mode: RETENTION_MODES // GOVERNANCE or COMPLIANCE

378

retainUntilDate: Date // Retention expiry date

379

}

380

381

interface Retention {

382

mode: RETENTION_MODES // Retention mode

383

retainUntilDate: Date // Retention until date

384

governanceBypass?: boolean // Bypass governance retention (requires permission)

385

}

386

```

387

388

#### Retention Examples

389

390

```javascript { .api }

391

import { RETENTION_MODES } from 'minio'

392

393

// Set governance retention for 30 days

394

const retentionDate = new Date()

395

retentionDate.setDate(retentionDate.getDate() + 30)

396

397

await client.putObjectRetention('compliance-bucket', 'important-doc.pdf', {

398

mode: RETENTION_MODES.GOVERNANCE,

399

retainUntilDate: retentionDate

400

})

401

402

// Set compliance retention (cannot be bypassed)

403

const complianceDate = new Date()

404

complianceDate.setFullYear(complianceDate.getFullYear() + 7) // 7 years

405

406

await client.putObjectRetention('legal-docs', 'contract.pdf', {

407

mode: RETENTION_MODES.COMPLIANCE,

408

retainUntilDate: complianceDate

409

})

410

411

// Check current retention

412

const retention = await client.getObjectRetention('compliance-bucket', 'important-doc.pdf')

413

if (retention) {

414

console.log('Retention mode:', retention.mode)

415

console.log('Retain until:', retention.retainUntilDate)

416

} else {

417

console.log('No retention set')

418

}

419

420

// Bypass governance retention (requires s3:BypassGovernanceRetention permission)

421

await client.putObjectRetention('compliance-bucket', 'temp-doc.pdf', {

422

mode: RETENTION_MODES.GOVERNANCE,

423

retainUntilDate: new Date(), // Immediate expiry

424

governanceBypass: true

425

})

426

```

427

428

### Legal Hold

429

430

#### Get Legal Hold Status

431

432

```javascript { .api }

433

const status = await client.getObjectLegalHold(bucketName, objectName, getOpts?)

434

435

// Parameters

436

bucketName: string // Bucket name

437

objectName: string // Object name

438

getOpts?: GetObjectLegalHoldOptions // Options

439

440

// Returns: Promise<LEGAL_HOLD_STATUS>

441

```

442

443

#### Set Legal Hold

444

445

```javascript { .api }

446

await client.setObjectLegalHold(bucketName, objectName, setOpts?)

447

448

// Parameters

449

bucketName: string // Bucket name

450

objectName: string // Object name

451

setOpts?: PutObjectLegalHoldOptions // Legal hold configuration

452

453

// Returns: Promise<void>

454

```

455

456

#### Legal Hold Types

457

458

```typescript { .api }

459

interface GetObjectLegalHoldOptions {

460

versionId?: string // Specific version ID

461

}

462

463

interface PutObjectLegalHoldOptions {

464

versionId?: string // Specific version ID

465

status: LEGAL_HOLD_STATUS // ON or OFF

466

}

467

468

enum LEGAL_HOLD_STATUS {

469

ENABLED = 'ON', // Legal hold is active

470

DISABLED = 'OFF' // Legal hold is not active

471

}

472

```

473

474

#### Legal Hold Examples

475

476

```javascript { .api }

477

import { LEGAL_HOLD_STATUS } from 'minio'

478

479

// Enable legal hold

480

await client.setObjectLegalHold('legal-bucket', 'evidence.pdf', {

481

status: LEGAL_HOLD_STATUS.ENABLED

482

})

483

484

// Check legal hold status

485

const holdStatus = await client.getObjectLegalHold('legal-bucket', 'evidence.pdf')

486

console.log('Legal hold status:', holdStatus) // 'ON' or 'OFF'

487

488

// Disable legal hold

489

await client.setObjectLegalHold('legal-bucket', 'evidence.pdf', {

490

status: LEGAL_HOLD_STATUS.DISABLED

491

})

492

493

// Set legal hold on specific version

494

await client.setObjectLegalHold('versioned-bucket', 'document.txt', {

495

versionId: 'version-123',

496

status: LEGAL_HOLD_STATUS.ENABLED

497

})

498

```

499

500

## Copy Conditions (Legacy)

501

502

The `CopyConditions` class provides legacy support for copy conditions:

503

504

```typescript { .api }

505

import { CopyConditions } from 'minio'

506

507

class CopyConditions {

508

// Properties

509

modified: string // Modified since condition

510

unmodified: string // Unmodified since condition

511

matchETag: string // ETag match condition

512

matchETagExcept: string // ETag not match condition

513

514

// Methods

515

setModified(date: Date): void

516

setUnmodified(date: Date): void

517

setMatchETag(etag: string): void

518

setMatchETagExcept(etag: string): void

519

}

520

```

521

522

#### Legacy Copy Example

523

524

```javascript { .api }

525

import { CopyConditions } from 'minio'

526

527

// Legacy copy conditions (deprecated - use CopySourceOptions instead)

528

const conditions = new CopyConditions()

529

conditions.setMatchETag('"abc123def456"')

530

conditions.setModified(new Date('2023-01-01'))

531

532

// Note: This is legacy API, prefer CopySourceOptions for new code

533

```

534

535

## Server-Side Encryption

536

537

### Encryption Types

538

539

```typescript { .api }

540

import { ENCRYPTION_TYPES } from 'minio'

541

542

type Encryption =

543

| {

544

type: ENCRYPTION_TYPES.SSEC // Server-side encryption with customer keys

545

SSECustomerKey?: string // Base64 encoded 256-bit key

546

SSECustomerKeyMD5?: string // MD5 hash of the key

547

}

548

| {

549

type: ENCRYPTION_TYPES.KMS // Server-side encryption with KMS

550

SSEAlgorithm?: string // Encryption algorithm

551

KMSMasterKeyID?: string // KMS key ID

552

}

553

```

554

555

### Encryption Examples

556

557

```javascript { .api }

558

import { ENCRYPTION_TYPES } from 'minio'

559

560

// Upload with SSE-C encryption

561

const sseCustomerKey = 'your-32-character-secret-key-here!!'

562

const uploadInfo = await client.putObject('encrypted-bucket', 'secret.txt', 'confidential data', undefined, {

563

'Content-Type': 'text/plain',

564

// Encryption headers are handled automatically when using CopySourceOptions/CopyDestinationOptions

565

})

566

567

// Copy with different encryption

568

const source = new CopySourceOptions({

569

Bucket: 'source-bucket',

570

Object: 'encrypted-file.txt',

571

Encryption: {

572

type: ENCRYPTION_TYPES.SSEC,

573

SSECustomerKey: 'source-key-32-characters-long!!!',

574

SSECustomerKeyMD5: 'md5-hash-of-source-key'

575

}

576

})

577

578

const dest = new CopyDestinationOptions({

579

Bucket: 'dest-bucket',

580

Object: 'reencrypted-file.txt',

581

Encryption: {

582

type: ENCRYPTION_TYPES.KMS,

583

KMSMasterKeyID: 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012'

584

}

585

})

586

587

await client.copyObject(source, dest)

588

```

589

590

## Error Handling

591

592

```javascript { .api }

593

import { S3Error, InvalidArgumentError } from 'minio'

594

595

try {

596

await client.selectObjectContent('bucket', 'data.csv', selectOptions)

597

} catch (error) {

598

if (error instanceof S3Error) {

599

switch (error.code) {

600

case 'NoSuchKey':

601

console.error('Object not found')

602

break

603

case 'InvalidRequest':

604

console.error('Invalid S3 Select query:', error.message)

605

break

606

case 'AccessDenied':

607

console.error('Access denied for S3 Select operation')

608

break

609

default:

610

console.error('S3 Error:', error.code, error.message)

611

}

612

}

613

}

614

615

// Handle retention/legal hold errors

616

try {

617

await client.putObjectRetention('bucket', 'object', retention)

618

} catch (error) {

619

if (error instanceof S3Error) {

620

switch (error.code) {

621

case 'InvalidRequest':

622

console.error('Object locking not enabled on bucket')

623

break

624

case 'AccessDenied':

625

console.error('Insufficient permissions for retention operation')

626

break

627

}

628

}

629

}

630

```

631

632

## Best Practices

633

634

### 1. Copy Operations

635

- Use conditions to ensure data consistency

636

- Validate source objects exist before copying

637

- Consider storage classes for destination objects

638

- Handle large object copies with appropriate timeouts

639

640

### 2. Object Composition

641

- Limit the number of source objects (max 10,000)

642

- Ensure all source objects are in the same bucket

643

- Consider the total size of composed object

644

- Use composition for log aggregation and data merging

645

646

### 3. S3 Select

647

- Use S3 Select to reduce data transfer costs

648

- Optimize queries for performance

649

- Consider input format and compression

650

- Test queries with small datasets first

651

652

### 4. Retention and Legal Hold

653

- Plan retention policies carefully (compliance mode cannot be changed)

654

- Document legal hold processes and requirements

655

- Monitor retention expiry dates

656

- Ensure proper permissions for governance bypass

657

658

### 5. Encryption

659

- Use consistent encryption strategies

660

- Securely manage encryption keys

661

- Consider key rotation policies

662

- Document encryption requirements and procedures

663

664

---

665

666

**Next:** [Presigned Operations](./presigned-operations.md) - Learn about presigned URLs and POST policies