CRUD operations (Create, Read, Update, Delete) form the backbone of most web applications. When building APIs, these operations typically map to HTTP methods: POST for creating, GET for reading, PUT for updating, and DELETE for removing resources.
In-Memory Storage Pattern
Before connecting to databases, it's common to prototype APIs using in-memory storage. This approach uses simple data structures like arrays or objects to store data temporarily. While data doesn't persist between server restarts, in-memory storage is perfect for development, testing, and learning core API patterns.
Validation and Error Handling
Robust APIs validate input data and provide clear error messages. Validation should check for required fields, data types, and format constraints. For user management, this typically means ensuring names aren't empty and emails follow a basic format.
API Response Consistency
Well-designed APIs return consistent response structures. A common pattern is to use objects with success boolean flags, optional data for successful responses, and error messages for failures. This consistency makes APIs easier to consume and debug.
Separation of Concerns
Good API architecture separates data storage logic from HTTP handling logic. A storage layer (like UserStore) manages data operations, while an API layer (like UserAPI) handles HTTP concerns like validation and response formatting. This separation makes code more testable and maintainable.
1// Storage layer - handles data operations
2class ProductStore {
3 private products: Product[] = [];
4 private nextId = 1;
5
6 create(name: string, price: number): Product {
7 const product = { id: this.nextId++, name, price };
8 this.products.push(product);
9 return product;
10 }
11
12 findAll(): Product[] {
13 return [...this.products]; // Return a copy
14 }
15}
16
17// API layer - handles HTTP concerns
18class ProductAPI {
19 constructor(private store: ProductStore) {}
20
21 createProduct(data: any): APIResponse {
22 // Validation first
23 if (!data.name || typeof data.price !== 'number') {
24 return { success: false, error: 'Invalid product data' };
25 }
26
27 // Then delegate to storage
28 const product = this.store.create(data.name, data.price);
29 return { success: true, data: product };
30 }
31}