Home  Reactjs   Simple todo ...

Simple todo app in react using prisma and express as backend

Backend

  1. Set up Prisma in your project
  2. Create a schema and generate Prisma Client
  3. Update your Express server to use Prisma for database operations

Here's a step-by-step guide to do this:

1. Set Up Prisma in Your Project

First, install Prisma CLI and initialize Prisma in your project:

npm install prisma --save-dev
npx prisma init

This will create a prisma folder with a schema.prisma file and a .env file in your project.

2. Create a Schema and Generate Prisma Client

Open the prisma/schema.prisma file and define your database schema. For example:

// prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

model Task {
  id        Int      @id @default(autoincrement())
  task      String
  completed Boolean  @default(false)
}

model User {
  id    Int    @id @default(autoincrement())
  name  String
  email String
}

After defining your schema, generate the Prisma Client:

npx prisma migrate dev --name init
npx prisma generate

This will create a prisma/dev.db SQLite database and generate the Prisma Client.

3. Setup Express Server to Use Prisma

Now, Create server.js to use Prisma for database operations:

const express = require('express');
const cors = require('cors');
const { PrismaClient } = require('@prisma/client');
const app = express();
const port = 5000;

// Middleware
app.use(cors());
app.use(express.json());

const prisma = new PrismaClient();

// Generic function to get all rows from a table
const getAll = async (model, res) => {
  try {
    const data = await prisma[model].findMany();
    res.json(data);
  } catch (err) {
    res.status(500).json({ error: 'Database error' });
  }
};

// Generic function to add a row to a table
const addRow = async (model, data, res) => {
  try {
    const newRow = await prisma[model].create({ data });
    res.json(newRow);
  } catch (err) {
    res.status(500).json({ error: 'Database error' });
  }
};

// Generic function to update a row in a table
const updateRow = async (model, id, data, res) => {
  try {
    const updatedRow = await prisma[model].update({
      where: { id: parseInt(id, 10) },
      data,
    });
    res.json(updatedRow);
  } catch (err) {
    res.status(500).json({ error: 'Database error' });
  }
};

// Generic function to delete a row from a table
const deleteRow = async (model, id, res) => {
  try {
    const deletedRow = await prisma[model].delete({
      where: { id: parseInt(id, 10) },
    });
    res.json(deletedRow);
  } catch (err) {
    res.status(500).json({ error: 'Database error' });
  }
};

// Routes for tasks
app.get('/tasks', (req, res) => getAll('task', res));
app.post('/tasks', (req, res) => addRow('task', { task: req.body.task }, res));
app.put('/tasks/:id', (req, res) => updateRow('task', req.params.id, { task: req.body.task, completed: req.body.completed }, res));
app.delete('/tasks/:id', (req, res) => deleteRow('task', req.params.id, res));

// Routes for users
app.get('/users', (req, res) => getAll('user', res));
app.post('/users', (req, res) => addRow('user', { name: req.body.name, email: req.body.email }, res));
app.put('/users/:id', (req, res) => updateRow('user', req.params.id, { name: req.body.name, email: req.body.email }, res));
app.delete('/users/:id', (req, res) => deleteRow('user', req.params.id, res));

app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});

Frontend

Add API Service

Update api.js to include CRUD functions:

import axios from 'axios';

const API_URL = 'http://localhost:5000';

// Generic function to get all rows from a table
export const getAll = (tableName) => axios.get(`${API_URL}/${tableName}`);

// Generic function to add a row to a table
export const addRow = (tableName, row) => axios.post(`${API_URL}/${tableName}`, row);

// Generic function to update a row in a table
export const updateRow = (tableName, id, row) => axios.put(`${API_URL}/${tableName}/${id}`, row);

// Generic function to delete a row from a table
export const deleteRow = (tableName, id) => axios.delete(`${API_URL}/${tableName}/${id}`);

Add React Components to Use above Functions

add App.js to manage both tasks and users using the generic API functions:

import React, { useState, useEffect } from 'react';
import { getAll, addRow, updateRow, deleteRow } from './api';

function App() {
  const [tasks, setTasks] = useState([]);
  const [users, setUsers] = useState([]);
  const [newTask, setNewTask] = useState('');
  const [newUser, setNewUser] = useState({ name: '', email: '' });

  useEffect(() => {
    fetchData('tasks', setTasks);
    fetchData('users', setUsers);
  }, []);

  const fetchData = async (tableName, setState) => {
    const response = await getAll(tableName);
    setState(response.data);
  };

  const handleAddTask = async () => {
    if (!newTask.trim()) return;
    await addRow('tasks', { task: newTask });
    setNewTask('');
    fetchData('tasks', setTasks);
  };

  const handleAddUser = async () => {
    if (!newUser.name.trim() || !newUser.email.trim()) return;
    await addRow('users', newUser);
    setNewUser({ name: '', email: '' });
    fetchData('users', setUsers);
  };

  const handleToggleComplete = async (id, completed) => {
    const taskToUpdate = tasks.find(task => task.id === id);
    await updateRow('tasks', id, { task: taskToUpdate.task, completed: !completed });
    fetchData('tasks', setTasks);
  };

  const handleDeleteTask = async (id) => {
    await deleteRow('tasks', id);
    fetchData('tasks', setTasks);
  };

  const handleDeleteUser = async (id) => {
    await deleteRow('users', id);
    fetchData('users', setUsers);
  };

  return (
    <div className="App">
      <h1>To-Do List</h1>
      <input 
        type="text" 
        value={newTask} 
        onChange={(e) => setNewTask(e.target.value)} 
        placeholder="Add a new task" 
      />
      <button onClick={handleAddTask}>Add Task</button>
      <ul>
        {tasks.map(task => (
          <li key={task.id}>
            <span style={{ textDecoration: task.completed ? 'line-through' : 'none' }}>
              {task.task}
            </span>
            <button onClick={() => handleToggleComplete(task.id, task.completed)}>
              {task.completed ? 'Undo' : 'Complete'}
            </button>
            <button onClick={() => handleDeleteTask(task.id)}>Delete</button>
          </li>
        ))}
      </ul>

      <h1>User List</h1>
      <input 
        type="text" 
        value={newUser.name} 
        onChange={(e) => setNewUser({ ...newUser, name: e.target.value })} 
        placeholder="Name" 
      />
      <input 
        type="email" 
        value={newUser.email} 
        onChange={(e) => setNewUser({ ...newUser, email: e.target.value })} 
        placeholder="Email" 
      />
      <button onClick={handleAddUser}>Add User</button>
      <ul>
        {users.map(user => (
          <li key={user.id}>
            <span>{user.name} ({user.email})</span>
            <button onClick={() => handleDeleteUser(user.id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;
Published on: Aug 01, 2024, 01:05 AM  
 

Comments

Add your comment