Create Operations
Create operations in Routier allow you to add new entities to your collections. The framework provides both synchronous and asynchronous methods, with automatic change tracking and type safety.
Quick Navigation
- Overview
- Basic Create Operations
- Schema-Driven Creation
- Type Safety and Error Handling
- Advanced Create Patterns
- Performance Considerations
- Best Practices
- Common Patterns
- Next Steps
Overview
When you create entities in Routier:
- Entities are type-checked against your schema
- Default values are applied automatically
- Identity fields are generated if specified
- Changes are tracked for later persistence
- Entities are returned with all properties set
⚠️ Important: Persistence Requires Save
Note: When you call addAsync(), the entity is added to the collection in memory, but it is NOT automatically persisted to the database. You must call saveChanges() or saveChangesAsync() to persist the changes.
Basic Create Operations
Adding Single Entities
The simplest way to create a new entity is using addAsync():
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema
const userSchema = s.define("users", {
id: s.string().key().identity(),
name: s.string(),
email: s.string().distinct(),
age: s.number().optional(),
createdAt: s.date().default(() => new Date()),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// Adding a single entity
const newUser = await ctx.users.addAsync({
name: "John Doe",
email: "[email protected]",
age: 30
});
console.log("Created user:", newUser);
// Output: [{ id: "generated-uuid", name: "John Doe", email: "[email protected]", age: 30, createdAt: Date }]
// Don't forget to save changes!
await ctx.saveChangesAsync();Adding Multiple Entities
You can add multiple entities in a single operation for better performance:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a product schema
const productSchema = s.define("products", {
id: s.string().key().identity(),
name: s.string(),
price: s.number(),
category: s.string("electronics", "books", "clothing"),
inStock: s.boolean().default(true),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
products = this.collection(productSchema).create();
}
const ctx = new AppContext();
// Adding multiple entities at once
const newProducts = await ctx.products.addAsync(
{
name: "Laptop",
price: 999.99,
category: "electronics"
},
{
name: "JavaScript Book",
price: 29.99,
category: "books"
},
{
name: "T-Shirt",
price: 19.99,
category: "clothing"
}
);
console.log("Created products:", newProducts);
// Output: Array of 3 products with generated IDs and default values
// Save all changes
await ctx.saveChangesAsync();Adding with Callbacks
For advanced scenarios, you can use callback-based operations with error handling:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema
const userSchema = s.define("users", {
id: s.string().key().identity(),
name: s.string(),
email: s.string().distinct(),
createdAt: s.date().default(() => new Date()),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// Adding with callback (discriminated union result pattern)
ctx.users.add([{
name: "Jane Doe",
email: "[email protected]"
}], (result) => {
if (result.ok === "success") {
console.log("User created successfully:", result.data);
// result.data is InferType<typeof userSchema>[]
} else {
console.error("Failed to create user:", result.error);
// result.error contains the error details
}
});
// Save changes after callback completes
await ctx.saveChangesAsync();Schema-Driven Creation
Automatic Default Values
Routier automatically applies default values defined in your schema:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a post schema with default values
const postSchema = s.define("posts", {
id: s.string().key().identity(),
title: s.string(),
content: s.string(),
status: s.string("draft", "published", "archived").default("draft"),
views: s.number().default(0),
publishedAt: s.date().optional(),
createdAt: s.date().default(() => new Date()),
updatedAt: s.date().default(() => new Date()),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
posts = this.collection(postSchema).create();
}
const ctx = new AppContext();
// Creating with minimal data - defaults are applied automatically
const newPost = await ctx.posts.addAsync({
title: "My First Post",
content: "This is the content of my first post."
});
console.log("Created post:", newPost);
// Output: [{
// id: "generated-uuid",
// title: "My First Post",
// content: "This is the content of my first post.",
// status: "draft", // ← Default applied
// views: 0, // ← Default applied
// publishedAt: undefined, // ← Optional field
// createdAt: Date, // ← Default applied
// updatedAt: Date // ← Default applied
// }]
await ctx.saveChangesAsync();Identity Field Generation
Identity fields are automatically generated when creating new entities:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema with identity field
const userSchema = s.define("users", {
id: s.string().key().identity(), // Identity field - datastore generates value
email: s.string().distinct(),
name: s.string(),
createdAt: s.date().default(() => new Date()),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// Creating without providing ID - identity field is auto-generated
const newUser = await ctx.users.addAsync({
email: "[email protected]",
name: "Alice Smith"
// No id provided - datastore will generate it
});
console.log("Created user:", newUser);
// Output: [{
// id: "generated-uuid", // ← Auto-generated by datastore
// email: "[email protected]",
// name: "Alice Smith",
// createdAt: Date
// }]
await ctx.saveChangesAsync();Nested Object Creation
You can create entities with nested objects and arrays:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema with nested objects
const userSchema = s.define("users", {
id: s.string().key().identity(),
name: s.string(),
email: s.string().distinct(),
profile: s.object({
firstName: s.string(),
lastName: s.string(),
bio: s.string().optional(),
avatar: s.string().optional(),
}),
preferences: s.object({
theme: s.string("light", "dark").default("light"),
notifications: s.boolean().default(true),
}).default({}),
createdAt: s.date().default(() => new Date()),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// Creating with nested objects
const newUser = await ctx.users.addAsync({
name: "John Doe",
email: "[email protected]",
profile: {
firstName: "John",
lastName: "Doe",
bio: "Software developer",
// avatar is optional
},
// preferences will use default value {}
});
console.log("Created user:", newUser);
// Output: [{
// id: "generated-uuid",
// name: "John Doe",
// email: "[email protected]",
// profile: {
// firstName: "John",
// lastName: "Doe",
// bio: "Software developer",
// avatar: undefined
// },
// preferences: { theme: "light", notifications: true }, // ← Default applied
// createdAt: Date
// }]
await ctx.saveChangesAsync();Type Safety and Error Handling
Schema Type Checking
Routier provides compile-time type safety through TypeScript:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema with distinct constraint
const userSchema = s.define("users", {
id: s.string().key().identity(),
email: s.string().distinct(), // Creates database index for uniqueness
name: s.string(),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// TypeScript will catch type mismatches at compile time
type CreateUser = InferCreateType<typeof userSchema>;
// ✅ Valid - TypeScript ensures correct types
const validUser: CreateUser = {
email: "[email protected]",
name: "John Doe"
};
// ❌ Invalid - TypeScript will show error
// const invalidUser: CreateUser = {
// email: 123, // Error: Type 'number' is not assignable to type 'string'
// name: "John Doe"
// };
// Note: Routier does not enforce unique constraints at runtime
// The .distinct() modifier only creates database indexes
// Duplicate emails will be allowed in memory and may cause issues at database level
const duplicateUser = await ctx.users.addAsync({
email: "[email protected]", // This will be allowed even if another user has this email
name: "Jane Doe"
});
await ctx.saveChangesAsync();TypeScript Type Safety
Use InferCreateType for proper type inference:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema
const userSchema = s.define("users", {
id: s.string().key().identity(),
name: s.string(),
email: s.string().distinct(),
age: s.number(),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// TypeScript type safety example
type CreateUser = InferCreateType<typeof userSchema>;
function createUserSafely(userData: CreateUser) {
// TypeScript ensures userData has correct structure
return ctx.users.addAsync(userData);
}
// ✅ TypeScript will catch these errors at compile time:
// createUserSafely({ name: "John" }); // Error: Missing required field 'email'
// createUserSafely({ name: "John", email: "[email protected]", age: "thirty" }); // Error: Wrong type for age
// ✅ This will work - TypeScript ensures type safety
const user = await createUserSafely({
name: "John Doe",
email: "[email protected]",
age: 30
});
console.log("Created user:", user);
await ctx.saveChangesAsync();Constraint Enforcement
Constraint enforcement depends on your plugin implementation:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema with constraints
const userSchema = s.define("users", {
id: s.string().key().identity(),
email: s.string().distinct(), // Unique constraint
age: s.number(18, 19, 20, 21), // Literal constraint
role: s.string("admin", "user", "guest").default("user"),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// Constraint enforcement is handled by plugins, not Routier core
// Different plugins may behave differently:
// Memory plugin - allows duplicates and invalid values
const user1 = await ctx.users.addAsync({
email: "[email protected]",
age: 25, // Not in allowed literals (18,19,20,21) but will be allowed
});
const user2 = await ctx.users.addAsync({
email: "[email protected]", // Duplicate email - will be allowed in memory
age: 18, // Valid literal
});
console.log("Both users created:", user1, user2);
// Note:
// - SQLite plugins may enforce unique constraints at database level
// - Memory plugins allow duplicates in memory
// - Check your specific plugin's documentation for constraint behavior
await ctx.saveChangesAsync();Advanced Create Patterns
Conditional Creation
Create entities based on conditions or business logic:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema
const userSchema = s.define("users", {
id: s.string().key().identity(),
name: s.string(),
email: s.string().distinct(),
isActive: s.boolean().default(true),
createdAt: s.date().default(() => new Date()),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// Conditional creation based on business logic
async function createUserIfNotExists(userData: InferCreateType<typeof userSchema>) {
// Check if user already exists
const existingUser = await ctx.users.firstOrUndefinedAsync(u => u.email === userData.email);
if (existingUser) {
console.log("User already exists:", existingUser);
return existingUser;
}
// Create new user
const newUser = await ctx.users.addAsync(userData);
console.log("Created new user:", newUser);
return newUser;
}
// Usage
const user = await createUserIfNotExists({
name: "John Doe",
email: "[email protected]"
});
await ctx.saveChangesAsync();Batch Creation with Type Safety
Create multiple entities with proper type checking:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a product schema
const productSchema = s.define("products", {
id: s.string().key().identity(),
name: s.string(),
price: s.number(),
category: s.string("electronics", "books", "clothing"),
inStock: s.boolean().default(true),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
products = this.collection(productSchema).create();
}
const ctx = new AppContext();
// Batch creation with type safety
async function createProductsBatch(productsData: InferCreateType<typeof productSchema>[]) {
try {
// TypeScript ensures all items have correct structure
const createdProducts = await ctx.products.addAsync(...productsData);
console.log(`Successfully created ${createdProducts.length} products`);
return createdProducts;
} catch (error) {
console.error("Failed to create products:", error);
throw error;
}
}
// Usage with type-safe data
const productsToCreate: InferCreateType<typeof productSchema>[] = [
{ name: "Laptop", price: 999.99, category: "electronics" },
{ name: "Book", price: 29.99, category: "books" },
{ name: "Shirt", price: 19.99, category: "clothing" }
];
const createdProducts = await createProductsBatch(productsToCreate);
await ctx.saveChangesAsync();Creation with Computed Fields
Create entities that include computed or derived fields:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a post schema with computed fields
const postSchema = s.define("posts", {
id: s.string().key().identity(),
title: s.string(),
content: s.string(),
author: s.string(),
publishedAt: s.date().optional(),
}).modify(w => ({
// Computed field - automatically calculated
slug: w.computed((entity) =>
entity.title.toLowerCase().replace(/\s+/g, '-')
).tracked(),
// Function field - non-persisted method
isPublished: w.function((entity) => entity.publishedAt != null),
})).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
posts = this.collection(postSchema).create();
}
const ctx = new AppContext();
// Creating with computed fields
const newPost = await ctx.posts.addAsync({
title: "My First Post",
content: "This is the content of my first post.",
author: "John Doe"
});
console.log("Created post:", newPost);
// Output: [{
// id: "generated-uuid",
// title: "My First Post",
// content: "This is the content of my first post.",
// author: "John Doe",
// publishedAt: undefined,
// slug: "my-first-post", // ← Computed field
// isPublished: function // ← Function field
// }]
// Access computed and function fields
const post = newPost[0];
console.log("Slug:", post.slug); // "my-first-post"
console.log("Is published:", post.isPublished()); // false
await ctx.saveChangesAsync();Performance Considerations
Batch Creation
Batch creation is more efficient than individual creates:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema
const userSchema = s.define("users", {
id: s.string().key().identity(),
name: s.string(),
email: s.string().distinct(),
createdAt: s.date().default(() => new Date()),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// Batch creation for better performance
async function createUsersBatch(userDataList: InferCreateType<typeof userSchema>[]) {
const startTime = performance.now();
// Create all users in a single operation
const createdUsers = await ctx.users.addAsync(...userDataList);
const endTime = performance.now();
console.log(`Created ${createdUsers.length} users in ${endTime - startTime}ms`);
return createdUsers;
}
// Generate test data
const usersToCreate: InferCreateType<typeof userSchema>[] = Array.from({ length: 100 }, (_, i) => ({
name: `User ${i + 1}`,
email: `user${i + 1}@example.com`
}));
// Batch create all users at once
const createdUsers = await createUsersBatch(usersToCreate);
// Save all changes in one operation
await ctx.saveChangesAsync();
console.log(`Total users created: ${createdUsers.length}`);Memory Management
Consider memory usage when creating large numbers of entities:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema
const userSchema = s.define("users", {
id: s.string().key().identity(),
name: s.string(),
email: s.string().distinct(),
profile: s.object({
bio: s.string().optional(),
avatar: s.string().optional(),
}).optional(),
createdAt: s.date().default(() => new Date()),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// Memory-efficient creation patterns
async function createUserEfficiently(userData: InferCreateType<typeof userSchema>) {
// Create user with minimal memory footprint
const user = await ctx.users.addAsync(userData);
// Access properties directly (no copying)
console.log("User ID:", user[0].id);
console.log("User name:", user[0].name);
return user[0];
}
// Avoid creating unnecessary intermediate objects
const userData: InferCreateType<typeof userSchema> = {
name: "John Doe",
email: "[email protected]"
// profile is optional - don't create empty object
};
const user = await createUserEfficiently(userData);
// Direct property access is memory efficient
user.name = "John Updated"; // Direct modification, no copying
await ctx.saveChangesAsync();Best Practices
1. Type-Check Data Before Creation
Validate data before creating entities:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema
const userSchema = s.define("users", {
id: s.string().key().identity(),
name: s.string(),
email: s.string().distinct(),
age: s.number().optional(),
createdAt: s.date().default(() => new Date()),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// Type-check data before creation
function validateUserData(data: any): data is InferCreateType<typeof userSchema> {
return (
typeof data === 'object' &&
typeof data.name === 'string' &&
typeof data.email === 'string' &&
(data.age === undefined || typeof data.age === 'number')
);
}
async function createUserSafely(userData: any) {
// Validate data structure before creation
if (!validateUserData(userData)) {
throw new Error("Invalid user data structure");
}
// TypeScript now knows userData is valid
const user = await ctx.users.addAsync(userData);
console.log("User created successfully:", user);
return user;
}
// Usage
try {
const user = await createUserSafely({
name: "John Doe",
email: "[email protected]",
age: 30
});
await ctx.saveChangesAsync();
} catch (error) {
console.error("Failed to create user:", error);
}2. Use Appropriate Default Values
Define meaningful default values in your schema:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema with appropriate defaults
const userSchema = s.define("users", {
id: s.string().key().identity(),
name: s.string(),
email: s.string().distinct(),
role: s.string("admin", "user", "guest").default("user"), // Sensible default
isActive: s.boolean().default(true), // Sensible default
lastLogin: s.date().optional(), // No default - let it be undefined
createdAt: s.date().default(() => new Date()),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// Use appropriate default values
async function createUserWithDefaults(userData: InferCreateType<typeof userSchema>) {
// Only provide required fields - defaults handle the rest
const user = await ctx.users.addAsync({
name: userData.name,
email: userData.email
// role defaults to "user"
// isActive defaults to true
// createdAt defaults to current date
// lastLogin remains undefined (optional)
});
console.log("User created with defaults:", user);
return user;
}
// Usage
const user = await createUserWithDefaults({
name: "Jane Doe",
email: "[email protected]"
});
await ctx.saveChangesAsync();3. Handle Errors Gracefully
Implement proper error handling for create operations:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema
const userSchema = s.define("users", {
id: s.string().key().identity(),
name: s.string(),
email: s.string().distinct(),
createdAt: s.date().default(() => new Date()),
}).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// Handle errors gracefully
async function createUserWithErrorHandling(userData: InferCreateType<typeof userSchema>) {
try {
const user = await ctx.users.addAsync(userData);
console.log("User created successfully:", user);
return user;
} catch (error) {
// Handle different types of errors
if (error instanceof Error) {
console.error("Error creating user:", error.message);
// Handle specific error types
if (error.message.includes("network")) {
console.error("Network error occurred");
// Handle network issues
} else if (error.message.includes("database")) {
console.error("Database error occurred");
// Handle database issues
} else {
console.error("Unexpected error:", error);
// Handle unexpected errors
}
}
throw error; // Re-throw for caller to handle
}
}
// Usage with error handling
try {
const user = await createUserWithErrorHandling({
name: "John Doe",
email: "[email protected]"
});
await ctx.saveChangesAsync();
} catch (error) {
console.error("Failed to create and save user:", error);
// Handle the error appropriately (retry, show user message, etc.)
}4. Leverage Schema Features
Use schema features like enums, defaults, and constraints effectively:
import { DataStore } from "@routier/datastore";
import { MemoryPlugin } from "@routier/memory-plugin";
import { s, InferCreateType } from "@routier/core/schema";
// Define a user schema leveraging schema features
const userSchema = s.define("users", {
id: s.string().key().identity(), // Auto-generated IDs
email: s.string().distinct(), // Unique constraint with indexing
name: s.string(),
role: s.string("admin", "user", "guest").default("user"), // Literal constraints
isActive: s.boolean().default(true), // Default values
createdAt: s.date().default(() => new Date()), // Function defaults
lastLogin: s.date().optional(), // Optional fields
}).modify(w => ({
// Computed properties
displayName: w.computed((entity) =>
`${entity.name} (${entity.role})`
).tracked(),
// Function methods
isAdmin: w.function((entity) => entity.role === "admin"),
})).compile();
// Create DataStore with collection
class AppContext extends DataStore {
constructor() {
super(new MemoryPlugin("app-db"));
}
users = this.collection(userSchema).create();
}
const ctx = new AppContext();
// Leverage schema features for powerful creation
async function createUserWithSchemaFeatures(userData: InferCreateType<typeof userSchema>) {
// Schema handles:
// - Auto-generated ID
// - Default values (role, isActive, createdAt)
// - Type checking
// - Computed properties
// - Function methods
const user = await ctx.users.addAsync(userData);
// Access computed and function properties
const createdUser = user[0];
console.log("Display name:", createdUser.displayName); // Computed
console.log("Is admin:", createdUser.isAdmin()); // Function
return createdUser;
}
// Usage
const user = await createUserWithSchemaFeatures({
email: "[email protected]",
name: "Admin User"
// role defaults to "user" (but we can override)
});
await ctx.saveChangesAsync();Common Patterns
User Registration
const newUser = await ctx.users.addAsync({
name: userData.name,
email: userData.email,
passwordHash: await hashPassword(userData.password),
});
await ctx.saveChangesAsync();
Product Catalog Management
const products = await ctx.products.addAsync(
...productData.map((p) => ({
name: p.name,
price: p.price,
category: p.category,
inStock: true,
}))
);
await ctx.saveChangesAsync();
Bulk Data Import
const batchSize = 100;
for (let i = 0; i < data.length; i += batchSize) {
const batch = data.slice(i, i + batchSize);
await ctx.items.addAsync(...batch);
await ctx.saveChangesAsync();
}
Next Steps
- Data Manipulation - Learn about proxy-based updates and array/object manipulation
- Read Operations - Learn how to query and retrieve data
- Update Operations - Learn how to modify existing entities
- Delete Operations - Learn how to remove entities
- Bulk Operations - Learn how to handle multiple entities efficiently