or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-api.mderrors.mdindex.mdmultipart-uploads.mdserver-api.md

client-api.mddocs/

0

# Client-side API

1

2

Client-side upload operations designed for frontend execution with token-based security and support for large file uploads.

3

4

## Capabilities

5

6

### Client Upload with Token

7

8

#### put

9

10

Uploads a blob using a client token for secure client-side uploads.

11

12

```typescript { .api }

13

/**

14

* Uploads a blob using a client token

15

* @param pathname - The pathname to upload the blob to, including the extension

16

* @param body - The content of your blob (string, File, Blob, Buffer, Stream)

17

* @param options - Client upload configuration options

18

* @returns Promise resolving to blob information

19

*/

20

function put(pathname: string, body: PutBody, options: ClientPutCommandOptions): Promise<PutBlobResult>;

21

22

interface ClientPutCommandOptions extends ClientCommonPutOptions, ClientTokenOptions {

23

}

24

25

interface ClientCommonPutOptions extends ClientCommonCreateBlobOptions, WithUploadProgress {

26

multipart?: boolean;

27

}

28

29

interface ClientCommonCreateBlobOptions {

30

access: 'public';

31

contentType?: string;

32

abortSignal?: AbortSignal;

33

}

34

35

interface ClientTokenOptions {

36

token: string;

37

}

38

39

interface WithUploadProgress {

40

onUploadProgress?: OnUploadProgressCallback;

41

}

42

```

43

44

**Usage Examples:**

45

46

```typescript

47

import { put } from '@vercel/blob/client';

48

49

// Upload with client token

50

const result = await put('profile.jpg', imageFile, {

51

access: 'public',

52

token: 'vercel_blob_client_...',

53

});

54

55

// Upload with progress tracking

56

const result = await put('large-video.mp4', videoFile, {

57

access: 'public',

58

token: 'vercel_blob_client_...',

59

onUploadProgress: ({ loaded, total, percentage }) => {

60

console.log(`Upload progress: ${percentage}%`);

61

},

62

});

63

```

64

65

### Server-Assisted Upload

66

67

#### upload

68

69

Client upload that fetches a client token from your server endpoint before uploading.

70

71

```typescript { .api }

72

/**

73

* Client upload that fetches token from server

74

* @param pathname - The pathname to upload the blob to

75

* @param body - The content of your blob

76

* @param options - Upload configuration options

77

* @returns Promise resolving to blob information

78

*/

79

function upload(pathname: string, body: PutBody, options: UploadOptions): Promise<PutBlobResult>;

80

81

interface UploadOptions extends BlobCommandOptions {

82

access: 'public';

83

addRandomSuffix?: boolean;

84

allowOverwrite?: boolean;

85

contentType?: string;

86

cacheControlMaxAge?: number;

87

handleUploadUrl: string;

88

onUploadProgress?: OnUploadProgressCallback;

89

multipart?: boolean;

90

}

91

```

92

93

**Usage Examples:**

94

95

```typescript

96

import { upload } from '@vercel/blob/client';

97

98

// Upload via server endpoint

99

const result = await upload('document.pdf', pdfFile, {

100

access: 'public',

101

handleUploadUrl: '/api/upload',

102

});

103

104

// Upload with custom options

105

const result = await upload('image.jpg', imageFile, {

106

access: 'public',

107

handleUploadUrl: '/api/upload',

108

addRandomSuffix: true,

109

multipart: true,

110

onUploadProgress: ({ percentage }) => {

111

setUploadProgress(percentage);

112

},

113

});

114

```

115

116

### Server-Side Upload Handling

117

118

#### handleUpload

119

120

Server-side function to handle client upload requests and generate client tokens.

121

122

