Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: @uploadthing/expo #583

Merged
merged 163 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
163 commits
Select commit Hold shift + click to select a range
f28790c
first attempt
juliusmarminge Jan 26, 2024
f2ad01b
bunchee powers
juliusmarminge Jan 28, 2024
ee9d04f
add transistive dep explicitely
juliusmarminge Jan 28, 2024
bf745e9
fix import
juliusmarminge Jan 28, 2024
b3dd46f
working uploads
juliusmarminge Jan 28, 2024
c2e6a2f
lint fix
juliusmarminge Jan 28, 2024
7cfea6c
make sure types are the same
juliusmarminge Jan 28, 2024
871a3cd
add require path
juliusmarminge Jan 28, 2024
1515d50
add back express but with ts-ignore
juliusmarminge Jan 28, 2024
dc2fae0
more fix
juliusmarminge Jan 28, 2024
716cfe0
restack expo 1/2
juliusmarminge Jan 29, 2024
c2f9bbf
sync lock
juliusmarminge Jan 29, 2024
c2bf1c9
revert exports of types
juliusmarminge Jan 29, 2024
5f38c4a
first attempt
juliusmarminge Jan 26, 2024
c7229fa
bunchee powers
juliusmarminge Jan 28, 2024
ae43f53
add transistive dep explicitely
juliusmarminge Jan 28, 2024
c4e3b04
fix import
juliusmarminge Jan 28, 2024
0a5ee48
working uploads
juliusmarminge Jan 28, 2024
eae63d2
lint fix
juliusmarminge Jan 28, 2024
6a62a7a
fmt
juliusmarminge Jan 28, 2024
48e94ba
restack expo 1/2
juliusmarminge Jan 29, 2024
cb324dc
sync lock
juliusmarminge Jan 29, 2024
b17da5b
revert exports of types
juliusmarminge Jan 29, 2024
ee7fd87
re-init expo app
juliusmarminge Jan 29, 2024
cfcdb2d
sync lock, rename exmaple path
juliusmarminge Jan 29, 2024
94de51e
nit
juliusmarminge Jan 29, 2024
201c086
up router
juliusmarminge Jan 29, 2024
3a4303e
fmt
juliusmarminge Jan 29, 2024
eda0a4e
fix express
juliusmarminge Jan 31, 2024
51b7cc7
module
juliusmarminge Jan 31, 2024
7f5fb1f
rev
juliusmarminge Jan 31, 2024
9cb71a6
resolve types from src during dev
juliusmarminge Jan 31, 2024
23db2fb
single * per pattern...
juliusmarminge Jan 31, 2024
abbc59c
vitest workspaces
juliusmarminge Jan 31, 2024
b6834a7
rm aliases for now
juliusmarminge Jan 31, 2024
ab60c73
fmt
juliusmarminge Feb 4, 2024
2f50476
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Feb 6, 2024
7acc1ec
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Feb 16, 2024
6d91300
rm splash
juliusmarminge Feb 16, 2024
7a3a1bc
up deps
juliusmarminge Feb 16, 2024
b86e07d
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Feb 16, 2024
1017fb8
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Feb 16, 2024
208674e
expo package
juliusmarminge Feb 16, 2024
b3e679a
nice
juliusmarminge Feb 16, 2024
50e5600
nicee
juliusmarminge Feb 16, 2024
fe78f85
document uploader [NOT WORKING]
juliusmarminge Feb 16, 2024
702744e
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Feb 26, 2024
88c7b8c
sync
juliusmarminge Feb 26, 2024
9a67d91
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Feb 26, 2024
2e496e8
fix uploads
juliusmarminge Feb 26, 2024
48f02d6
nice
juliusmarminge Feb 26, 2024
b22a9a1
fix lint
juliusmarminge Feb 26, 2024
2a0e0f8
fix turbo cache deps
juliusmarminge Feb 26, 2024
ce52acb
update config to support json
juliusmarminge Feb 26, 2024
af507cb
cs
juliusmarminge Feb 26, 2024
19dc987
eas
juliusmarminge Feb 26, 2024
d6766df
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Feb 28, 2024
5fcc604
sync
juliusmarminge Feb 28, 2024
aa01d0e
format
juliusmarminge Feb 28, 2024
a2b04c9
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Feb 28, 2024
f00006d
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Feb 28, 2024
2f46e27
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Feb 29, 2024
d174b37
fixynit
juliusmarminge Feb 29, 2024
94d9253
fmt
juliusmarminge Feb 29, 2024
b46058a
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Mar 5, 2024
4478703
sync lock
juliusmarminge Mar 5, 2024
1aaf82b
nice
juliusmarminge Mar 5, 2024
d9b3892
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Mar 5, 2024
2373a24
niceer
juliusmarminge Mar 5, 2024
24e91c5
pnpm i
markflorkowski Apr 11, 2024
6dc58bd
Merge branch 'main' into 01-29-re-init_expo_app
markflorkowski Apr 11, 2024
416cabf
bum bunchee for expo package
markflorkowski Apr 11, 2024
d88ce8c
lint
markflorkowski Apr 11, 2024
b44195b
helps to save
markflorkowski Apr 11, 2024
11231b7
pnpm i
markflorkowski Apr 11, 2024
4e137e9
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Apr 16, 2024
a2915d2
sync versions
juliusmarminge Apr 16, 2024
3972aff
sync lock
juliusmarminge Apr 16, 2024
883635d
node_linker hoisted, expo file store
juliusmarminge Apr 16, 2024
32e3cc4
manypkg fix
juliusmarminge Apr 16, 2024
75013f5
forward isUploading
juliusmarminge Apr 16, 2024
b87959a
wording
juliusmarminge Apr 16, 2024
e90ff26
improve example
juliusmarminge Apr 16, 2024
6323979
comment, add clean script to minimal-expo
markflorkowski Apr 16, 2024
707f647
short modal styling, empty state
markflorkowski Apr 17, 2024
62d0066
align deps
juliusmarminge Apr 17, 2024
273075f
hide button on scroll
juliusmarminge Apr 17, 2024
5ac269d
check permissions
juliusmarminge Apr 17, 2024
820a5cb
simplify
juliusmarminge Apr 17, 2024
cea2bbe
fix logic
juliusmarminge Apr 17, 2024
474f3dd
permission alert
juliusmarminge Apr 17, 2024
857cf80
3rd button
juliusmarminge Apr 17, 2024
56687fd
nit
juliusmarminge Apr 17, 2024
a92e232
do some splitting, nicer drawer
juliusmarminge Apr 17, 2024
31c547f
only dark mode
juliusmarminge Apr 17, 2024
5041b4a
apparently RN.FormData isn't spec compliant...
juliusmarminge Apr 17, 2024
da88346
fix manypkg
juliusmarminge Apr 17, 2024
f64640f
allow multiple
juliusmarminge Apr 17, 2024
6fec3c7
onUploadError
juliusmarminge Apr 17, 2024
0b85f17
psuh test
juliusmarminge Apr 17, 2024
1e0b533
don't hardcode ip for testing
markflorkowski Apr 17, 2024
b810e64
correct url format
markflorkowski Apr 17, 2024
843cb4e
docs
juliusmarminge Apr 18, 2024
ad38368
tell'em to do auth
juliusmarminge Apr 18, 2024
c2ddb78
createContext
juliusmarminge Apr 18, 2024
3e0b005
refactor a bit
juliusmarminge Apr 18, 2024
699473c
swap rnsvg to expo vector icons
juliusmarminge Apr 18, 2024
81cd953
active class on more-button
juliusmarminge Apr 18, 2024
0f9b9e1
rm
juliusmarminge Apr 18, 2024
d473c2f
swipe to delete
juliusmarminge Apr 18, 2024
3377ee3
typo
juliusmarminge Apr 18, 2024
108c03b
classname
juliusmarminge Apr 18, 2024
84f50a6
filter deleted
juliusmarminge Apr 18, 2024
7073a58
make dates descending
juliusmarminge Apr 18, 2024
1fb7fd7
revert to deterministic to avoid flickering
juliusmarminge Apr 18, 2024
0a7cd8b
fix generateMediaTypes
juliusmarminge Apr 18, 2024
27e8284
memoize
juliusmarminge Apr 18, 2024
135cb03
memoize
juliusmarminge Apr 18, 2024
fdb56a1
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Apr 24, 2024
337600a
fix effect expo
juliusmarminge Apr 25, 2024
857958e
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge May 7, 2024
2536547
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge May 7, 2024
c064929
bump to sdk51
juliusmarminge May 7, 2024
58647ca
rm TextEncoder polyfill
juliusmarminge May 7, 2024
a096a78
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge May 11, 2024
36526df
fix: mismatch in response schema for `utapi.listFiles` (#798)
juliusmarminge May 12, 2024
8620002
chore: set `sideEffects: false` (#806)
juliusmarminge May 13, 2024
6b239b6
add missing changeset
juliusmarminge May 13, 2024
fa6035e
chore(release): 📦 version packages (#803)
t3dotgg May 13, 2024
3b8ceba
docs: fix typo on react api reference (#811)
NicklessOne May 16, 2024
96c691e
fix: better mime type support for generic types (#810)
juliusmarminge May 16, 2024
d244186
feat: added onDrop prop for UploadDropzone component (#809)
growupanand May 16, 2024
e96200a
fix: Node 20.13 compat (#813)
juliusmarminge May 16, 2024
5d7351f
feat: add onDrop to remaining libs (#814)
juliusmarminge May 16, 2024
8aa19e4
feat: add with-novel example (#800)
juliusmarminge May 16, 2024
ba37b13
refactor: UploadButton (#801)
juliusmarminge May 16, 2024
831a869
chore: inline returns
juliusmarminge May 16, 2024
9188a8a
fix: make `@uploadthing/shared` treeshakeable (#808)
juliusmarminge May 16, 2024
5f18384
fix lint
juliusmarminge May 16, 2024
f157ee6
merge
juliusmarminge May 16, 2024
0f12de1
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge May 21, 2024
a2fa533
rename imporrt
juliusmarminge May 21, 2024
a9c2162
update expo
juliusmarminge May 21, 2024
4bf669e
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge May 21, 2024
7a5e12b
better rnw support
juliusmarminge May 22, 2024
d14fa0d
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge May 22, 2024
257b18a
fixx
juliusmarminge May 22, 2024
c8b8410
maybe now?
juliusmarminge May 22, 2024
61db742
man
juliusmarminge May 22, 2024
559e783
fmt
juliusmarminge May 23, 2024
327afa1
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Jun 3, 2024
9be7aac
kewl
juliusmarminge Jun 3, 2024
4a267b9
swap
juliusmarminge Jun 3, 2024
1a40575
fix manypkg lint
juliusmarminge Jun 3, 2024
b83d5c5
fix
juliusmarminge Jun 3, 2024
d4034e8
mayb?
juliusmarminge Jun 3, 2024
9004814
mby?
juliusmarminge Jun 3, 2024
d4ecf70
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Jun 4, 2024
d2bee79
Update docs/src/pages/getting-started/expo.mdx
juliusmarminge Jun 10, 2024
aee2dc1
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge Jun 10, 2024
cdd06ba
tasks*
juliusmarminge Jun 10, 2024
1e3b465
fmt
juliusmarminge Jun 10, 2024
0690df8
lint-fix
juliusmarminge Jun 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/afraid-berries-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@uploadthing/react": minor
"@uploadthing/expo": minor
---

feat: support expo
5 changes: 5 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@
save-workspace-protocol=false
prefer-workspace-packages=true

# Expo doesn't play nice with pnpm by default.
# The symbolic links of pnpm break the rules of Expo monorepos.
# @link https://docs.expo.dev/guides/monorepos/#common-issues
node-linker=hoisted

engine-strict=true
171 changes: 171 additions & 0 deletions docs/src/pages/api-reference/expo.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { Callout } from "nextra-theme-docs";

## `generateReactNativeHelpers`

The `generateReactNativeHelpers` function is used to generate the
useImageUploader and useDocumentUploader hooks you use to interact with
UploadThing in your app.

```tsx copy filename="utils/uploadthing.tsx"
import { generateReactNativeHelpers } from "@uploadthing/expo";

import type { UploadRouter } from "~/app/api/uploadthing+api";

export const { useImageUploader, useDocumentUploader } =
generateReactNativeHelpers<UploadRouter>({
/**
* Your server url.
* @default process.env.EXPO_PUBLIC_SERVER_URL
* @remarks In dev we will also try to use Expo.debuggerHost
*/
url: "https://my-server.com",
Comment on lines +16 to +21
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way to "hook" into this variable @EvanBacon?

CleanShot 2024-04-18 at 12 06 07

});
```

## `useImageUploader`

A hook wrapping the native `expo-image-picker` module that allows access to the
camera and photo library and uploads files to your server. The first time the
user triggers the picker they will be prompted to grant your app permission to
access the camera or photo library.

```tsx copy filename="app/example-uploader.tsx"
import { Alert, Pressable, Text, View } from "react-native";

import { useImageUploader } from "@uploadthing/expo";

export function MultiUploader() {
const { openImagePicker, isUploading } = useImageUploader("imageUploader", {
/**
* Any props here are forwarded to the underlying `useUploadThing` hook.
* Refer to the React API reference for more info.
*/
onClientUploadComplete: () => Alert.alert("Upload Completed"),
onUploadError: (error) => Alert.alert("Upload Error", error.message),
});

return (
<View>
<Pressable
onPress={() => {
openImagePicker({
input, // Matches the input schema from the FileRouter endpoint
source: "library", // or "camera"
onInsufficientPermissions: () => {
Alert.alert(
"No Permissions",
"You need to grant permission to your Photos to use this",
[
{ text: "Dismiss" },
{ text: "Open Settings", onPress: openSettings },
],
);
},
});
}}
>
<Text>Select Image</Text>
</Pressable>
</View>
);
}
```

### Configuration

| Prop | Type | Required | Notes | Description |
| :------- | :-------------------- | :------- | :----------------------------------------------- | --------------------------------------------------------------------------------- |
| endpoint | `keyof UploadRouter` | Yes | | The name of the [route](./server#FileRouter) you want this button to upload to |
| opts | `UseUploadThingProps` | No | [See docs](/api-reference/react#configuration-3) | Props forwarded to the underlying `useUploadThing` hook from `@uploadthing/react` |

### Returns

| Prop | Type | Description |
| :-------------- | :------------------------------------------------ | :------------------------------------------------------------------------------- |
| openImagePicker | `(opts: OpenImagePickerOptions) => Promise<void>` | Function to open the native image picker and start uploading the selected files. |
| isUploading | boolean | Flag whether file(s) are currently uploading |

```ts
type OpenImagePickerOptions = {
input: TInput; // Matches the input schema from the FileRouter endpoint
source: "library" | "camera";
/**
* Callback to run if the user cancels the picker.
*/
onCancel?: () => void;
/**
* Callback to run if the user hasn't granted your app permission to the camera or photo library.
*/
onInsufficientPermissions: () => void;
};
```

## `useDocumentUploader`

A hook wrapping the native `expo-document-picker` module that allows access to
the native file system and uploads files to your server.

```tsx copy filename="app/example-uploader.tsx"
import { Alert, Pressable, Text, View } from "react-native";

import { useDocumentUploader } from "@uploadthing/expo";

export function MultiUploader() {
const { openDocumentPicker, isUploading } = useDocumentUploader("document", {
/**
* Any props here are forwarded to the underlying `useUploadThing` hook.
* Refer to the React API reference for more info.
*/
onClientUploadComplete: () => Alert.alert("Upload Completed"),
onUploadError: (error) => Alert.alert("Upload Error", error.message),
});

return (
<View>
<Pressable
onPress={() => {
openDocumentPicker({
input, // Matches the input schema from the FileRouter endpoint
onInsufficientPermissions: () => {
Alert.alert(
"No Permissions",
"You need to grant permission to your Photos to use this",
[
{ text: "Dismiss" },
{ text: "Open Settings", onPress: openSettings },
],
);
},
});
}}
>
<Text>Select Document</Text>
</Pressable>
</View>
);
}
```

### Configuration

| Prop | Type | Required | Notes | Description |
| :------- | :-------------------- | :------- | :----------------------------------------------- | --------------------------------------------------------------------------------- |
| endpoint | `keyof UploadRouter` | Yes | | The name of the [route](./server#FileRouter) you want this button to upload to |
| opts | `UseUploadThingProps` | No | [See docs](/api-reference/react#configuration-3) | Props forwarded to the underlying `useUploadThing` hook from `@uploadthing/react` |

### Returns

| Prop | Type | Description |
| :-------------- | :------------------------------------------------ | :------------------------------------------------------------------------------- |
| openImagePicker | `(opts: OpenImagePickerOptions) => Promise<void>` | Function to open the native image picker and start uploading the selected files. |
| isUploading | boolean | Flag whether file(s) are currently uploading |

```ts
type OpenImagePickerOptions = {
input: TInput; // Matches the input schema from the FileRouter endpoint
/**
* Callback to run if the user cancels the picker.
*/
onCancel?: () => void;
};
```
201 changes: 201 additions & 0 deletions docs/src/pages/getting-started/expo.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import { Callout, Steps } from "nextra-theme-docs";

# Expo Setup

UploadThing is the easiest way to add file uploads to your native mobile
applications powered by export.

<Callout type="info">
Check out a full example
[here](https://github.com/pingdotgg/uploadthing/tree/main/examples/minimal-expo)
</Callout>

## Package Setup

<Steps>

### Install the packages

```bash copy npm2yarn
npm install uploadthing @uploadthing/expo expo-image-picker expo-document-picker
```

### Add env variables

<Callout>
If you don't already have a uploadthing secret key, [sign
up](https://uploadthing.com/sign-in) and create one from the
[dashboard!](https://uploadthing.com/dashboard)
</Callout>

```bash copy
UPLOADTHING_SECRET=... # A secret key for your app (starts with sk_live_)
UPLOADTHING_APP_ID=... # Your app id
EXPO_PUBLIC_SERVER_URL=... # Absolute URL to your server
```

</Steps>

## Set Up A FileRouter

<Callout type="info">
Steps 1 and 2 below assumes you're using [Expo API
routes](https://docs.expo.dev/router/reference/api-routes/) which are
currently experimental. You can also choose to use a standalone server using
some of our [backend adapters, e.g. Fetch](/backend-adapters/fetch)
</Callout>

<Steps>
### Creating your first FileRoute

<Callout>
For more details on how to create a file router, see the
[router](/api-reference/server#file-routes) docs
</Callout>

All files uploaded to uploadthing are associated with a FileRoute. The following
is a very minimalistic example, with a single FileRoute "imageUploader". Think
of a FileRoute similar to an endpoint, it has:

- Permitted types ["image", "video", etc]
- Max file size
- (Optional) `middleware` to authenticate and tag requests
- `onUploadComplete` callback for when uploads are completed

To get full insight into what you can do with the FileRoutes, please refer to
the [File Router API](/api-reference/server#file-routes).

```ts copy filename="src/app/api/uploadthing+api.ts"
import { createUploadthing, UploadThingError } from "uploadthing/server";
import type { FileRouter } from "uploadthing/server";

const f = createUploadthing();

const auth = (req: Request) => ({ id: "fakeId" }); // Fake auth function

const uploadRouter = {
profileImage: f({
image: {
maxFileSize: "4MB",
},
})
.middleware(async ({ req }) => {
// This code runs on your server before upload
const user = await auth(req);

// If you throw, the user will not be able to upload
if (!user) throw new UploadThingError("Unauthorized");

// Whatever is returned here is accessible in onUploadComplete as `metadata`
return { userId: user.id };
})
.onUploadComplete(({ file, metadata }) => {
// This code RUNS ON YOUR SERVER after upload
console.log("Upload complete for userId:", metadata.userId);

console.log("file url", file.url);

// !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback
return { uploadedBy: metadata.userId };
}),
} satisfies FileRouter;
export type UploadRouter = typeof uploadRouter;
```

### Create an API route using the FileRouter

```ts copy filename="src/app/api/uploadthing+api.ts"
import { createRouteHandler } from "uploadthing/server";

const uploadRouter = { ... } satisfies FileRouter;
export type UploadRouter = typeof uploadRouter;

export const { GET, POST } = createRouteHandler({
router: uploadRouter,

// Apply an (optional) custom config:
// config: { ... },
});
```

> See configuration options in
> [server API reference](/api-reference/server#createroutehandler)

### Generate typed hooks

Unlike the other UploadThing packages, the Expo package does not include any
prebuilt components. Instead, we provide some helper hooks to help interact with
the native file pickers.

```tsx copy filename="src/utils/uploadthing.ts"
import { generateReactNativeHelpers } from "@uploadthing/expo";

import type { UploadRouter } from "~/app/api/uploadthing+api";

export const { useImageUploader, useDocumentUploader } =
generateReactNativeHelpers<UploadRouter>({
/**
* Your server url.
* @default process.env.EXPO_PUBLIC_SERVER_URL
* @remarks In dev we will also try to use Expo.debuggerHost
*/
url: "https://my-server.com",
});
```

### Use the FileRouter in your app

```tsx copy filename="src/routes/index.tsx"
import { openSettings } from "expo-linking";
import { Alert, Pressable, StyleSheet, Text, View } from "react-native";

import { useImageUploader } from "~/utils/uploadthing";

export default function Home() {
const { openImagePicker, isUploading } = useImageUploader({
/**
* Any props here are forwarded to the underlying `useUploadThing` hook.
* Refer to the React API reference for more info.
*/
onClientUploadComplete: () => Alert.alert("Upload Completed"),
onUploadError: (error) => Alert.alert("Upload Error", error.message),
});

return (
<View>
<Pressable
style={styles.button}
onPress={() => {
openImagePicker({
input, // Matches the input schema from the FileRouter endpoint
source: "library", // or "camera"
onInsufficientPermissions: () => {
Alert.alert(
"No Permissions",
"You need to grant permission to your Photos to use this",
[
{ text: "Dismiss" },
{ text: "Open Settings", onPress: openSettings },
],
);
},
})
}}
>
<Text>Select Image</Text>
</Pressable>
</View>
);
}

const styles = StyleSheet.create({
button: { ... },
});
```

</Steps>

## Deployment

Follow the
[Expo Router API Routes Deployment Guide](https://docs.expo.dev/router/reference/api-routes/#deployment)
1 change: 1 addition & 0 deletions examples/minimal-expo/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UPLOADTHING_SECRET="sk_live_xxx"
Loading
Loading