Build a Todo API
Step-by-step guide to creating a complete CRUD API for a todo list application with authentication.
What You'll Build
Features
- • Create, read, update, delete todos
- • Mark todos as complete/incomplete
- • Filter by status (all, active, completed)
- • User authentication with API tokens
- • Pagination for large lists
What You'll Learn
- • RESTful API design principles
- • HTTP methods and status codes
- • Request/response data structures
- • Authentication and authorization
- • Error handling patterns
Time Required
⏱️ Approximately 30-45 minutes
Step 1: Create Your Project
Navigate to Dashboard
Go to the main dashboard and click "New Project"
Name Your Project
Enter a descriptive name
Project Name: Todo APIAdd Description
Optional but recommended
Description: A RESTful API for managing todo itemsCreate Project
Click "Create" and note your project slug
✓ Your project slug will look like proj_abc123xyz. You'll use this in all your API URLs.
Step 2: Define the Todo Schema
Navigate to your project's Endpoints tab and create a new endpoint /todos. Use this schema:
{
"id": "{{datatype.uuid}}",
"userId": "{{datatype.number(1, 100)}}",
"title": "{{lorem.words(3)}}",
"description": "{{lorem.sentence}}",
"completed": "{{datatype.boolean}}",
"priority": "{{helpers.arrayElement(['low', 'medium', 'high'])}}",
"dueDate": "{{date.future}}",
"createdAt": "{{date.past}}",
"updatedAt": "{{date.recent}}"
}Schema Breakdown
| Field | Type | Description |
|---|---|---|
| id | UUID | Unique identifier for the todo |
| userId | Number | ID of user who owns the todo |
| title | String | Short todo title (3 words) |
| description | String | Detailed description |
| completed | Boolean | Completion status |
| priority | Enum | low, medium, or high |
| dueDate | Date | When the todo is due |
| createdAt | Date | When todo was created |
| updatedAt | Date | Last update timestamp |
💡 Tip: Set the record count to 20 for testing. You can increase it later.
Step 3: Configure CRUD Endpoints
Endpoint:
GET /api/proj_abc123/todosQuery Parameters
| Parameter | Type | Description |
|---|---|---|
| page | number | Page number (default: 1) |
| limit | number | Items per page (default: 10) |
| completed | boolean | Filter by completion status |
| priority | string | Filter by priority level |
Example Request
GET /api/proj_abc123/todos?page=1&limit=10&completed=false
Response (200 OK)
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"userId": 42,
"title": "Buy groceries",
"description": "Get milk, eggs, and bread from the store",
"completed": false,
"priority": "high",
"dueDate": "2024-02-15T10:00:00Z",
"createdAt": "2024-01-10T08:30:00Z",
"updatedAt": "2024-01-12T14:22:00Z"
}
],
"meta": {
"page": 1,
"limit": 10,
"total": 20,
"totalPages": 2
}
}Endpoint:
GET /api/proj_abc123/todos/:idExample Request
GET /api/proj_abc123/todos/550e8400-e29b-41d4-a716-446655440000
Response (200 OK)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"userId": 42,
"title": "Buy groceries",
"description": "Get milk, eggs, and bread from the store",
"completed": false,
"priority": "high",
"dueDate": "2024-02-15T10:00:00Z",
"createdAt": "2024-01-10T08:30:00Z",
"updatedAt": "2024-01-12T14:22:00Z"
}Response (404 Not Found)
{
"error": {
"code": "NOT_FOUND",
"message": "Todo not found"
}
}Endpoint:
POST /api/proj_abc123/todosRequest Body
{
"title": "Finish project documentation",
"description": "Complete API docs and user guide",
"priority": "high",
"dueDate": "2024-02-20T17:00:00Z"
}Response (201 Created)
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"userId": 42,
"title": "Finish project documentation",
"description": "Complete API docs and user guide",
"completed": false,
"priority": "high",
"dueDate": "2024-02-20T17:00:00Z",
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-01-15T10:00:00Z"
}Note: The id, userId, completed,createdAt, and updatedAt fields are automatically generated.
Endpoint:
PUT /api/proj_abc123/todos/:idRequest Body (All fields required)
{
"title": "Finish project documentation (Updated)",
"description": "Complete API docs, user guide, and examples",
"completed": true,
"priority": "medium",
"dueDate": "2024-02-22T17:00:00Z"
}Response (200 OK)
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"userId": 42,
"title": "Finish project documentation (Updated)",
"description": "Complete API docs, user guide, and examples",
"completed": true,
"priority": "medium",
"dueDate": "2024-02-22T17:00:00Z",
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-01-16T14:30:00Z"
}Endpoint:
PATCH /api/proj_abc123/todos/:idRequest Body (Only fields to update)
{
"completed": true
}Common Use Case: Toggle Completion
// Mark as complete
PATCH /api/proj_abc123/todos/660e8400-e29b-41d4-a716-446655440001
{ "completed": true }
// Mark as incomplete
PATCH /api/proj_abc123/todos/660e8400-e29b-41d4-a716-446655440001
{ "completed": false }Response (200 OK)
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"userId": 42,
"title": "Finish project documentation (Updated)",
"description": "Complete API docs, user guide, and examples",
"completed": true,
"priority": "medium",
"dueDate": "2024-02-22T17:00:00Z",
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-01-16T15:45:00Z"
}Endpoint:
DELETE /api/proj_abc123/todos/:idExample Request
DELETE /api/proj_abc123/todos/660e8400-e29b-41d4-a716-446655440001
Response (204 No Content)
// No response body // HTTP Status: 204 No Content
Response (404 Not Found)
{
"error": {
"code": "NOT_FOUND",
"message": "Todo not found"
}
}Step 4: Add Authentication
Generate API Token
Go to Profile → API Tokens → Create New Token
Token Name: Todo App DevelopmentEnable Authentication
In Project Settings → Security
- • Toggle "Require Authentication" ON
- • Select which endpoints require auth (all or specific)
- • Save changes
Include Token in Requests
// Add Authorization header
GET /api/proj_abc123/todos
Authorization: Bearer your_token_here
// cURL example
curl -H "Authorization: Bearer your_token_here" \
http://localhost:3000/api/proj_abc123/todos
// JavaScript fetch
fetch('/api/proj_abc123/todos', {
headers: {
'Authorization': 'Bearer your_token_here'
}
})⚠️ Security: Never commit API tokens to version control. Use environment variables in production.
Step 5: Test Your API
Open API Tester
Navigate to Project → API Tester tab
Test GET All Todos
Select: GET
/todosClick "Send" → Verify 200 OK response with todo list
Test POST Create Todo
Select: POST
/todosBody:
{
"title": "Test todo",
"priority": "high"
}Click "Send" → Verify 201 Created with new todo
Test PATCH Update Todo
Copy an ID from GET response, then PATCH with {"completed": true}
Check Logs
Go to Logs tab to see all your test requests with timestamps and response times
Step 6: Integrate with Frontend
import { useState, useEffect } from 'react';
import { CodeBlock } from '@/app/components/CodeBlock';
const API_URL = 'http://localhost:3000/api/proj_abc123';
const API_TOKEN = process.env.REACT_APP_API_TOKEN;
function TodoApp() {
const [todos, setTodos] = useState([]);
const [loading, setLoading] = useState(true);
// Fetch all todos
useEffect(() => {
fetchTodos();
}, []);
const fetchTodos = async () => {
try {
const response = await fetch(`${API_URL}/todos`, {
headers: {
'Authorization': `Bearer ${API_TOKEN}`
}
});
const result = await response.json();
setTodos(result.data);
} catch (error) {
console.error('Error fetching todos:', error);
} finally {
setLoading(false);
}
};
// Create new todo
const createTodo = async (title, priority = 'medium') => {
try {
const response = await fetch(`${API_URL}/todos`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ title, priority })
});
const newTodo = await response.json();
setTodos([...todos, newTodo]);
} catch (error) {
console.error('Error creating todo:', error);
}
};
// Toggle todo completion
const toggleTodo = async (id, completed) => {
try {
await fetch(`${API_URL}/todos/${id}`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ completed: !completed })
});
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !completed } : todo
));
} catch (error) {
console.error('Error updating todo:', error);
}
};
// Delete todo
const deleteTodo = async (id) => {
try {
await fetch(`${API_URL}/todos/${id}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${API_TOKEN}`
}
});
setTodos(todos.filter(todo => todo.id !== id));
} catch (error) {
console.error('Error deleting todo:', error);
}
};
if (loading) return <div>Loading...</div>;
return (
<div className="todo-app">
<h1>My Todos</h1>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id, todo.completed)}
/>
<span style={{
textDecoration: todo.completed ? 'line-through' : 'none'
}}>
{todo.title}
</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
<button onClick={() => createTodo('New Task', 'medium')}>
Add Todo
</button>
</div>
);
}
export default TodoApp;Next Steps & Enhancements
• Add categories/tags
• Implement search
• Add sorting options
• Create subtasks
• Add file attachments
• Add rate limiting
• Implement user roles
• Add input validation
• Enable CORS restrictions
• Add request logging
• Enable caching
• Add database indexing
• Implement lazy loading
• Use pagination
• Compress responses
• Generate OpenAPI spec
• Add API examples
• Create user guide
• Document error codes
• Add code samples
You've successfully built a complete Todo API with:
Continue learning with more tutorials or explore advanced features!