A hands-on tutorial that takes you from nothing to a working REST API. No prior API experience required.
By the end of this tutorial, you'll have a working REST API with multiple endpoints. You'll understand HTTP methods (GET, POST, PUT, DELETE) and know how to test your API. You'll also see why teams often choose auto-generation over hand-coding for production APIs.
Prerequisites: Node.js installed (v18+), a code editor, and basic terminal familiarity.
An API (Application Programming Interface) is how software components communicate. REST APIs use HTTP—the same protocol your browser uses—to let applications request and send data.
| HTTP Method | Purpose | Example |
|---|---|---|
| GET | Retrieve data | Get list of users |
| POST | Create new data | Create a new user |
| PUT/PATCH | Update existing data | Update user's email |
| DELETE | Remove data | Delete a user |
Open your terminal and run:
mkdir my-first-api
cd my-first-api
npm init -y
npm install express
Create a file called server.js. Start with the setup and initial data:
const express = require('express');
const app = express();
const PORT = 3000;
// Middleware to parse JSON
app.use(express.json());
// In-memory data store (simulating a database)
let users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
Add the GET endpoints to retrieve users:
// GET /users - Retrieve all users
app.get('/users', (req, res) => {
res.json(users);
});
// GET /users/:id - Retrieve a single user
app.get('/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
});
Add POST for creating users and PUT for updates:
// POST /users - Create a new user
app.post('/users', (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: 'Name and email required' });
}
const newUser = { id: users.length + 1, name, email };
users.push(newUser);
res.status(201).json(newUser);
});
// PUT /users/:id - Update a user
app.put('/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
const { name, email } = req.body;
if (name) user.name = name;
if (email) user.email = email;
res.json(user);
});
Finally, add DELETE and start the server:
// DELETE /users/:id - Delete a user
app.delete('/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) {
return res.status(404).json({ error: 'User not found' });
}
users.splice(index, 1);
res.status(204).send();
});
// Start the server
app.listen(PORT, () => {
console.log(`API running at http://localhost:${PORT}`);
});
node server.js
You should see: API running at http://localhost:3000
Get all users:
curl http://localhost:3000/users
Create a new user:
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"name": "Charlie", "email": "charlie@example.com"}'
Update a user:
curl -X PUT http://localhost:3000/users/1 \
-H "Content-Type: application/json" \
-d '{"name": "Alice Smith"}'
Delete a user:
curl -X DELETE http://localhost:3000/users/2
Congratulations! You now have a working REST API with full CRUD operations:
GET /usersto list all,GET /users/:idfor one,POST /usersto create,PUT /users/:idto update, andDELETE /users/:idto remove.
What we built works, but it's not production-ready. Missing pieces:
Building all this from scratch for a production API takes weeks, not minutes.
What if you could skip all that boilerplate? DreamFactory connects to your existing database and auto-generates a complete REST API in minutes—not hours or days.
Here's a sample of what DreamFactory generates automatically for every table in your database:
# Auto-generated endpoints for any database table
GET /api/v2/db/_table/users # SELECT * FROM users
GET /api/v2/db/_table/users/1 # SELECT * FROM users WHERE id = 1
POST /api/v2/db/_table/users # INSERT INTO users
PATCH /api/v2/db/_table/users/1 # UPDATE users WHERE id = 1
DELETE /api/v2/db/_table/users/1 # DELETE FROM users WHERE id = 1
But that's just the beginning. DreamFactory generates far more than basic CRUD endpoints. You also get filtering, sorting, pagination, field selection, related record retrieval, batch operations, stored procedure calls, and schema introspection—all without writing a single line of code:
# Advanced querying out of the box
GET /api/v2/db/_table/users?filter=status=active
GET /api/v2/db/_table/users?order=created_at DESC&limit=25
GET /api/v2/db/_table/users?fields=id,name,email
GET /api/v2/db/_table/orders?related=customer_by_customer_id
# Schema and stored procedures
GET /api/v2/db/_schema # List all tables
GET /api/v2/db/_schema/users # Get table structure
POST /api/v2/db/_proc/calculate_totals # Call stored procedures
Connect to Any Database: MySQL, PostgreSQL, SQL Server, Oracle, MongoDB, SQLite, IBM DB2, and 15+ other databases. DreamFactory works with what you already have—no migration required.
Enterprise Security Built-In: JWT authentication, API key management, OAuth 2.0, SAML, and LDAP integration. Role-based access control lets you restrict access at the service, table, or even individual field level. Rate limiting protects against abuse.
Instant API Documentation: Every endpoint includes auto-generated OpenAPI (Swagger) documentation. Your team can explore and test the API immediately through an interactive interface.
Server-Side Scripting: Need custom business logic? Add pre- and post-processing scripts in PHP, Node.js, or Python. Validate input, transform responses, integrate with external services—all without abandoning the auto-generated foundation.
Deploy Anywhere: Self-host on your own infrastructure for complete data control, or use DreamFactory's cloud offering. Docker images make deployment straightforward.
Remember all those "missing pieces" from our hand-coded API? Here's how DreamFactory handles them:
| Missing Piece | Hand-Coded Solution | DreamFactory |
|---|---|---|
| Database persistence | Set up ORM, write queries | Connect once, done |
| Authentication | Implement JWT, sessions | Built-in, configurable |
| Input validation | Write validators for each field | Schema-driven validation |
| Rate limiting | Add middleware, configure | Per-user/app limits included |
| Documentation | Write and maintain manually | Auto-generated OpenAPI |
Interested in trying DreamFactory? Get started with our documentation and have your first API running in under 30 minutes.
| Scenario | Approach |
|---|---|
| Learning APIs | Build from scratch (like this tutorial) |
| Custom business logic | Code your own endpoints |
| Standard CRUD operations | Auto-generate with DreamFactory |
| Internal tools | Auto-generate |
| Rapid prototyping | Auto-generate |
Both have their place. Build from scratch when learning (like this tutorial), when you need highly custom business logic, or when the API doesn't map to database tables. Use auto-generation when you need standard CRUD operations, when speed matters, or when building internal tools. Many teams use auto-generated APIs for 80% of their needs and hand-code the remaining 20% that requires custom logic.
At minimum: use HTTPS everywhere, implement authentication (JWT tokens are popular), add authorization checks on every endpoint, validate all input, and rate limit requests. For production, consider an API gateway or platform like DreamFactory that handles these concerns out of the box. Never rely on client-side security alone.
Yes. DreamFactory connects to existing databases without requiring schema changes. It supports MySQL, PostgreSQL, SQL Server, Oracle, MongoDB, SQLite, IBM DB2, and 15+ other database systems. You point it at your database, and it generates APIs for your existing tables, views, and stored procedures. Your current applications continue working unchanged.
REST uses multiple endpoints (one per resource) with standard HTTP methods. GraphQL uses a single endpoint where clients specify exactly what data they want. REST is simpler to learn and cache; GraphQL reduces over-fetching and under-fetching. For most applications, REST is the better choice, which DreamFactory natively supports.
| Code | Meaning | When to Use |
|---|---|---|
| 200 | OK | Successful GET/PUT |
| 201 | Created | Successful POST |
| 204 | No Content | Successful DELETE |
| 400 | Bad Request | Invalid input |
| 401 | Unauthorized | No authentication |
| 404 | Not Found | Resource doesn't exist |
| 500 | Server Error | Something broke |
You built a working REST API in about 10 minutes. You understand HTTP methods, JSON responses, and basic CRUD operations. That foundational knowledge is valuable—it helps you debug issues, understand what's happening under the hood, and make informed decisions about your architecture.
But here's the practical reality: most production APIs need the same things—authentication, validation, rate limiting, documentation, database connectivity. Building these from scratch every time doesn't make you a better developer; it just makes you slower.
The best developers know when to code and when to leverage tools. For learning, prototypes, and genuinely custom business logic, build from scratch. For standard database APIs that need to ship quickly, let DreamFactory generate the boilerplate while you focus on what actually differentiates your product.
Ready to see how fast you can go? Try DreamFactory and turn your existing database into a secure, documented REST API in under 30 minutes.