Entity Client
Auto-generated CRUD methods for entities with full type safety.
Setup
typescript
import { createTypedClient, entityClient } from "nevr/client"
import type { API } from "./server/api"
const client = createTypedClient<API>({
baseURL: "http://localhost:3000",
plugins: [
entityClient({ entities: ["user", "product", "order"] })
]
})entityClient()
typescript
function entityClient<TEntities>(options?: EntityClientOptions): NevrClientPlugin
interface EntityClientOptions<TEntities> {
/** Entity names to expose */
entities?: string[]
/** Entity actions configuration */
actions?: Record<string, string[]>
/** Type-safe entity definitions from server */
$types?: TEntities
}EntityMethods
Each entity gets these methods:
| Method | Description |
|---|---|
list(options?) | List with pagination, filter, sort |
create(data) | Create new record |
get(id) | Get by ID |
update(id, data) | Update by ID |
delete(id) | Delete by ID |
count(filter?) | Count matching records |
action(name, id?, input?) | Call entity action |
list()
typescript
const { data, error } = await client.products.list({
filter: { published: true, price: { gte: 100 } },
sort: { createdAt: "desc" },
take: 10,
skip: 0,
include: ["category"],
})
// Response
interface ListResponse<T> {
data: T[]
pagination: { total: number; limit: number; offset: number }
}ListOptions
typescript
interface ListOptions<T> {
filter?: EntityFilter<T>
sort?: EntitySort<T>
take?: number
skip?: number
include?: string[]
}Filter Operators
typescript
// Exact match
filter: { status: "active" }
// Operators
filter: { price: { gte: 100, lte: 500 } }
filter: { name: { contains: "phone" } }
filter: { category: { in: ["electronics", "gadgets"] } }
// Logical operators
filter: { OR: [{ status: "active" }, { featured: true }] }
filter: { AND: [{ price: { gte: 100 } }, { stock: { gt: 0 } }] }
filter: { NOT: { deleted: true } }FilterOperators
typescript
interface FilterOperators<T> {
equals?: T
not?: T
in?: T[]
notIn?: T[]
lt?: T
lte?: T
gt?: T
gte?: T
contains?: string
startsWith?: string
endsWith?: string
}create()
typescript
const { data, error } = await client.users.create({
email: "john@example.com",
name: "John Doe",
})
// data = { id: "user_123", email: "...", name: "...", createdAt: "..." }get()
typescript
const { data, error } = await client.users.get("user_123")
if (error) {
console.error(error.message) // "User not found"
}update()
typescript
const { data, error } = await client.users.update("user_123", {
name: "Jane Doe",
})delete()
typescript
const { data, error } = await client.users.delete("user_123")count()
typescript
const { data } = await client.users.count({ role: "admin" })
// data = { count: 5 }action()
Call entity actions:
typescript
// Resource action (with ID)
await client.orders.action("checkout", "order_123", {
paymentMethodId: "pm_xxx",
})
// Resource action (no input)
await client.posts.action("publish", "post_123")
// Collection action (no ID)
await client.products.action("bulkPublish", {
ids: ["prod_1", "prod_2"],
})Type Inference
Full E2E type safety from server using createTypedClient:
typescript
// Server (api.ts)
export const api = nevr({
entities: [user, product],
plugins: [auth(), stripe()],
})
export type API = typeof api
// Client (client.ts)
import { createTypedClient, entityClient } from "nevr/client"
import { authClient } from "nevr/plugins/auth/client"
import { stripeClient } from "nevr/plugins/stripe/client"
import type { API } from "../server/api"
export const client = createTypedClient<API>({
baseURL: "http://localhost:3000",
plugins: [
entityClient({ entities: ["user", "product"] }),
authClient(),
stripeClient(),
// Any plugin works!
],
})
// Fully typed entities!
const { data } = await client.users.list()
const { data: product } = await client.products.create({ name: "Widget" })
// Fully typed plugin methods!
await client.auth.signIn.email({ email: "...", password: "..." })
await client.stripe.createCheckout({ priceId: "..." })TIP
createTypedClient<API>() = createClient() + entity type inference from your server API. Use the same plugins: [...] pattern - works with any plugin!
NOTE
The import type { API } is required for entity type safety. This is a TypeScript requirement - runtime strings like ["user"] cannot carry type information.
Examples
Paginated List
typescript
async function getPage(page: number, pageSize = 10) {
return client.products.list({
take: pageSize,
skip: (page - 1) * pageSize,
sort: { createdAt: "desc" },
})
}Search
typescript
async function search(query: string) {
return client.products.list({
filter: {
OR: [
{ name: { contains: query } },
{ description: { contains: query } },
]
}
})
}Filter by Relation
typescript
const { data } = await client.orders.list({
filter: { customerId: "user_123", status: "pending" },
include: ["items", "customer"],
})