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
```