Claude Code से पहली REST API बनाएं: CRUD, validation और tests
Claude Code से पहली Express REST API बनाएं: CRUD, validation, error design, tests और चलने वाला code.
पहली REST API बनाते समय मुश्किल हिस्सा सिर्फ Express routes लिखना नहीं होता। असली चुनौती यह समझना है कि एक उपयोगी API में क्या होना चाहिए: साफ URL, सही HTTP methods, input validation, समझ आने वाले errors, और कुछ tests जो साबित करें कि code चलता है।
इस guide में हम Claude Code की मदद से Express पर एक छोटी Todo API बनाएंगे। हम project बनाएंगे, CRUD endpoints जोड़ेंगे, JSON input validate करेंगे, सही status codes लौटाएंगे, और curl तथा Node.js के built-in test runner से API को जांचेंगे। यह code Node.js 22/24 LTS या उससे नए version के लिए है।
अगर Claude Code आपके लिए नया है, तो पहले Claude Code getting started guide पढ़ें। API testing आगे सीखने के लिए Claude Code API testing और schema validation के लिए Claude Code Zod validation उपयोगी हैं।
जरूरी शब्द सरल भाषा में
REST API HTTP के जरिए resources को read और change करने का तरीका है। इस article में resource todo है। आसान भाषा में, कोई app एक URL call करता है, JSON भेजता या पाता है, और status code से समझता है कि result क्या हुआ।
| शब्द | आसान अर्थ | उदाहरण |
|---|---|---|
| Endpoint | API का URL entry point | GET /todos |
| Method | HTTP verb जो action बताता है | GET, POST, PUT, DELETE |
| Status code | result बताने वाली संख्या | 200, 201, 400, 404 |
| JSON | data भेजने का हल्का text format | { "title": "Learn REST" } |
| Validation | input सही है या नहीं, यह जांचना | खाली title रोकना |
| Idempotency | वही request दोहराने पर final state वही रहे | वही PUT दो बार भेजना |
Official references के लिए MDN के HTTP request methods, MDN के HTTP status codes, Express routing, Express error handling, और Node.js test runner देखें।
हम क्या बनाएंगे
API Todos को memory में रखेगी। Server restart होने पर data फिर initial state में लौट आएगा। यह जानबूझकर छोटा रखा गया है ताकि database जोड़ने से पहले REST shape समझ में आए।
| Action | Method और URL | Success status |
|---|---|---|
| Health check | GET /health | 200 OK |
| Todos list | GET /todos | 200 OK |
| एक Todo पढ़ना | GET /todos/:id | 200 OK |
| Todo बनाना | POST /todos | 201 Created |
| Todo replace करना | PUT /todos/:id | 200 OK |
| Todo delete करना | DELETE /todos/:id | 204 No Content |
यह छोटा example real work में भी काम आता है। पहला use case internal task API है, जैसे review requests, tickets या operations tasks। दूसरा use case frontend mock backend है, ताकि React, Vue या Astro UI backend तैयार होने से पहले test हो सके। तीसरा use case contact या newsletter API है, जहां title को email और message से बदला जा सकता है।
Claude Code को clear completion criteria दें
Stack, endpoints, error behavior और verification commands एक साथ लिखें। सिर्फ “API बना दो” कहने से code चल सकता है, लेकिन design review करना कठिन हो जाता है।
Create a beginner Todo REST API with Express 5 and Node.js ES Modules.
Requirements:
- Provide package.json and server.js
- Add GET /health, GET /todos, GET /todos/:id, POST /todos, PUT /todos/:id, DELETE /todos/:id
- Accept JSON input
- title must be 1 to 120 characters; completed must be boolean
- Return JSON errors for 400, 404, and 500
- Keep PUT idempotent: repeating the same request should not change the final resource state
- Include curl requests and a node:test example
Idempotency वाली line जरूरी है। PUT resource replacement दिखाता है। अगर वही body दो बार भेजी जाए, तो Todo की final state वही रहनी चाहिए। अगर हर identical request पर updatedAt बदलता है, तो code और REST explanation अलग हो जाते हैं।
Project बनाएं
mkdir claude-rest-api
cd claude-rest-api
npm init -y
npm install express
package.json को बदलें:
{
"name": "claude-rest-api",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "node --watch server.js",
"start": "node server.js",
"test": "node --test"
},
"dependencies": {
"express": "^5.0.0"
}
}
server.js
import express from "express";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
const PORT = Number(process.env.PORT ?? 3000);
function createHttpError(status, message, details) {
const error = new Error(message);
error.status = status;
error.details = details;
return error;
}
function hasOwn(object, key) {
return Object.prototype.hasOwnProperty.call(object, key);
}
function parseId(rawId) {
const id = Number(rawId);
return Number.isInteger(id) && id > 0 ? id : null;
}
function validateTodoInput(body, { partial = false } = {}) {
const errors = [];
if (!partial || hasOwn(body, "title")) {
if (typeof body.title !== "string" || body.title.trim().length === 0) {
errors.push({ field: "title", message: "title is required" });
} else if (body.title.trim().length > 120) {
errors.push({ field: "title", message: "title must be 120 characters or fewer" });
}
}
if (!partial || hasOwn(body, "completed")) {
if (typeof body.completed !== "boolean") {
errors.push({ field: "completed", message: "completed must be a boolean" });
}
}
return errors;
}
export function createApp() {
const app = express();
let nextId = 3;
const todos = [
{
id: 1,
title: "Read MDN HTTP status docs",
completed: false,
updatedAt: "2026-06-03T00:00:00.000Z"
},
{
id: 2,
title: "Ask Claude Code to review API errors",
completed: true,
updatedAt: "2026-06-03T00:00:00.000Z"
}
];
app.use(express.json({ limit: "32kb" }));
app.get("/health", (req, res) => {
res.json({ status: "ok", uptime: process.uptime() });
});
app.get("/todos", (req, res) => {
res.json({ data: todos, count: todos.length });
});
app.get("/todos/:id", (req, res, next) => {
const id = parseId(req.params.id);
if (!id) return next(createHttpError(400, "id must be a positive integer"));
const todo = todos.find((item) => item.id === id);
if (!todo) return next(createHttpError(404, "todo not found"));
res.json({ data: todo });
});
app.post("/todos", (req, res, next) => {
const errors = validateTodoInput(req.body);
if (errors.length > 0) {
return next(createHttpError(400, "invalid request body", errors));
}
const todo = {
id: nextId,
title: req.body.title.trim(),
completed: req.body.completed,
updatedAt: new Date().toISOString()
};
nextId += 1;
todos.push(todo);
res.status(201).location(`/todos/${todo.id}`).json({ data: todo });
});
app.put("/todos/:id", (req, res, next) => {
const id = parseId(req.params.id);
if (!id) return next(createHttpError(400, "id must be a positive integer"));
const errors = validateTodoInput(req.body);
if (errors.length > 0) {
return next(createHttpError(400, "invalid request body", errors));
}
const index = todos.findIndex((item) => item.id === id);
if (index === -1) return next(createHttpError(404, "todo not found"));
const previous = todos[index];
const nextTodo = {
...previous,
title: req.body.title.trim(),
completed: req.body.completed
};
const changed =
previous.title !== nextTodo.title || previous.completed !== nextTodo.completed;
todos[index] = {
...nextTodo,
updatedAt: changed ? new Date().toISOString() : previous.updatedAt
};
res.json({ data: todos[index] });
});
app.delete("/todos/:id", (req, res, next) => {
const id = parseId(req.params.id);
if (!id) return next(createHttpError(400, "id must be a positive integer"));
const index = todos.findIndex((item) => item.id === id);
if (index === -1) return next(createHttpError(404, "todo not found"));
todos.splice(index, 1);
res.status(204).send();
});
app.use((req, res, next) => {
next(createHttpError(404, `route not found: ${req.method} ${req.originalUrl}`));
});
app.use((err, req, res, next) => {
const status = Number.isInteger(err.status) && err.status >= 400 ? err.status : 500;
const body = {
error: {
status,
message: status === 500 ? "Internal Server Error" : err.message
}
};
if (err.details) {
body.error.details = err.details;
}
res.status(status).json(body);
});
return app;
}
const currentFile = fileURLToPath(import.meta.url);
const startedFile = process.argv[1] ? resolve(process.argv[1]) : "";
if (startedFile === currentFile) {
createApp().listen(PORT, () => {
console.log(`REST API listening on http://localhost:${PORT}`);
});
}
इस code में सिर्फ routes नहीं हैं। यह input validate करता है, JSON error shape को समान रखता है, और हर स्थिति में सही status code देता है।
curl से test करें
npm run dev
curl -i http://localhost:3000/health
curl -i http://localhost:3000/todos
curl -i -X POST http://localhost:3000/todos \
-H "Content-Type: application/json" \
-d '{"title":"Write API tests","completed":false}'
curl -i -X POST http://localhost:3000/todos \
-H "Content-Type: application/json" \
-d '{"title":"","completed":false}'
curl -i -X PUT http://localhost:3000/todos/1 \
-H "Content-Type: application/json" \
-d '{"title":"Read MDN HTTP status docs","completed":true}'
curl -i -X DELETE http://localhost:3000/todos/2
Success और failure दोनों देखें। जो API सिर्फ happy path पर चलती है, वह real frontend के लिए पर्याप्त नहीं है।
node:test जोड़ें
server.test.js बनाएं:
import assert from "node:assert/strict";
import test from "node:test";
import { createApp } from "./server.js";
function listen(app) {
return new Promise((resolve) => {
const server = app.listen(0, () => resolve(server));
});
}
async function request(baseUrl, path, options = {}) {
const response = await fetch(`${baseUrl}${path}`, {
headers: { "Content-Type": "application/json", ...options.headers },
...options
});
const text = await response.text();
return {
response,
body: text ? JSON.parse(text) : null
};
}
test("creates, updates, and deletes a todo", async (t) => {
const server = await listen(createApp());
t.after(() => server.close());
const baseUrl = `http://127.0.0.1:${server.address().port}`;
const created = await request(baseUrl, "/todos", {
method: "POST",
body: JSON.stringify({ title: "Test the API", completed: false })
});
assert.equal(created.response.status, 201);
assert.equal(created.body.data.title, "Test the API");
const id = created.body.data.id;
const updated = await request(baseUrl, `/todos/${id}`, {
method: "PUT",
body: JSON.stringify({ title: "Test the API", completed: true })
});
assert.equal(updated.response.status, 200);
assert.equal(updated.body.data.completed, true);
const deleted = await request(baseUrl, `/todos/${id}`, { method: "DELETE" });
assert.equal(deleted.response.status, 204);
});
test("rejects invalid todo input", async (t) => {
const server = await listen(createApp());
t.after(() => server.close());
const baseUrl = `http://127.0.0.1:${server.address().port}`;
const result = await request(baseUrl, "/todos", {
method: "POST",
body: JSON.stringify({ title: "", completed: false })
});
assert.equal(result.response.status, 400);
assert.equal(result.body.error.details[0].field, "title");
});
npm test
ये tests छोटे हैं, लेकिन safety net देते हैं। जब आप Claude Code से database, OpenAPI या authentication जोड़ने को कहेंगे, तो कहें कि ये tests pass रहने चाहिए।
आम गलतियां
Endpoints को /getTodos जैसा नाम न दें। Verb को method में रखें: GET /todos, POST /todos, PUT /todos/:id।
हर result के लिए 200 न लौटाएं। खराब input के लिए 400, missing resource के लिए 404, create success के लिए 201, और body के बिना delete success के लिए 204 उपयोग करें।
सिर्फ frontend validation पर भरोसा न करें। API direct call हो सकती है, इसलिए server validation जरूरी है।
Client को stack traces न दिखाएं। Details server logs में रखें, लेकिन response controlled JSON हो।
Idempotency न तोड़ें। अगर identical PUT हर बार timestamp या random value बदलता है, तो final state stable नहीं रहती।
अगले कदम और CTA
इसके बाद एक बार में एक चीज जोड़ें: database, मजबूत schema validation, API documentation, फिर authentication। Error design के लिए Claude Code error handling patterns भी देखें।
अपने project पर practice करने के लिए free cheat sheet खुली रखें। Reusable prompts, review checklists और CLAUDE.md templates के लिए Claude Code product library देखें। Team onboarding, API review rules और permission design के लिए Claude Code training and consulting उपयोग करें।
इस article की चीजें practice में आजमाने के बाद सबसे बड़ा beginner moment CRUD code नहीं था। सबसे मददगार था success और failure को साथ देखना: खाली title पर 400, unknown ID पर 404, और वही PUT दोहराने पर वही final state। इससे REST URLs की list नहीं, बल्कि frontend और backend के बीच contract जैसा महसूस होता है।
मुफ़्त PDF: Claude Code cheatsheet
Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.
हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.
लेखक के बारे में
Masa
Claude Code workflow और team adoption पर काम करने वाला engineer.
संबंधित लेख
Claude Code permission safety ladder: access धीरे-धीरे बढ़ाएं
read-only से limited edits, proof commands और deploy checks तक permission बढ़ाने की सुरक्षित ladder.
Claude Code Small PR Proof Pack: छोटे PR को review-ready बनाना
Claude Code PR के लिए diff, checks, public URL, CTA path और rollback वाला practical proof pack.
Claude Code Review Gate Before Commit: diff, test, public URL और CTA जांच
Claude Code से commit से पहले review gate बनाएं: diff, build, public URL, Gumroad, consultation, tests और unrelated files।