```typescript { .api }

123

/**

124

* Server-side route helper for handling client uploads

125

* @param options - Upload handling configuration

126

* @returns Promise resolving to client token or completion result

127

*/

128

function handleUpload(options: HandleUploadOptions): Promise<HandleUploadResult>;

129

130

interface HandleUploadOptions {

131

body: HandleUploadBody;

132

onBeforeGenerateToken: (

133

pathname: string,

134

clientPayload: string | null,

135

multipart: boolean,

136

) => Promise<

137

Pick<

138

GenerateClientTokenOptions,

139

| 'allowedContentTypes'

140

| 'maximumSizeInBytes'

141

| 'validUntil'

142

| 'addRandomSuffix'

143

| 'allowOverwrite'

144

| 'cacheControlMaxAge'

145

> & { tokenPayload?: string | null }

146

>;

147

onUploadCompleted: (body: UploadCompletedEvent['payload']) => Promise<void>;

148

token?: string;

149

request: RequestType;

150

}

151

152

type HandleUploadResult =

153

| { type: 'blob.generate-client-token'; clientToken: string }

154

| { type: 'blob.upload-completed'; response: 'ok' };

155

156

type RequestType = IncomingMessage | Request;

157

158

type HandleUploadBody = GenerateClientTokenEvent | UploadCompletedEvent;

159

160

interface GenerateClientTokenEvent {

161

type: 'blob.generate-client-token';

162

payload: {

163

pathname: string;

164

callbackUrl: string;

165

multipart: boolean;

166

clientPayload: string | null;

167

};

168

}

169

170

interface UploadCompletedEvent {

171

type: 'blob.upload-completed';

172

payload: {

173

blob: PutBlobResult;

174

tokenPayload?: string | null;

175

};

176

}

177

```

178

179

**Usage Examples:**

180

181

```typescript

182

// Next.js API route

183

import { handleUpload } from '@vercel/blob/client';

184

185

export async function POST(request: Request) {

186

const body = await request.json();

187

188

return handleUpload({

189

request,

190

body,

191

onBeforeGenerateToken: async (pathname, clientPayload, multipart) => {

192

// Validate upload permissions

193

if (!pathname.startsWith('user-uploads/')) {

194

throw new Error('Invalid upload path');

195

}

196

197

return {

198

maximumSizeInBytes: 10 * 1024 * 1024, // 10MB limit

199

allowedContentTypes: ['image/*', 'application/pdf'],

200

};

201

},

202

onUploadCompleted: async ({ blob, tokenPayload }) => {

203

// Save to database

204

await saveFileToDatabase({

205

url: blob.url,

206

pathname: blob.pathname,

207

size: blob.size,

208

});

209

},

210

});

211

}

212

```

213

214

### Client Multipart Operations

215

216

#### createMultipartUpload

217

218

Initiates a multipart upload from the client side.

219

220

```typescript { .api }

221

/**

222

* Creates a multipart upload session for client-side uploads

223

* @param pathname - The pathname to upload the blob to

224

* @param options - Multipart creation options

225

* @returns Promise resolving to upload session info

226

*/

227

function createMultipartUpload(pathname: string, options: ClientCreateMultipartUploadCommandOptions): Promise<MultipartUploadInfo>;

228

229

interface ClientCreateMultipartUploadCommandOptions extends ClientCommonCreateBlobOptions, ClientTokenOptions {

230

}

231

232

interface MultipartUploadInfo {

233

key: string;

234

uploadId: string;

235

}

236

```

237

238

#### createMultipartUploader

239

240

Creates a simplified multipart uploader wrapper for client-side uploads.

241

242

```typescript { .api }

243

/**

244

* Creates a simplified multipart uploader for client-side use

245

* @param pathname - The pathname to upload the blob to

246

* @param options - Uploader configuration options

247

* @returns Promise resolving to multipart uploader

248

*/

249

function createMultipartUploader(pathname: string, options: ClientCreateMultipartUploadCommandOptions): Promise<MultipartUploader>;

250

251

interface MultipartUploader {

252

uploadPart(partNumber: number, body: PutBody): Promise<Part>;

253

complete(parts: Part[]): Promise<PutBlobResult>;

254

abort(): Promise<void>;

255

}

256

```

257

258

#### uploadPart

259

260

Uploads a single part in a client-side multipart upload.

261

262

```typescript { .api }

263

/**

264

* Uploads a part in a client-side multipart upload

265

* @param pathname - Same pathname used in createMultipartUpload

266

* @param body - Part content (minimum 5MB except for the last part)

267

* @param options - Part upload options

268

* @returns Promise resolving to part information

269

*/

270

function uploadPart(pathname: string, body: PutBody, options: ClientMultipartUploadCommandOptions): Promise<Part>;

271

272

interface ClientMultipartUploadCommandOptions extends ClientCommonCreateBlobOptions, ClientTokenOptions {

273

key: string;

274

uploadId: string;

275

partNumber: number;

276

}

277

```

