Building a CRUD API With Node.js
SkillVeris Team
Engineering Team

A CRUD API is a set of HTTP endpoints that let clients create, read, update, and delete records, mapping the four operations to POST, GET, PUT/PATCH, and DELETE.
In this guide, you'll learn:
- Express makes each operation a route: POST /items to create, GET /items to list, GET /items/:id to read one, PUT /items/:id to update, DELETE /items/:id to remove.
- Use express.json() so the server can read JSON request bodies into req.body.
- Return proper status codes: 201 for created, 200 for success, 404 for missing, and 400 for bad input.
- Validate incoming data before writing it, and never trust the client to send well-formed payloads.
1What Is a CRUD API?
A CRUD API is a web service that exposes the four fundamental data operations — Create, Read, Update, and Delete — over HTTP. Each operation maps to an HTTP method: POST creates, GET reads, PUT or PATCH updates, and DELETE removes. Together they cover almost everything an application does with data.
CRUD is the backbone of most backends. A todo app, a blog, an online store — under the hood they are all creating, reading, updating, and deleting records. Learning to build one CRUD API in Node.js gives you a template you will reuse for nearly every project.
2Setting Up the Project
Start with a fresh Node.js project and install Express. Enable the JSON body parser so the server can read request bodies, and create a simple in-memory array to hold data while you learn the shape of the routes.
- npm init -y
- npm install express
- const express = require('express')
- const app = express()
- app.use(express.json()) // parse JSON bodies
- let items = [] // in-memory store for now
3Create and Read Endpoints
The create endpoint accepts a POST, builds a new record, stores it, and returns it with a 201 Created status. The read endpoints handle both a list of all items and a single item looked up by its id.
- app.post('/items', (req, res) => {
- const item = { id: Date.now(), ...req.body }
- items.push(item)
- res.status(201).json(item)
- })
- app.get('/items', (req, res) => res.json(items))
- app.get('/items/:id', (req, res) => {
- const item = items.find(i => i.id == req.params.id)
- item ? res.json(item) : res.status(404).end()
- })
💡Return the Created Resource
Responding to a POST with the full created object, including its new id, lets the client update its UI without a second request.
4Update and Delete Endpoints
The update endpoint finds a record by id and merges the incoming fields, and the delete endpoint removes the matching record. Both return 404 when the id does not exist so the client knows the operation targeted nothing.
- app.put('/items/:id', (req, res) => {
- const item = items.find(i => i.id == req.params.id)
- if (!item) return res.status(404).end()
- Object.assign(item, req.body)
- res.json(item)
- })
- app.delete('/items/:id', (req, res) => {
- items = items.filter(i => i.id != req.params.id)
- res.status(204).end()
- })
PUT vs PATCH
PUT conventionally replaces the whole record, while PATCH applies a partial update. For learning purposes a merge with Object.assign works for both, but honouring the distinction makes your API clearer to consumers.
5Using the Right Status Codes
HTTP status codes are how your API tells clients what happened without them parsing the body. Using them correctly makes the API predictable and easy to consume from any language or tool.
- 200 OK — a successful read or update.
- 201 Created — a new resource was created by a POST.
- 204 No Content — success with nothing to return, common for DELETE.
- 400 Bad Request — the client sent invalid or missing data.
- 404 Not Found — the requested id does not exist.
6Validating Input
Never write client data to your store without checking it. At minimum, confirm required fields are present and of the right type, and reject bad payloads with a 400 and a helpful message. Libraries like zod or joi formalise this, but a manual guard is fine to start.
- app.post('/items', (req, res) => {
- if (!req.body.name) return res.status(400).json({ error: 'name required' })
- const item = { id: Date.now(), name: req.body.name }
- items.push(item)
- res.status(201).json(item)
- })
⚠️Trust Nothing From the Client
Assume every request could be malformed or malicious. Validate types, lengths, and required fields on the server, even if the frontend already checks them.
7From Memory to a Database
The in-memory array resets every time the server restarts, so a real API needs persistent storage. The key insight is that swapping to a database changes only the code inside each handler — the routes, methods, and status codes stay identical.
Replace items.push with an INSERT, items.find with a SELECT, and so on. Whether you reach for PostgreSQL with an ORM like Prisma, or MongoDB with Mongoose, the CRUD shape you have built remains the same contract for clients.
8Best Practices
A few habits make a CRUD API robust enough for real use.
- Use plural, noun-based resource names like /items rather than verbs in the URL.
- Validate and sanitise every incoming payload before it touches storage.
- Return consistent error shapes so clients can handle failures uniformly.
- Wrap database calls in try/catch and forward errors to an error-handling middleware.
- Add pagination to list endpoints before the dataset grows large.
9Key Takeaways
A CRUD API is a small, repeatable pattern once you see the mapping.
- The four operations map to POST, GET, PUT/PATCH, and DELETE.
- Enable express.json() so req.body carries the parsed payload.
- Return accurate status codes so clients understand each outcome.
- Validate input on the server before writing anything.
- Moving to a database changes the handler internals, not the route design.
10Frequently Asked Questions
Q: What does CRUD stand for? A: Create, Read, Update, and Delete — the four basic operations for persistent data. In a REST API they correspond to the POST, GET, PUT or PATCH, and DELETE HTTP methods respectively.
Q: Do I need Express to build a CRUD API in Node.js? A: No, you can use Node's built-in http module, but Express handles routing, body parsing, and middleware with far less boilerplate, which is why it is the most common choice for beginners.
Q: What is the difference between PUT and PATCH? A: PUT conventionally replaces an entire resource, so the client sends the full object, while PATCH applies a partial update with only the fields that change. Choose based on whether callers send complete or partial data.
Q: Where should I store the data? A: For learning, an in-memory array works, but it resets on restart. For anything real use a database such as PostgreSQL or MongoDB; the route structure stays the same and only the code inside each handler changes.
Related Reading
Get The Print Version
Download a PDF of this article for offline reading.
About the Publisher
SkillVeris Team
Engineering Team
Our engineering writers turn abstract code concepts into hands-on, project-driven learning experiences.
View all postsRelated Posts
Never miss an update
Get the latest tutorials and guides delivered to your inbox.
No spam. Unsubscribe anytime.