Observed since 20 Oct, 21:37 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
9gen
The 9gen SDK is a full-stack development toolkit for React Native/Expo applications, providing authentication, database operations, and API routing.
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 |
|---|---|---|
zodEnvironment{
"local_name": "zod",
"alias": false,
"peer_metadata": null
} | catalog: | runtime |
axiosEnvironment{
"local_name": "axios",
"alias": false,
"peer_metadata": null
} | ^1.13.2 | runtime |
better-authEnvironment{
"local_name": "better-auth",
"alias": false,
"peer_metadata": null
} | catalog: | runtime |
tsdownEnvironment{
"local_name": "tsdown",
"alias": false,
"peer_metadata": null
} | catalog: | development |
@types/nodeEnvironment{
"local_name": "@types/node",
"alias": false,
"peer_metadata": null
} | catalog: | development |
@types/reactEnvironment{
"local_name": "@types/react",
"alias": false,
"peer_metadata": null
} | catalog: | development |
@9gen/tsconfigEnvironment{
"local_name": "@9gen/tsconfig",
"alias": false,
"peer_metadata": null
} | workspace:* | development |
@types/react-domEnvironment{
"local_name": "@types/react-dom",
"alias": false,
"peer_metadata": null
} | catalog: | development |
@9gen/eslint-configEnvironment{
"local_name": "@9gen/eslint-config",
"alias": false,
"peer_metadata": null
} | workspace:* | development |
typescriptEnvironment{
"local_name": "typescript",
"alias": false,
"peer_metadata": null
} | catalog: | peer |
expo-secure-storeEnvironment{
"local_name": "expo-secure-store",
"alias": false,
"peer_metadata": null
} | ^15.0.7 | peer |
expo-apple-authenticationEnvironment{
"local_name": "expo-apple-authentication",
"alias": false,
"peer_metadata": null
} | ^8.0.7 | peer |
Source distribution
{
"shasum": "94a39451404645453d9924481dbfeda5946aa238",
"tarball": "https://registry.npmjs.org/9gen/-/9gen-1.0.1.tgz",
"fileCount": 36,
"integrity": "sha512-clAOBuXnOQMs7sEM1y9Ipd204s1KDKj/oB0zcGCPG/uCbyQWVt5fTtAn+hYJzUDzb0JZp23E6uXjCnKgCfuHOw==",
"signatures": [
{
"sig": "MEUCIQD6fpkEcMtQjlgGPQcgIAp+3C+bj+WGEoOYRoi3PWWwVAIgS0gXMZ5dWupkK0LHVZcwm0mBFv8iVh3FyuwnOMj9je0=",
"keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"
}
],
"unpackedSize": 34362
}Observed since 20 Oct, 21:37 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:6161cd15a7c4cbee49b94db3bb76d460dc4f91095f6ddf05a7a5eb6b2fe5d72e