278

279

#### completeMultipartUpload

280

281

Completes a client-side multipart upload.

282

283

```typescript { .api }

284

/**

285

* Completes a client-side multipart upload

286

* @param pathname - Same pathname used in createMultipartUpload

287

* @param parts - Array of uploaded parts in order

288

* @param options - Completion options

289

* @returns Promise resolving to blob information

290

*/

291

function completeMultipartUpload(pathname: string, parts: Part[], options: ClientCompleteMultipartUploadCommandOptions): Promise<PutBlobResult>;

292

293

interface ClientCompleteMultipartUploadCommandOptions extends ClientCommonCreateBlobOptions, ClientTokenOptions {

294

key: string;

295

uploadId: string;

296

}

297

```

298

299

**Client Multipart Usage Example:**

300

301

```typescript

302

import {

303

createMultipartUpload,

304

uploadPart,

305

completeMultipartUpload

306

} from '@vercel/blob/client';

307

308

// Upload large file using multipart

309

const file = largeVideoFile; // File > 100MB

310

const pathname = 'videos/large-video.mp4';

311

const token = 'vercel_blob_client_...';

312

313

// 1. Create multipart upload

314

const { key, uploadId } = await createMultipartUpload(pathname, {

315

access: 'public',

316

token,

317

});

318

319

// 2. Upload parts (minimum 5MB each, except last part)

320

const partSize = 5 * 1024 * 1024; // 5MB

321

const parts: Part[] = [];

322

323

for (let i = 0; i < Math.ceil(file.size / partSize); i++) {

324

const start = i * partSize;

325

const end = Math.min(start + partSize, file.size);

326

const chunk = file.slice(start, end);

327

328

const part = await uploadPart(pathname, chunk, {

329

key,

330

uploadId,

331

partNumber: i + 1,

332

token,

333

});

334

335

parts.push(part);

336

}

337

338

// 3. Complete multipart upload

339

const result = await completeMultipartUpload(pathname, parts, {

340

key,

341

uploadId,

342

token,

343

});

344

345

console.log('Upload completed:', result.url);

346

```

347

348

### Token Management

349

350

#### generateClientTokenFromReadWriteToken

351

352

Generates a client token from a read-write token for secure client-side uploads.

353

354

```typescript { .api }

355

/**

356

* Generates a client token from a read-write token

357

* @param options - Token generation options

358

* @returns Promise resolving to client token string

359

*/

360

function generateClientTokenFromReadWriteToken(options: GenerateClientTokenOptions): Promise<string>;

361

362

interface GenerateClientTokenOptions extends BlobCommandOptions {

363

pathname: string;

364

onUploadCompleted?: {

365

callbackUrl: string;

366

tokenPayload?: string | null;

367

};

368

maximumSizeInBytes?: number;

369

allowedContentTypes?: string[];

370

validUntil?: number;

371

addRandomSuffix?: boolean;

372

allowOverwrite?: boolean;

373

cacheControlMaxAge?: number;

374

}

375

```

376

377

**Usage Examples:**

378

379

```typescript

380

import { generateClientTokenFromReadWriteToken } from '@vercel/blob/client';

381

382

// Generate client token for specific file

383

const clientToken = await generateClientTokenFromReadWriteToken({

384

pathname: 'uploads/user-avatar.jpg',

385

allowedContentTypes: ['image/jpeg', 'image/png'],

386

maximumSizeInBytes: 2 * 1024 * 1024, // 2MB

387

validUntil: Date.now() + 5 * 60 * 1000, // 5 minutes (timestamp in ms)

388

addRandomSuffix: true,

389

allowOverwrite: false,

390

});

391

392

// Generate token with callback configuration

393

const clientToken = await generateClientTokenFromReadWriteToken({

394

pathname: 'documents/report.pdf',

395

onUploadCompleted: {

396

callbackUrl: 'https://myapp.com/api/upload-callback',

397

tokenPayload: JSON.stringify({ userId: '123', department: 'engineering' }),

398

},

399

cacheControlMaxAge: 86400, // 1 day

400

});

401

```

