@geekapps/silo-react-native
v0.3.0
Published
Silo SDK for React Native / Expo — typed client + React hooks
Readme
@geekapps/silo-react-native
Silo SDK for React Native and Expo apps — typed client + React hooks. Works in both Expo (managed) and bare React Native projects; it has no dependency on either.
Install
bun add @geekapps/silo-react-nativeAuth: token is always required, fetched from your backend
A mobile app's bundle can always be extracted from a public build. Because of
that, this SDK never generates or stores a Silo service account credential
itself — there is no serviceAccount option, and SiloClient holds no
token internally. Every authenticated call takes an explicit, required
token argument.
Your own backend — the one place a service account secret is safe to live — should hold the Silo service account credentials and expose an endpoint that mints a short-lived token for your app to fetch:
async function getSiloToken(): Promise<string> {
const res = await fetch("https://your-backend.com/silo-token", {
headers: { Authorization: `Bearer ${yourAppSessionToken}` }, // however your backend authenticates the app
});
const { token } = await res.json();
return token;
}That backend endpoint, in turn, calls Silo's own service-account token
endpoint server-side (see @geekapps/silo-nextjs's ServiceAccountTokenProvider
for a reference implementation) and returns just the short-lived token to the
app — the secret itself never leaves your server.
Setup
<SiloProvider> only configures where to reach Silo — no credentials:
import { SiloProvider } from "@geekapps/silo-react-native";
export default function App() {
return (
<SiloProvider baseUrl="https://api.silo.geekapps.com.br">
{/* ... */}
</SiloProvider>
);
}Usage
import { useUpload } from "@geekapps/silo-react-native";
import * as ImagePicker from "expo-image-picker";
function UploadButton() {
const { state, upload } = useUpload({ bucket: "avatars" });
async function pickAndUpload() {
const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: "images" });
if (result.canceled) return;
const asset = result.assets[0];
const token = await getSiloToken();
await upload(
{
uri: asset.uri,
name: asset.fileName ?? "photo.jpg",
type: asset.mimeType ?? "image/jpeg",
size: asset.fileSize,
},
token
);
}
return (
<Button onPress={pickAndUpload} title={state.status === "uploading" ? `${state.progress}%` : "Upload"} />
);
}Or without hooks, using the client directly:
import { SiloClient } from "@geekapps/silo-react-native";
const silo = new SiloClient({ baseUrl: "https://api.silo.geekapps.com.br" });
const token = await getSiloToken();
const result = await silo.upload({ uri, name: "video.mp4", type: "video/mp4", size }, token);
const playbackUrl = await silo.getHlsStreamUrl(result.id, token);Hooks
Every hook that touches an authenticated endpoint takes token as an
explicit argument (usually right after the resource id) — there's no
implicit/ambient auth.
useUpload()→upload(file, token, opts?)— single-file upload with progressuseBatchUpload()→upload(files, token, opts?)— upload multiple files at onceuseSignedUrl(fileId, token, opts?)— presigned download URL for a fileuseFileStatus(fileId, token, opts?)— poll a file's processing status until it's ready/faileduseDeleteFile()→deleteFile(fileId, token)— delete a fileuseThumbnailUrl(fileId, token, screenW?)/useStoryboardUrl(...)/useHlsStreamUrl(fileId, token)— media URLs for a fileuseSiloClient()— the rawSiloClientinstance from context, for anything not covered by a hook
Large file / true multipart uploads
React Native has no File.slice() equivalent, so this SDK's upload() and
useUpload/useBatchUpload send each file as a single part — that's fine for
images and most videos recorded on-device. For very large files where you want
real chunked/resumable multipart upload, read and slice the file yourself (e.g.
with expo-file-system's base64 reading, or react-native-blob-util) and drive
the primitives directly:
const { fileId, uploadId } = await silo.startMultipartUpload({ fileName, mimeType, size, bucket }, token);
for (const chunk of yourChunks) {
const { url } = await silo.getUploadPartUrl(fileId, uploadId, partNumber, token);
await silo.uploadPart(url, chunk, mimeType); // part URLs are presigned — no token needed here
}
await silo.completeMultipartUpload(fileId, uploadId, parts, token);