Skip to content

usiblogger/codeinter-nodejs-reborn

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Remix CRUD - Perfect Transition from CodeIgniter to Remix

Migrate from PHP/CodeIgniter to Node.js/Remix while keeping the familiar development experience and gaining modern performance and UX.


❌ PHP/CodeIgniter Disadvantages

1. Poor User Experience

  • Full page reload on every action
  • Slow loading with white screen time
  • Long wait time after form submission

2. Development Efficiency Limitations

  • Difficult frontend/backend separation
  • Repetitive CRUD code
  • Manual SQL writing for every table

3. Outdated Ecosystem

  • Lack of modern frontend components
  • Package management not as convenient as npm
  • No type checking

4. Deployment and Scaling Limitations

  • Dependent on Apache/PHP environment
  • Difficult to containerize
  • Complex horizontal scaling

❌ Remix Disadvantages

1. Steep Learning Curve

  • React Hooks, component lifecycle concepts
  • Complex TypeScript type system
  • Too much boilerplate code

2. Missing Model Layer

  • No Model like CodeIgniter
  • Need to write CRUD logic for every table
  • Lots of repetitive SQL code

3. Over-complicated Routing

  • Multiple files needed to handle one resource
  • _index.tsx, $id.tsx, edit.$id.tsx scattered
  • Not as clear as CodeIgniter's Controller

βœ… Our Solution

🎯 Core Philosophy: Take the best, leave the rest

We created a minimalist architecture that solves all disadvantages of both PHP and Remix:

1. BaseModel - Auto-generated SQL

Problem: Both PHP and Remix require manual repetitive CRUD SQL

Solution:

// ❌ Traditional way (both PHP and Remix)
db.prepare("INSERT INTO users (name, email) VALUES (?, ?)").run(name, email);
db.prepare("UPDATE users SET name = ?, email = ? WHERE id = ?").run(name, email, id);

// βœ… Our way: Auto-generated SQL
const userBase = new BaseModel('users');

userBase.create({ name, email });           // πŸ‘ˆ Auto-generates INSERT
userBase.update(id, { name, email });       // πŸ‘ˆ Auto-generates UPDATE
userBase.delete(id);                        // πŸ‘ˆ Auto-generates DELETE

Advantages:

  • βœ… Zero SQL code
  • βœ… Auto-generates correct SQL based on object
  • βœ… Supports any field combination

2. Optional Parameter Routing - One File Does It All

Problem: Remix needs multiple files to handle one resource

Solution:

// ❌ Traditional Remix (needs 2-3 files)
users._index.tsx       β†’ /users
users.$id.tsx         β†’ /users/:id
users.edit.$id.tsx    β†’ /users/edit/:id

// βœ… Our way: One file
users.($action).($id).tsx  β†’ /users, /users/edit/1

Code Example:

// app/routes/users.($action).($id).tsx
const userBase = new BaseModel('users');

export async function loader({ params }) {
  // GET /users/edit/1
  if (params.action === 'edit' && params.id) {
    return json({ user: userBase.getById(params.id) });
  }
  
  // GET /users
  return json({ users: userBase.getAll() });
}

export async function action({ request }) {
  const formData = await request.formData();
  
  if (intent === "create") {
    userBase.create({ 
      name: formData.get("name"),
      email: formData.get("email")
    });  // πŸ‘ˆ Auto-generates SQL!
  }
}

Advantages:

  • βœ… One file = Complete CRUD
  • βœ… As clear as CodeIgniter Controller
  • βœ… Reduces 80% file count

3. Flexible Type System - Your Choice

Problem: TypeScript too complex vs PHP has no type checking

Solution: We support both ways, you choose!

// app/models/User.model.ts

// Define interface (optional)
export interface User {
  id: number;
  name: string;
  email: string;
}

// βœ… Way 1: With types (TypeScript lovers)
export const userBaseTyped = new BaseModel<User>('users');

// βœ… Way 2: Fully any (PHP developers)
export const userBase = new BaseModel('users');

Usage Comparison:

// Way 1: Type hints (editor auto-complete)
const users: User[] = userBaseTyped.getAll();
users[0].name  // βœ… Editor suggests name, email properties
users[0].nmae  // ❌ Editor error: property doesn't exist

// Way 2: No types (like PHP, free and flexible)
const users = userBase.getAll();
users[0].name  // βœ… Works, but no auto-complete
users[0].anything  // βœ… No error, runtime only knows

Advantages:

  • βœ… Free choice: Want type safety? Use <User>. Want flexibility? Don't.
  • βœ… Same BaseModel, two ways to use
  • βœ… Team can mix and match

πŸš€ Final Solution Comparison

Feature CodeIgniter Traditional Remix Our Solution
CRUD SQL Manual Manual βœ… Auto-generated
File Count 2-3 3-5 βœ… 1
Type System None Complex βœ… Optional (use any)
User Experience Full page reload Good βœ… Good
Learning Curve Low High βœ… Low
Development Speed Fast Slow βœ… Fastest

πŸ’» Complete Code Example

BaseModel (Core)

// app/models/BaseModel.ts
export class BaseModel<T = any> {
  constructor(tableName: string) { }
  
  getAll(): T[]
  getById(id: any): T | undefined
  