402

403

#### getPayloadFromClientToken

404

405

Extracts payload information from a client token.

406

407

```typescript { .api }

408

/**

409

* Extracts payload from a client token

410

* @param clientToken - Client token to decode

411

* @returns Decoded client token payload

412

*/

413

function getPayloadFromClientToken(clientToken: string): DecodedClientTokenPayload;

414

415

interface DecodedClientTokenPayload {

416

pathname: string;

417

onUploadCompleted?: {

418

callbackUrl: string;

419

tokenPayload?: string | null;

420

};

421

maximumSizeInBytes?: number;

422

allowedContentTypes?: string[];

423

validUntil: number;

424

addRandomSuffix?: boolean;

425

allowOverwrite?: boolean;

426

cacheControlMaxAge?: number;

427

}

428

```

429

430

**Usage Examples:**

431

432

```typescript

433

import { getPayloadFromClientToken } from '@vercel/blob/client';

434

435

// Decode client token

436

const payload = getPayloadFromClientToken(clientToken);

437

console.log('Allowed path:', payload.pathname);

438

console.log('Max size:', payload.maximumSizeInBytes);

439

console.log('Valid until:', payload.validUntil);

440

441

// Access custom payload

442

if (payload.clientPayload) {

443

const customData = JSON.parse(payload.clientPayload);

444

console.log('User ID:', customData.userId);

445

}

446

```

447

448

### Folder Management

449

450

#### createFolder

451

452

Creates virtual folders (re-exported from main module for convenience).

453

454

```typescript { .api }

455

/**

456

* Creates virtual folders in blob store for UI display purposes

457

* @param pathname - Folder path (trailing slash added automatically)

458

* @param options - Configuration options

459

* @returns Promise resolving to folder information

460

*/

461

function createFolder(pathname: string, options?: BlobCommandOptions): Promise<CreateFolderResult>;

462

```

463

464

This function works identically to the server-side version and supports the same options and return type.

465

466

## Common Request Body Types

467

468

### HandleUploadBody

469

470

```typescript { .api }

471

interface HandleUploadBody {

472

type: 'blob.generate-client-token';

473

pathname: string;

474

callbackUrl: string;

475

clientPayload?: string;

476

}

477

```

478

479

## Integration Patterns

480

481

### Next.js Integration

482

483

```typescript

484

// pages/api/upload.ts (Pages Router)

485

import { handleUpload } from '@vercel/blob/client';

486

import type { NextApiRequest, NextApiResponse } from 'next';

487

488

export default async function handler(req: NextApiRequest, res: NextApiResponse) {

489

const { json } = await handleUpload({

490

request: req,

491

onBeforeGenerateToken: async (pathname) => {

492

// Add validation logic

493

},

494

onUploadCompleted: async (result) => {

495

// Handle completion

496

},

497

});

498

499

return res.status(200).json(json);

500

}

501

502

// app/api/upload/route.ts (App Router)

503

import { handleUpload } from '@vercel/blob/client';

504

505

export async function POST(request: Request) {

506

return handleUpload({

507

request,

508

onBeforeGenerateToken: async (pathname) => {

509

// Add validation logic

510

},

511

});

512

}

513

```

514

515

### React Hook Example

516

517

```typescript

518

import { upload } from '@vercel/blob/client';

519

import { useState } from 'react';

520

521

export function useFileUpload() {

522

const [progress, setProgress] = useState(0);

523

const [uploading, setUploading] = useState(false);

524

525

const uploadFile = async (file: File, pathname: string) => {

526

setUploading(true);

527

setProgress(0);

528

529

try {

530

const result = await upload(pathname, file, {

531

access: 'public',

532

handleUploadUrl: '/api/upload',

533

onUploadProgress: ({ percentage }) => {

534

setProgress(percentage);

535

},

536

});

537

538

return result;

539

} finally {

540

setUploading(false);

541

setProgress(0);

542

}

543

};

544

545

return { uploadFile, progress, uploading };

546

}

547

```