Current collected state Current
Observed since 20 Oct, 21:37 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
9gen
9gen SDK
The 9gen SDK is a full-stack development toolkit for React Native/Expo applications, providing authentication, database operations, and API routing.
npm install 9gen zod
# or
yarn add 9gen zod
# or
bun add 9gen zod
Create a client instance in your app (e.g., lib/9gen.ts):
import { createClient } from "9gen";
import { z } from "zod";
export const client = createClient({
__DEV__, // Development mode flag
appId: "your-app-id", // Your 9gen application ID
baseURL: "https://api.9gen.dev", // API base URL (or localhost for dev)
requireAuth: true, // Require authentication for API routes
scheme: "myapp", // Your app's URL scheme
storagePrefix: "myapp", // Optional: prefix for secure storage keys
db: {
models: {
Todo: z.object({
title: z.string(),
description: z.string(),
completed: z.boolean(),
}),
// Add more models as needed
},
},
});
| Option | Type | Required | Description |
|---|---|---|---|
__DEV__ | boolean | Yes | Development mode flag (uses "exp" scheme in dev) |
appId | string | Yes | Your 9gen application ID |
baseURL | string | No | API base URL (default: "https://api.9gen.dev") |
requireAuth | boolean | No | Require authentication for API routes (default: false) |
scheme | string | Yes | Your app's URL scheme for OAuth redirects |
storagePrefix | string | No | Prefix for secure storage keys (default: appId) |
db.models | Record<string, ZodObject> | Yes | Database model definitions using Zod schemas |
// Get current session
const { data: session } = client.auth.useSession();
// Check if user is authenticated
if (session?.user) {
console.log("User:", session.user);
}
// Google Sign In
await client.auth.signIn.social({
provider: "google",
callbackURL: "/",
});
// Apple Sign In
await client.auth.signIn.social({
provider: "apple",
callbackURL: "/",
});
// Step 1: Send OTP to email
await client.auth.emailOtp.sendVerificationOtp({
email: "user@example.com",
type: "sign-in",
});
// Step 2: Verify OTP
await client.auth.signIn.emailOtp({
email: "user@example.com",
otp: "123456",
});
await client.auth.signOut();
The SDK automatically creates typed database entities based on your Zod schemas.
const todo = await db.Todo.insert({
title: "Learn 9gen SDK",
description: "Read the documentation",
completed: false,
});
// Returns: { id: string, title: string, description: string, completed: boolean }
const todo = await db.Todo.get("todo-id");
const updatedTodo = await db.Todo.update("todo-id", {
completed: true,
});
await db.Todo.delete("todo-id");
const result = await db.Todo.list({
query: { completed: false }, // Filter criteria
sort: "createdAt", // Sort field
limit: 10, // Number of items
skip: 0, // Pagination offset
fields: ["title", "completed"], // Select specific fields
});
// Returns: { entities: Todo[], totalCount: number }
const result = await db.Todo.deleteMany({
completed: true,
});
// Returns: { success: boolean, deletedCount: number }
Create server-side API routes in your Expo app with automatic authentication and database access.
Create a file like app/api/todos+api.ts:
import { client } from "@/lib/9gen";
export const GET = client.api.handler(async (request, context) => {
const { user, db } = context;
// User is automatically authenticated (if requireAuth: true)
console.log("Current user:", user);
// Database entities are available
const todos = await db.Todo.list();
return new Response(JSON.stringify(todos), {
headers: { "Content-Type": "application/json" },
});
});
export const POST = client.api.handler(async (request, context) => {
const { user, db } = context;
const body = await request.json();
const newTodo = await db.Todo.insert(body);
return new Response(JSON.stringify(newTodo), {
status: 201,
headers: { "Content-Type": "application/json" },
});
});
// Automatically includes authentication headers
const response = await client.api.fetch("/api/todos");
const todos = await response.json();
All entities are fully typed based on your Zod schemas:
// Define schema
const TodoSchema = z.object({
title: z.string(),
description: z.string(),
completed: z.boolean(),
});
// Entity type is automatically inferred
type Todo = z.infer<typeof TodoSchema> & { id: string };
// Full autocomplete and type safety
const todo = await db.Todo.insert({
title: "...", // ✓ Type-safe
description: "...", // ✓ Type-safe
completed: false, // ✓ Type-safe
// @ts-expect-error - Property doesn't exist
invalid: "field", // ✗ Type error
});
// lib/9gen.ts
import { createClient } from "9gen";
import { z } from "zod";
export const client = createClient({
__DEV__,
appId: "my-app",
baseURL: "http://localhost:3000",
requireAuth: true,
scheme: "myapp",
db: {
models: {
Todo: z.object({
title: z.string(),
description: z.string(),
completed: z.boolean(),
}),
User: z.object({
name: z.string(),
email: z.string().email(),
}),
},
},
});
// app/index.tsx
import { client } from "@/lib/9gen";
import { View, Button } from "react-native";
export default function Home() {
const { data: session } = client.auth.useSession();
const handleLogin = async () => {
await client.auth.signIn.social({
provider: "google",
callbackURL: "/",
});
};
const handleFetchTodos = async () => {
const response = await client.api.fetch("/api/todos");
const todos = await response.json();
console.log(todos);
};
return (
<View>
{!session ? (
<Button title="Sign In" onPress={handleLogin} />
) : (
<>
<Button title="Fetch Todos" onPress={handleFetchTodos} />
<Button title="Sign Out" onPress={() => client.auth.signOut()} />
</>
)}
</View>
);
}
// app/api/todos+api.ts
import { client } from "@/lib/9gen";
export const GET = client.api.handler(async (request, context) => {
const { user, db } = context;
const todos = await db.Todo.list();
return new Response(JSON.stringify(todos));
});
client.api.fetch automatically includes auth headerslib/9gen.tsapp/api/*+api.tsapp/index.tsx:8 (useSession)app/index.tsx:10-22app/index.tsx:23-38app/api/hello+api.ts:4-27app/index.tsx:44-48Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
tsupEnvironment{
"local_name": "tsup",
"alias": false,
"peer_metadata": null
} | ^8.5.0 | development |
@types/bunEnvironment{
"local_name": "@types/bun",
"alias": false,
"peer_metadata": null
} | latest | development |
@types/jestEnvironment{
"local_name": "@types/jest",
"alias": false,
"peer_metadata": null
} | ^30.0.0 | development |
@jest/globalsEnvironment{
"local_name": "@jest/globals",
"alias": false,
"peer_metadata": null
} | ^30.1.2 | development |
firebase-adminEnvironment{
"local_name": "firebase-admin",
"alias": false,
"peer_metadata": null
} | ^13.5.0 | development |
zodEnvironment{
"local_name": "zod",
"alias": false,
"peer_metadata": null
} | ^4.1.8 | peer |
reactEnvironment{
"local_name": "react",
"alias": false,
"peer_metadata": null
} | ^19.0.0 | peer |
firebaseEnvironment{
"local_name": "firebase",
"alias": false,
"peer_metadata": null
} | ^12.2.1 | peer |
typescriptEnvironment{
"local_name": "typescript",
"alias": false,
"peer_metadata": null
} | ^5 | peer |
react-nativeEnvironment{
"local_name": "react-native",
"alias": false,
"peer_metadata": null
} | ^0.79.0 | peer |
@tanstack/react-queryEnvironment{
"local_name": "@tanstack/react-query",
"alias": false,
"peer_metadata": null
} | ^5.81.2 | peer |
react-native-reanimatedEnvironment{
"local_name": "react-native-reanimated",
"alias": false,
"peer_metadata": null
} | ^4.1.0 | peer |
expo-apple-authenticationEnvironment{
"local_name": "expo-apple-authentication",
"alias": false,
"peer_metadata": null
} | ~7.2.4 | peer |
@react-native-async-storage/async-storageEnvironment{
"local_name": "@react-native-async-storage/async-storage",
"alias": false,
"peer_metadata": null
} | ^2.1.2 | peer |
Source distribution
{
"shasum": "72435e54f8fa114a9e10ea2291f8cda73a2b9981",
"tarball": "https://registry.npmjs.org/9gen/-/9gen-0.0.18.tgz",
"fileCount": 69,
"integrity": "sha512-IAm34z7pCIs37g1jpdvHFgbmUgn2p5mKA98E92XbjQyB74SzhVTnA83pMzksHVwZVtHk7gyTrdtS72i4Vpv9sg==",
"signatures": [
{
"sig": "MEUCIQCiX+4TLljw9YTGdlB9d4wc4kpAkBUOJE+hsShdDZ1c3QIgBlaHgZXBmZa3ldFKFoDYK12WrEnW0TayNuQsXxYYfbU=",
"keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"
}
],
"unpackedSize": 298727
}Observed since 20 Oct, 21:37 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:49714ecd506d4c27ae22fc71d13bb8bd8888ea2a6748cfa02f116c02499215e2