  // πŸ‘‡ Auto-generates SQL based on object
  create(data: any): T | undefined {
    const keys = Object.keys(data);
    const values = Object.values(data);
    const sql = `INSERT INTO ${tableName} (${keys.join(', ')}) VALUES (${keys.map(() => '?').join(', ')})`;
    // Auto-generates: INSERT INTO users (name, email) VALUES (?, ?)
  }
  
  update(id: any, data: any): T | undefined {
    const keys = Object.keys(data);
    const values = Object.values(data);
    const sql = `UPDATE ${tableName} SET ${keys.map(k => `${k} = ?`).join(', ')} WHERE id = ?`;
    // Auto-generates: UPDATE users SET name = ?, email = ? WHERE id = ?
  }
  
  delete(id: any): boolean
}

Route File (One file does it all)

// app/routes/users.($action).($id).tsx
import { BaseModel } from "~/models/BaseModel";

const userBase = new BaseModel('users');

export async function loader({ params }) {
  if (params.action === 'edit' && params.id) {
    return json({ user: userBase.getById(params.id) });
  }
  return json({ users: userBase.getAll() });
}

export async function action({ request }) {
  const formData = await request.formData();
  const intent = formData.get("intent");
  
  if (intent === "create") {
    userBase.create({ 
      name: formData.get("name"), 
      email: formData.get("email") 
    });
  }
  
  if (intent === "update") {
    userBase.update(params.id, { 
      name: formData.get("name"), 
      email: formData.get("email") 
    });
  }
  
  if (intent === "delete") {
    userBase.delete(formData.get("id"));
  }
  
  return redirect("/users");
}

export default function Users() {
  const { users, user } = useLoaderData();
  
  if (user) {
    return <EditView user={user} />;
  }
  
  return <IndexView users={users} />;
}

🎯 Quick Start

1. Install Dependencies

npm install

2. Start Development

npm run dev

3. Visit Application

http://localhost:5173/users

4. Create New Table (3 Steps)

Step 1: Create Database Table

// app/config/database.ts
db.exec(`
  CREATE TABLE IF NOT EXISTS products (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    price REAL NOT NULL
  )
`);

Step 2: Create Route File

// app/routes/products.($action).($id).tsx
const productBase = new BaseModel('products');

export async function loader({ params }) {
  if (params.action === 'edit' && params.id) {
    return json({ product: productBase.getById(params.id) });
  }
  return json({ products: productBase.getAll() });
}

export async function action({ request }) {
  const formData = await request.formData();
  
  if (intent === "create") {
    productBase.create({
      name: formData.get("name"),
      price: formData.get("price")
    });  // πŸ‘ˆ Auto-generates: INSERT INTO products (name, price) VALUES (?, ?)
  }
}

Step 3: Done!

That simple!


πŸ“ Project Structure

app/
β”œβ”€β”€ config/
β”‚   └── database.ts              # Database configuration
β”œβ”€β”€ models/
β”‚   β”œβ”€β”€ BaseModel.ts             # Core: Auto-generates SQL
β”‚   └── User.model.ts            # Optional: Type definition demo
β”œβ”€β”€ routes/
β”‚   β”œβ”€β”€ _index.tsx               # Redirect to /users
β”‚   └── users.($action).($id).tsx # User CRUD (one file)
└── views/
    └── users/
        β”œβ”€β”€ IndexView.tsx        # List view
        └── EditView.tsx         # Edit view

Note: User.model.ts is optional, only for type system demo. You can:

  • Skip it: Directly new BaseModel('users') in routes (like PHP)
  • Use it: Import userBase or userBaseTyped (with type hints)

πŸŽ“ Key Innovations

1. Auto SQL Generation

No SQL writing needed, BaseModel auto-generates based on passed object

2. Optional Parameter Routing

users.($action).($id).tsx matches:

  • /users β†’ List
  • /users/edit/1 β†’ Edit

3. Flexible Type System

Two ways to use in routes:

// app/routes/users.($action).($id).tsx

// βœ… Way 1: Direct creation (no types, simplest)
const userBase = new BaseModel('users');

export async function loader({ params }) {
  const users = userBase.getAll();  // any[]
  return json({ users });
}

// βœ… Way 2: Import with types (has hints)
import { userBaseTyped, User } from "~/models/User.model";

export async function loader({ params }) {
  const users = userBaseTyped.getAll();  // User[]
  return json({ users });
}

Your Choice:

  • 🟒 Beginners/PHP developers β†’ Way 1 (direct creation, no types)
  • πŸ”΅ TypeScript lovers β†’ Way 2 (import, with types)
  • 🟣 Team projects β†’ Mix (each takes what they need)

πŸ”₯ Why Better Than CodeIgniter?

βœ… Keep the Advantages

  • Simple API
  • Fast development
  • Low learning curve

βœ… Solve the Disadvantages

  • No page reload (smooth page transitions)
  • Modern ecosystem (npm packages)
  • Containerized deployment (Docker)
  • Auto-generate SQL (zero repetitive code)

πŸ”₯ Why Better Than Traditional Remix?

βœ… Keep the Advantages

  • React components
  • SSR performance
  • Type safety (optional)

βœ… Solve the Disadvantages

  • One file does it all (no need for multiple route files)
  • Auto SQL (no manual CRUD)
  • Optional types (no complex TypeScript)

πŸ“ License

MIT


πŸ™ Acknowledgments

Thanks to CodeIgniter's simple design philosophy and Remix's modern architecture.

From PHP to Node.js, development experience not compromised, but better! πŸš€

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published