API Design | Nov 11, 2024 | 8 min read | By Rahul Khinchi | Reviewed by Pratim Maloji Bhosale
Rahul Khinchi is a Developer Advocate at Treblle, where he focuses on improving API workflows and developer experience. He writes technical content on API observability, security, and governance, and builds open-source tools to demonstrate real-world API use cases. Rahul has spoken at developer meetups and universities, and previously held developer advocacy roles at Syncloop. He was selected for Google Summer of Code in 2022 and has led community initiatives supporting thousands of beginners in open source.
APIs are the glue that holds modern apps together, letting different parts of your system communicate effortlessly.
If you’ve been working with RESTful APIs, you’ve probably heard about CRUD operations: Create, Read, Update, and Delete.
These are the basic actions you use to interact with data through an API, and they’re essential for building, maintaining, and scaling applications effectively.
This blog post will break down CRUD operations in the REST framework. We’ll go over the basics and explore how each operation connects to HTTP methods, along with some practical examples and tips to help you design a more organized and efficient API.
First, CRUD stands for Create, Read, Update, and Delete. It’s the basic set of functions to manipulate data and is foundational in software development.
Each function will connect to a specific HTTP method in the RESTful API world.
In RESTful APIs, each CRUD operation has a matching HTTP method that clarifies your intentions.
Here’s a quick breakdown:
For example, if you’re adding a new product to an e-commerce site, you’d hit the POST /api/v1/products
API endpoint with the product details in the request body. If you’re unfamiliar with what an API endpoint is or how it works, check out this explanation of API endpoints.
If you want to get a product’s details, you will make a GET /api/v1/products/{id}
call, where {id}
is the unique identifier for that product.
The difference is subtle: PUT replaces a resource entirely, while PATCH applies partial updates. So, if you’re changing just a product’s price, PATCH might be a better fit.
/api/v1/products/{id}
would remove the product with the specified ID from the system.Each of these methods has a clear purpose. Using the right one makes your API intuitive and easy to maintain.
Let’s walk through building and testing an API for managing products, which involves performing CRUD operations—Create, Read, Update, and Delete.
Managing products means setting up an API enabling users to add new products, retrieve product details, update product information, and delete products as needed.
This type of setup is useful for applications like e-commerce platforms, inventory systems, and other product-oriented applications.
We'll use a simple Node.js setup with Express for the API. Then, let’s test the operations using Treblle's API testing tool, ASPEN.
Here’s a basic setup for your API to manage product data.
mkdir product-api
cd product-api
npm init -y
npm install express body-parser
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = 3000;
app.use(bodyParser.json());
// Dummy data
let products = [
{ id: 1, name: 'Product 1', price: 29.99 },
{ id: 2, name: 'Product 2', price: 39.99 }
];
// Create a new product
app.post('/api/v1/products', (req, res) => {
const newProduct = req.body;
newProduct.id = products.length + 1; // simple id generation
products.push(newProduct);
res.status(201).json(newProduct);
});
// Fetch all products
app.get('/api/v1/products', (req, res) => {
res.json(products);
});
// Fetch a single product by its ID
app.get('/api/v1/products/:id', (req, res) => {
const product = products.find(p => p.id === parseInt(req.params.id));
if (!product) return res.status(404).send('Product not found');
res.json(product);
});
// Update a product's information
app.put('/api/v1/products/:id', (req, res) => {
const product = products.find(p => p.id === parseInt(req.params.id));
if (!product) return res.status(404).send('Product not found');
product.name = req.body.name;
product.price = req.body.price;
res.json(product);
});
// Delete a product by its ID
app.delete('/api/v1/products/:id', (req, res) => {
const index = products.findIndex(p => p.id === parseInt(req.params.id));
if (index === -1) return res.status(404).send('Product not found');
products.splice(index, 1);
res.status(204).send();
});
app.listen(port, () => {
console.log(`API is running at http://localhost:${port}`);
});
Run your server with:
node app.js
Your API is now up and running at http://localhost:3000
. Let's test it using ASPEN.
We’ll now use ASPEN to test each CRUD operation for managing products.
http://localhost:3000/api/v1/products
.{
"name": "New Product",
"price": 49.99
}
{
"id": 3,
"name": "New Product",
"price": 49.99
}
The above message indicates that the system has successfully created a new product.
http://localhost:3000/api/v1/products/3
.{
"id": 3,
"name": "New Product",
"price": 49.99
}
The above image shows the details of the product you just created.
http://localhost:3000/api/v1/products/3
.{
"name": "Updated Product",
"price": 55.99
}
{
"id": 3,
"name": "Updated Product",
"price": 55.99
}
This JSON data updates the existing product with ID 3.
http://localhost:3000/api/v1/products/3
.204 No Content status
, confirming that the system has successfully deleted the product.CRUD operations might sound straightforward, but there are a few things to keep in mind to make sure your API stays reliable:
Even if you’ve done front-end validation, there’s always a chance that insufficient data slips through. Use frameworks or libraries to validate requests and avoid errors.
For example:
201 Created
for successful POST requests
200 OK
for successful GET, PUT, and PATCH requests
204 No Content
for successful DELETE requests
404 Not Found
for missing resources
Error Handling: Always provide meaningful error responses.
A generic error message isn’t helpful for a developer trying to figure out what happened. Give enough info to point them in the right direction without exposing sensitive data.
Behind the scenes, each CRUD operation corresponds to a database action.
Here’s how it breaks down:
An ORM (Object-Relational Mapper) like Sequelize for Node.js or Eloquent for Laravel simplifies these mappings. ORMs automatically translate your CRUD calls into the proper SQL commands, saving you from writing raw SQL code and ensuring consistent database interactions.
To keep your API functional and easy to maintain, here are a few tips:
Stick to clear, predictable naming conventions. Avoid mixing singular and plural nouns or using inconsistent terms.
Only let authorized users perform specific CRUD actions, especially when working with sensitive data.
Rate limiting helps prevent abuse, while logging lets you keep track of operations for easier monitoring and debugging.
Even with the basics down, making mistakes with CRUD operations is easy.
Here are some common pitfalls to watch out for:
Use POST, PUT, or PATCH for those actions to avoid unintended side effects.
Return informative error messages and avoid generic error codes like 500 Internal Server Error
without context.
When building or working with an API, consider each CRUD operation's structure, consistency, and reliability. Adopting an API-First approach can enhance these aspects for a more robust design.
As you continue working with APIs, Aspen can be a great tool to make your life easier. It’s a free, native macOS app designed to test REST APIs. Aspen requires no login and simplifies the process with its user-friendly design.
You can run tests without the hassle, generate data models, create Open API specs, and even get integration code with minimal effort.
If you’re looking for a straightforward way to test and improve your APIs, Aspen is worth checking out.
API endpoints are the access points where clients interact with your API. They define how data is requested, updated, or deleted. In this guide, we’ll break down what endpoints are, how they work, and how to design and manage them effectively as your API grows.
When building apps that rely on external data, choosing how to receive updates is crucial. In this article, we compare API polling and webhooks, explain when to use each, and walk through practical Go examples to help you implement the right approach.
API headers are a fundamental part of how web services communicate, yet they’re often glossed over in documentation or misunderstood in practice. This guide breaks down what API headers are, how they work in both requests and responses, and why you should pay attention to them.