Building a Full-Stack CRUD App with React, TypeScript, and a Serverless API
Introduction
This article will guide you through building a basic CRUD (Create, Read, Update, Delete) application using a modern stack: React with TypeScript for the frontend and a serverless API (using, for example, AWS Lambda and API Gateway, or Netlify Functions) for the backend. This stack offers scalability, maintainability, and a great developer experience. We'll focus on the core concepts and provide examples to get you started.
Frontend (React with TypeScript)
Setting up the project
First, create a new React project with TypeScript:
npx create-react-app my-crud-app --template typescript
cd my-crud-app
Defining the Data Type
Let's define a simple Task
type:
// src/Task.ts
export interface Task {
id: string;
title: string;
description: string;
completed: boolean;
}
Creating the Task List Component
// src/components/TaskList.tsx
import React, { useState, useEffect } from 'react';
import { Task } from '../Task';
const TaskList: React.FC = () => {
const [tasks, setTasks] = useState<Task[]>([]);
useEffect(() => {
// Fetch tasks from the API on component mount
fetchTasks();
}, []);
const fetchTasks = async () => {
try {
const response = await fetch('/.netlify/functions/get-tasks'); // Assuming Netlify Functions
const data = await response.json();
setTasks(data);
} catch (error) {
console.error("Error fetching tasks:", error);
}
};
const handleComplete = async (id: string) => {
try {
await fetch(`/.netlify/functions/update-task/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ completed: !tasks.find(task => task.id === id)?.completed }),
});
fetchTasks();
} catch (error) {
console.error("Error updating task:", error);
}
}
return (
<ul>
{tasks.map((task) => (
<li key={task.id}>
{task.title} - {task.description} - Completed: {task.completed ? 'Yes' : 'No'}
<button onClick={() => handleComplete(task.id)}>Toggle Complete</button>
</li>
))}
</ul>
);
};
export default TaskList;
Backend (Serverless API - Example: Netlify Functions)
Setting up Netlify Functions
Create a netlify.toml
file in your project root:
[build]
functions = "netlify/functions"
Creating the Get Tasks Function
// netlify/functions/get-tasks.js
exports.handler = async (event, context) => {
// In a real application, you would fetch from a database
const tasks = [
{ id: "1", title: "Learn React", description: "Master React fundamentals", completed: false },
{ id: "2", title: "Build API", description: "Create a serverless API", completed: true },
];
return {
statusCode: 200,
body: JSON.stringify(tasks),
headers: {
"Content-Type": "application/json",
},
};
};
Creating the Update Task Function
// netlify/functions/update-task/[id].js
exports.handler = async (event, context) => {
const taskId = event.pathParameters.id;
const { completed } = JSON.parse(event.body);
// In a real application, you would update the task in a database
// For this example, we'll just log the ID and completion status
console.log(`Updating task with ID: ${taskId} to completed: ${completed}`);
return {
statusCode: 200,
body: JSON.stringify({ message: `Task ${taskId} updated to completed: ${completed}` }),
headers: {
"Content-Type": "application/json",
},
};
};
Conclusion
This is a simplified example, but it illustrates the core principles of building a full-stack CRUD application with React, TypeScript, and a serverless API. Remember to replace the mock data with a real database connection for a production-ready application.