10 CRUD Interview Questions and Answers
Prepare for your next technical interview with our guide on CRUD operations, covering the core concepts of data management and manipulation.
Prepare for your next technical interview with our guide on CRUD operations, covering the core concepts of data management and manipulation.
CRUD, an acronym for Create, Read, Update, and Delete, represents the foundational operations of persistent storage in software applications. These operations are essential for managing data in databases and are integral to the functionality of most web and mobile applications. Understanding CRUD operations is crucial for developers as they form the backbone of data manipulation and management in any system.
This article offers a curated selection of interview questions focused on CRUD operations. By reviewing these questions and their answers, you will gain a deeper understanding of how to effectively implement and optimize CRUD functionalities, thereby enhancing your readiness for technical interviews.
CRUD stands for Create, Read, Update, and Delete, which are the four basic operations for managing data in a database. Each operation corresponds to a specific HTTP method in RESTful APIs.
In RESTful APIs, PUT and PATCH methods update resources but differ in their approach.
The PUT method replaces a resource entirely, requiring a complete representation. It can also create a resource if it doesn’t exist. PUT is idempotent, meaning multiple identical requests have the same effect as one.
The PATCH method applies partial updates, sending only the changes. PATCH is not necessarily idempotent, as outcomes can vary based on the resource’s state.
Optimistic locking handles concurrent database updates by assuming conflicts are rare and checking for them at transaction commit time. It’s useful in CRUD applications where multiple users interact with the same data.
To implement it, add a version number or timestamp to records. When reading a record, its version is also read. During an update, the version is checked to ensure it hasn’t changed. If it has, the update is aborted.
Example using SQL and Python:
import sqlite3 def update_record(id, new_value, version): conn = sqlite3.connect('example.db') cursor = conn.cursor() cursor.execute("SELECT version FROM records WHERE id = ?", (id,)) current_version = cursor.fetchone()[0] if current_version != version: raise Exception("Record has been modified by another transaction") new_version = current_version + 1 cursor.execute("UPDATE records SET value = ?, version = ? WHERE id = ?", (new_value, new_version, id)) conn.commit() conn.close() # Example usage try: update_record(1, 'new_value', 1) except Exception as e: print(e)
In this example, update_record
retrieves the current version, checks for changes, and updates the record if the version is unchanged.
To retrieve users who registered in the last 30 days, use this SQL query. It assumes a users
table with a registration_date
column.
SELECT * FROM users WHERE registration_date >= CURDATE() - INTERVAL 30 DAY;
Pagination divides large datasets into smaller chunks, improving performance in RESTful APIs. It retrieves a subset of data at a time, reducing server and client load.
Use query parameters like page
and limit
to specify the page number and items per page. The server uses these to determine the data subset to return.
Example:
from flask import Flask, request, jsonify app = Flask(__name__) # Sample data data = list(range(1, 101)) # A list of 100 items @app.route('/items', methods=['GET']) def get_items(): page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 10)) start = (page - 1) * limit end = start + limit return jsonify(data[start:end]) if __name__ == '__main__': app.run(debug=True)
In this example, get_items
calculates indices for the data subset based on page
and limit
.
Indexes speed up row retrieval by using pointers, created on columns frequently used in WHERE clauses. They improve read performance but can slow down writes due to index updates.
Example of creating an index in SQL:
CREATE INDEX idx_employee_name ON employees (name);
This index on the name
column of the employees
table speeds up name-based queries.
ACID properties ensure reliable database transactions, maintaining data integrity during CRUD operations. ACID stands for Atomicity, Consistency, Isolation, and Durability.
Validating data before CRUD operations ensures data integrity and security. Validation can occur at client-side, server-side, and database-level.
Client-side validation provides immediate feedback but can be bypassed.
Server-side validation is more secure, ensuring data is validated before server processing.
Database-level validation uses constraints and triggers to enforce rules.
Example of server-side validation in Python with Flask:
from flask import Flask, request, jsonify from wtforms import Form, StringField, IntegerField, validators app = Flask(__name__) class UserForm(Form): name = StringField('Name', [validators.Length(min=1, max=50)]) age = IntegerField('Age', [validators.NumberRange(min=0, max=120)]) @app.route('/create_user', methods=['POST']) def create_user(): form = UserForm(request.form) if form.validate(): return jsonify({"message": "User created successfully"}), 201 else: return jsonify({"errors": form.errors}), 400 if __name__ == '__main__': app.run(debug=True)
In this example, UserForm
defines validation rules for name
and age
, and create_user
validates data before creation.
Error handling in a CRUD application ensures predictable behavior and meaningful feedback. Key strategies include:
Example:
from flask import Flask, request, jsonify from sqlalchemy.exc import SQLAlchemyError app = Flask(__name__) @app.route('/create', methods=['POST']) def create_record(): data = request.get_json() try: if 'name' not in data or 'age' not in data: return jsonify({'error': 'Invalid input'}), 400 return jsonify({'message': 'Record created successfully'}), 201 except SQLAlchemyError as e: app.logger.error(f'Database error: {e}') return jsonify({'error': 'Database error'}), 500 except Exception as e: app.logger.error(f'Unexpected error: {e}') return jsonify({'error': 'An unexpected error occurred'}), 500 if __name__ == '__main__': app.run(debug=True)
To implement rate limiting in a CRUD API, use middleware to apply rate limiting logic. A common approach is the token bucket algorithm, allowing a set number of requests per time unit.
Example using Flask and Redis:
from flask import Flask, request, jsonify import time import redis app = Flask(__name__) r = redis.Redis() RATE_LIMIT = 5 # Number of requests TIME_WINDOW = 60 # Time window in seconds def is_rate_limited(ip): current_time = int(time.time()) key = f"rate_limit:{ip}:{current_time // TIME_WINDOW}" current_count = r.get(key) if current_count and int(current_count) >= RATE_LIMIT: return True r.incr(key, 1) r.expire(key, TIME_WINDOW) return False @app.route('/api/resource', methods=['GET', 'POST', 'PUT', 'DELETE']) def resource(): ip = request.remote_addr if is_rate_limited(ip): return jsonify({"error": "Rate limit exceeded"}), 429 return jsonify({"message": "Request successful"}) if __name__ == '__main__': app.run()