Skip to content

Commit 6705b8b

Browse files
feat(blob)!: change API for consistency (#187)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent b127a38 commit 6705b8b

File tree

11 files changed

+168
-124
lines changed

11 files changed

+168
-124
lines changed

.github/workflows/autofix.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ jobs:
1919

2020
- name: Install dependencies
2121
run: pnpm install
22-
22+
2323
- name: prepare
2424
run: pnpm dev:prepare
2525

26-
- name: Release PR version
27-
run: pnpm dlx pkg-pr-new@0.0 publish
26+
# - name: Release PR version
27+
# run: pnpm dlx pkg-pr-new@0.0 publish
2828

2929
- name: Lint (code)
3030
run: pnpm lint --fix

docs/content/1.docs/2.storage/3.blob.md

+93-83
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ Returns a paginated list of blobs.
2929

3030
```ts [server/api/files.get.ts]
3131
export default eventHandler(async () => {
32-
return hubBlob().list()
32+
const { blobs } = await hubBlob().list({ limit: 10 })
33+
34+
return blobs
3335
})
3436
```
3537

@@ -38,17 +40,19 @@ export default eventHandler(async () => {
3840
::field-group
3941
::field{name="options" type="Object"}
4042
The list options.
41-
::field{name="limit" type="Number"}
42-
The maximum number of blobs to return per request. Defaults to `1000`.
43-
::
44-
::field{name="prefix" type="String"}
45-
Filters the results to only those that begin with the specified prefix.
46-
::
47-
::field{name="cursor" type="String"}
48-
The cursor to continue from a previous list operation.
49-
::
50-
::field{name="folded" type="Boolean"}
51-
If `true`, the list will be folded using `/` separator and list of folders will be returned.
43+
::collapsible
44+
::field{name="limit" type="Number"}
45+
The maximum number of blobs to return per request. Defaults to `1000`.
46+
::
47+
::field{name="prefix" type="String"}
48+
Filters the results to only those that begin with the specified prefix.
49+
::
50+
::field{name="cursor" type="String"}
51+
The cursor to continue from a previous list operation.
52+
::
53+
::field{name="folded" type="Boolean"}
54+
If `true`, the list will be folded using `/` separator and list of folders will be returned.
55+
::
5256
::
5357
::
5458
::
@@ -174,17 +178,22 @@ async function uploadImage (e: Event) {
174178
::
175179
::field{name="options" type="Object"}
176180
The put options. Any other provided field will be stored in the blob's metadata.
177-
::field{name="contentType" type="String"}
178-
The content type of the blob. If not given, it will be inferred from the Blob or the file extension.
179-
::
180-
::field{name="contentLength" type="String"}
181-
The content length of the blob.
182-
::
183-
::field{name="addRandomSuffix" type="Boolean"}
184-
If `true`, a random suffix will be added to the blob's name. Defaults to `false`.
185-
::
186-
::field{name="prefix" type="string"}
187-
The prefix to use for the blob pathname.
181+
::collapsible
182+
::field{name="contentType" type="String"}
183+
The content type of the blob. If not given, it will be inferred from the Blob or the file extension.
184+
::
185+
::field{name="contentLength" type="String"}
186+
The content length of the blob.
187+
::
188+
::field{name="addRandomSuffix" type="Boolean"}
189+
If `true`, a random suffix will be added to the blob's name. Defaults to `false`.
190+
::
191+
::field{name="prefix" type="string"}
192+
The prefix to use for the blob pathname.
193+
::
194+
::field{name="customMetadata" type="Record<string, string>"}
195+
An object with custom metadata to store with the blob.
196+
::
188197
::
189198
::
190199
::
@@ -231,16 +240,26 @@ Returns nothing.
231240
232241
### `handleUpload()`
233242
234-
`handleUpload` is a all in one function to validate a `Blob` by checking its size and type and upload it to the storage.
235-
It can used to handle file uploads in API routes.
243+
This is an "all in one" function to validate a `Blob` by checking its size and type and upload it to the storage.
244+
245+
::note
246+
This server util is made to be used with the [`useUpload()`](#useupload) Vue composable.
247+
::
248+
249+
It can be used to handle file uploads in API routes.
236250
237251
::code-group
238252
```ts [server/api/blob.put.ts]
239253
export default eventHandler(async (event) => {
240254
return hubBlob().handleUpload(event, {
241255
formKey: 'files', // read file or files form the `formKey` field of request body (body should be a `FormData` object)
242256
multiple: true, // when `true`, the `formKey` field will be an array of `Blob` objects
243-
contentType: ['image/jpeg', 'images/png'], // allowed types of the file
257+
ensure: {
258+
contentType: ['image/jpeg', 'images/png'], // allowed types of the file
259+
},
260+
put: {
261+
addRandomSuffix: true
262+
}
244263
})
245264
})
246265
```
@@ -269,25 +288,11 @@ async function onFileSelect(event: Event) {
269288
::field{name="multiple" type="boolean"}
270289
When `true`, the `formKey` field will be an array of `Blob` objects.
271290
::
272-
::field{name="maxSize" type="BlobSize"}
273-
The maximum size of the file, should be: :br
274-
(`1` | `2` | `4` | `8` | `16` | `32` | `64` | `128` | `256` | `512` | `1024`) + (`B` | `KB` | `MB` | `GB`) :br
275-
e.g. `'512KB'`, `'1MB'`, `'2GB'`, etc.
276-
::
277-
::field{name="types" type="BlobType[]"}
278-
Allowed types of the file, e.g. `['image/jpeg']`.
279-
::
280-
::field{name="contentType" type="string"}
281-
The content type of the blob.
282-
::
283-
::field{name="contentLength" type="string"}
284-
The content length of the blob.
285-
::
286-
::field{name="addRandomSuffix" type="boolean"}
287-
If `true`, a random suffix will be added to the blob's name. Defaults to `false`.
291+
::field{name="ensure" type="BlobEnsureOptions"}
292+
See [`ensureBlob()`](#ensureblob) options for more details.
288293
::
289-
::field{name="prefix" type="string"}
290-
The prefix to use for the blob pathname.
294+
::field{name="put" type="BlobPutOptions"}
295+
See [`put()`](#put) options for more details.
291296
::
292297
::
293298
@@ -370,14 +375,16 @@ export default eventHandler(async (event) => {
370375
::
371376
::field{name="options" type="Object"}
372377
The put options. Any other provided field will be stored in the blob's metadata.
373-
::field{name="contentType" type="String"}
374-
The content type of the blob. If not given, it will be inferred from the Blob or the file extension.
375-
::
376-
::field{name="contentLength" type="String"}
377-
The content length of the blob.
378-
::
379-
::field{name="addRandomSuffix" type="Boolean"}
380-
If `true`, a random suffix will be added to the blob's name. Defaults to `true`.
378+
::collapsible
379+
::field{name="contentType" type="String"}
380+
The content type of the blob. If not given, it will be inferred from the Blob or the file extension.
381+
::
382+
::field{name="contentLength" type="String"}
383+
The content length of the blob.
384+
::
385+
::field{name="addRandomSuffix" type="Boolean"}
386+
If `true`, a random suffix will be added to the blob's name. Defaults to `true`.
387+
::
381388
::
382389
::
383390
::
@@ -520,13 +527,15 @@ ensureBlob(file, { maxSize: '1MB', types: ['image' ]})
520527
::
521528
::field{name="options" type="Object" required}
522529
Note that at least `maxSize` or `types` should be provided.
523-
::field{name="maxSize" type="BlobSize"}
524-
The maximum size of the file, should be: :br
525-
(`1` | `2` | `4` | `8` | `16` | `32` | `64` | `128` | `256` | `512` | `1024`) + (`B` | `KB` | `MB` | `GB`) :br
526-
e.g. `'512KB'`, `'1MB'`, `'2GB'`, etc.
527-
::
528-
::field{name="types" type="BlobType[]"}
529-
Allowed types of the file, e.g. `['image/jpeg']`.
530+
::collapsible
531+
::field{name="maxSize" type="BlobSize"}
532+
The maximum size of the file, should be: :br
533+
(`1` | `2` | `4` | `8` | `16` | `32` | `64` | `128` | `256` | `512` | `1024`) + (`B` | `KB` | `MB` | `GB`) :br
534+
e.g. `'512KB'`, `'1MB'`, `'2GB'`, etc.
535+
::
536+
::field{name="types" type="BlobType[]"}
537+
Allowed types of the file, e.g. `['image/jpeg']`.
538+
::
530539
::
531540
::
532541
::
@@ -577,14 +586,13 @@ async function onFileSelect({ target }: Event) {
577586
::
578587
::field{name="options" type="Object" required}
579588
Optionally, you can pass Fetch options to the request. Read more about Fetch API [here](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options).
580-
::field{name="formKey" type="string"}
581-
The key to add the file/files to the request form. Defaults to `'files'`.
582-
::
583-
::field{name="multiple" type="boolean"}
584-
Whether to allow multiple files to be uploaded. Defaults to `true`.
585-
::
586-
::field{name="prefix" type="string"}
587-
The prefix to use for the blob pathname.
589+
::collapsible
590+
::field{name="formKey" type="string"}
591+
The key to add the file/files to the request form. Defaults to `'files'`.
592+
::
593+
::field{name="multiple" type="boolean"}
594+
Whether to allow multiple files to be uploaded. Defaults to `true`.
595+
::
588596
::
589597
::
590598
::
@@ -614,21 +622,23 @@ export const mpu = useMultipartUpload('/api/files/multipart')
614622
::
615623
::field{name="options"}
616624
The options for the multipart upload helper.
617-
::field{name="partSize" type="number"}
618-
The size of each part of the file to be uploaded. Defaults to `10MB`.
619-
::
620-
::field{name="concurrent" type="number"}
621-
The maximum number of concurrent uploads. Defaults to `1`.
622-
::
623-
::field{name="maxRetry" type="number"}
624-
The maximum number of retry attempts for the whole upload. Defaults to `3`.
625-
::
626-
::field{name="prefix" type="string"}
627-
The prefix to use for the blob pathname.
628-
::
629-
::field{name="fetchOptions" type="Omit<FetchOptions, 'method' | 'baseURL' | 'body' | 'parseResponse' | 'responseType'>"}
630-
Override the ofetch options.
631-
The `query` and `headers` will be merged with the options provided by the uploader.
625+
::collapsible
626+
::field{name="partSize" type="number"}
627+
The size of each part of the file to be uploaded. Defaults to `10MB`.
628+
::
629+
::field{name="concurrent" type="number"}
630+
The maximum number of concurrent uploads. Defaults to `1`.
631+
::
632+
::field{name="maxRetry" type="number"}
633+
The maximum number of retry attempts for the whole upload. Defaults to `3`.
634+
::
635+
::field{name="prefix" type="string"}
636+
The prefix to use for the blob pathname.
637+
::
638+
::field{name="fetchOptions" type="Omit<FetchOptions, 'method' | 'baseURL' | 'body' | 'parseResponse' | 'responseType'>"}
639+
Override the ofetch options.
640+
The `query` and `headers` will be merged with the options provided by the uploader.
641+
::
632642
::
633643
::
634644
::
@@ -728,7 +738,7 @@ async function loadMore() {
728738
return
729739
}
730740
const res = await $fetch('/api/blobs', {
731-
params: {
741+
query: {
732742
limit: 3,
733743
cursor: cursor.value
734744
}

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"citty": "^0.1.6",
4646
"confbox": "^0.1.7",
4747
"defu": "^6.1.4",
48+
"destr": "^2.0.3",
4849
"h3": "^1.11.1",
4950
"mime": "^4.0.3",
5051
"nitro-cloudflare-dev": "^0.1.4",

playground/app/pages/blob.vue

+2-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const limit = ref(5)
1010
const prefix = computed(() => prefixes.value?.[prefixes.value.length - 1])
1111
const toast = useToast()
1212
const { data: blobData } = await useFetch('/api/blob', {
13-
params: {
13+
query: {
1414
folded,
1515
prefix,
1616
limit
@@ -23,7 +23,7 @@ const folders = computed(() => blobData.value?.folders || [])
2323
async function loadMore() {
2424
if (!blobData.value?.hasMore) return
2525
const nextPage = await $fetch('/api/blob', {
26-
params: {
26+
query: {
2727
folded: folded.value,
2828
prefix: prefix.value,
2929
limit: limit.value,
@@ -66,7 +66,6 @@ async function uploadFiles(files: File[]) {
6666
6767
let uploadedFiles: any[] = []
6868
// upload small files
69-
console.log('upload small files', smallFiles.length)
7069
if (smallFiles.length) {
7170
uploadedFiles = await useUpload('/api/blob', {
7271
method: 'PUT',

playground/server/api/blob/index.put.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ export default eventHandler(async (event) => {
33
return hubBlob().handleUpload(event, {
44
formKey: 'files', // default
55
multiple: true, // default
6-
prefix: String(prefix || '')
6+
put: {
7+
prefix: String(prefix || ''),
8+
customMetadata: {
9+
hello: 'world'
10+
}
11+
}
712
})
813
})

pnpm-lock.yaml

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/runtime/blob/app/composables/useUpload.ts

-9
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,6 @@ interface UploadOptions extends FetchOptions {
1414
* @default true
1515
*/
1616
multiple?: boolean
17-
18-
/**
19-
* The prefix to use for the blobs pathname.
20-
*/
21-
prefix?: string
2217
}
2318

2419
export function useUpload(apiBase: string, options?: UploadOptions & { multiple: false }): (data: FileList | HTMLInputElement | File[] | File) => Promise<BlobObject>
@@ -51,10 +46,6 @@ export function useUpload(apiBase: string, options: UploadOptions = {}) {
5146
return $fetch(apiBase, {
5247
...fetchOptions,
5348
method: (method || 'POST') as any,
54-
params: {
55-
...fetchOptions.params,
56-
prefix: options.prefix
57-
},
5849
body: formData
5950
}).then(result => (multiple === false || data instanceof File) ? result[0] : result)
6051
}

src/runtime/blob/server/api/_hub/blob/index.post.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { eventHandler, getQuery } from 'h3'
2+
import { destr } from 'destr'
23
import { hubBlob } from '../../../utils/blob'
34
import { requireNuxtHubAuthorization } from '../../../../../utils/auth'
45
import { requireNuxtHubFeature } from '../../../../../utils/features'
@@ -10,8 +11,9 @@ export default eventHandler(async (event) => {
1011
const query = getQuery(event)
1112

1213
return hubBlob().handleUpload(event, {
13-
formKey: 'files',
14-
...query,
15-
multiple: query.multiple !== 'false'
14+
formKey: query.formKey || 'files',
15+
multiple: query.multiple !== 'false',
16+
put: destr(query.put),
17+
ensure: destr(query.ensure)
1618
})
1719
})

0 commit comments

Comments
 (0)