I built a todo app using 9 different languages

John Rush - Jun 9 '23 - - Dev Community

Hi, I'm John, Multi Startup Builder.
I enjoy both coding and marketing.
See all my 20 products here
johnrush.me
my BIO
Say Hi to me On Twitter

Try my website builder

Greetings, fellow web developers!
Today we're going on an epic journey through not one, not two... but nine different web frameworks! 🤯

mindblown

But first... Why did I do this? Well as a founder and CEO of MarsX - lowcode platform (since age 6 🤯), who lived in many countries and has lofty dreams to transform the software development world; it was just another day at work!

That's right - I painstakingly built a simple todo list app in each of these languages and documented my experience for your reading pleasure.

Without further ado, let's meet our contenders:

  1. Next.js
  2. Ruby on Rails
  3. Python Django
  4. PHP Laravel
  5. Java Spring
  6. C# .NET
  7. Go Gin
  8. Swift Vapor
  9. Kotlin Ktor
  10. MarsX (bonus)

For each language/framework combo, we'll discuss setup time⏱️ ,routing🚦 ,database integration💾 ,as well as overall architecture. And ofc, you'll see the code...my spagetti code :D

1. Next.js

import { useState } from 'react'

export default function TodoList() {
  const [todos, setTodos] = useState([])
  const [inputValue, setInputValue] = useState('')

  const addTodo = () => {
    if (inputValue) {
      setTodos([...todos, inputValue])
      setInputValue('')
    }
  }

  return (
    <div>
      <h1>Todo List</h1>

      <ul>
        {todos.map((todo) => (
          <li key={todo}>{todo}</li>
        ))}
      </ul>

      <form onSubmit={(e) => e.preventDefault()}>
        <input
          type="text"
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
        />

        <button onClick={() => addTodo()}>Add</button>
      </form>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Next.js brings React to life with its easy setup process and great developer experience out-of-the-box!

Setup: Easy peasy lemon squeezy 🍋
Routing: Built-in dynamic routing.
Database integration: Use any database you like, as long as it's supported by Node.js.

2. Ruby on Rails

# todo.rb

class Todo < ApplicationRecord
  validates :title, presence: true
end

# todos_controller.rb

class TodosController < ApplicationController
  before_action :set_todo, only: [:show, :edit, :update, :destroy]

  def index
    @todos = Todo.all.order(created_at: :desc)
  end

  def create
    @todo = Todo.new(todo_params)

    if @todo.save
      redirect_to todos_path, notice: 'Todo was successfully created.'
    else 
      render 'new'
    end 
  end 

private 

def set_todo 
   @todo = Todo.find(params[:id])
end 

def todo_params 
   params.require(:todo).permit(:title) # add additional parameters as needed.  
end 

end

# index.html.erb

<h1>Todos</h1>

<ul>
<% @todos.each do |t| %>
<li><%= t.title %></li>
<% end %>
</ul>

<%= form_for(@todo) do |f| %>  
 <%= f.text_field :title %>   
 <%= f.submit "Add" %>              
<% end %>
Enter fullscreen mode Exit fullscreen mode

Ah, the classic Ruby on Rails. This framework has been around for ages and remains one of my favorites!

Setup: rails new and off we go! 🚀
Database integration: ActiveRecord has your back, making it easy to work with databases.
Routing: RESTful routing is built-in and simple to use.

3. Python Django

# models.py
from django.db import models

class TodoItem(models.Model):
    title = models.CharField(max_length=200)
    completed = models.BooleanField(default=False)

    def __str__(self):
        return self.title


# views.py
from django.shortcuts import render
from .models import TodoItem

def todo_list(request):
    todo_items = TodoItem.objects.all()
    context = {
        'todo_items': todo_items,
    }
    return render(request, 'todo/todo_list.html', context)


# urls.py (inside app folder)
from django.urls import path
from .views import todo_list

app_name = 'todo'
urlpatterns = [
  path('', todo_list, name='list'),
]


# templates/todo/todo_list.html
{% extends "base.html" %}

{% block content %}
  <h1>Todo List</h1>
  <ul>
  {% for item in todo_items %}
      <li>{{ item.title }} - {{ item.completed }}</li>
  {% endfor %}
  </ul>
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

Django - the web framework "for perfectionists with deadlines" - doesn't disappoint. Plus, who doesn't love Python?

Setup: django-admin startproject gets you up and running quickly.
Routing: URL configuration can be a bit more complex than other frameworks but is still manageable.
Database integration: ORM makes database interactions smooth sailing⛵️ .

4. PHP Laravel

// routes/web.php
Route::get('/', 'TodoController@index');
Route::post('/todos', 'TodoController@store');

// Todo model
class Todo extends Model {
  protected $fillable = ['title'];
}

// Todo controller
class TodoController extends Controller {
  public function index() {
    $todos = Todo::all();
    return view('todo.index', compact('todos'));
  }

  public function store(Request $request) {
    $this->validate($request, [
      'title' => 'required'
    ]);

    Todo::create(['title' => request('title')]);

    return redirect('/');
  }
}

// resources/views/todo/index.blade.php
<form method="POST" action="/todos">
    {{ csrf_field() }}

    <label>Title:</label>
    <input type="text" name="title">

    <button type="submit">Add</button>
</form>

<ul>
@foreach ($todos as $todo)
<li>{{ $todo->title }}</li>
@endforeach
</ul>
Enter fullscreen mode Exit fullscreen mode

Laravel brings elegance to PHP development by providing clean syntax and an enjoyable developer experience.

Setup: A breeze with Composer (composer create-project --prefer-dist laravel/laravel my_todo_app)
Routing: Simple web.php file for defining routes 🗺️ .
Database integration: Eloquent ORM keeps everything neat and tidy.

5. Java Spring

@RestController
@RequestMapping("/todos")
public class TodoController {

    private final TodoRepository todoRepository;

    public TodoController(TodoRepository todoRepository) {
        this.todoRepository = todoRepository;
    }

    @GetMapping("/")
    public List<Todo> getAllTodos() {
        return this.todoRepository.findAll();
    }

    @PostMapping("/")
    public ResponseEntity<Object> createTodo(@RequestBody Todo newTodo) {
        try {
            this.todoRepository.save(newTodo);
            return ResponseEntity.ok().build();
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
     }
}

@Entity
@Table(name = "todos")
public class Todo {

  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private Long id;

  private String title;

  private boolean completed;

}
Enter fullscreen mode Exit fullscreen mode

Java Spring brings the power of Java to web development with a robust framework and plenty of configuration options.

Setup: A bit more involved, but manageable.
Routing: Annotated controllers make routing a breeze.
Database integration: JPA provides solid database support.

6. C# .NET

using System;
using System.Collections.Generic;

class Program {
    static void Main(string[] args) {
        List<string> todos = new List<string>();

        Console.WriteLine("Todo list");

        while (true) {
            Console.Write("> ");
            string input = Console.ReadLine();

            if (input == "quit") {
                break;
            }

            if (input.StartsWith("+")) {
                string todo = input.Substring(1).Trim();
                todos.Add(todo);
                Console.WriteLine($"Added: {todo}");
            } else if (input.StartsWith("-")) {
                int indexToRemove = int.Parse(input.Substring(1).Trim()) - 1;

                if (indexToRemove >= 0 && indexToRemove < todos.Count) {                    
                    string removedTodo = todos[indexToRemove];
                    todos.RemoveAt(indexToRemove);
                    Console.WriteLine($"Removed: {removedTodo}");
                }
            } else if (input == "") {
                Console.WriteLine("Todos:");

                 for(int i=0; i<todos.Count; i++){
                     Console.WriteLine($"{i+1}: {todos[i]}");
                 }
             }else{
                 //invalid command entered
             }
         }
     }
}
Enter fullscreen mode Exit fullscreen mode

C# .NET offers top-notch performance and comes packed with features for building full-stack web apps.

Setup: Use Visual Studio or CLI tools - either way, it's smooth sailing⛵️ .
Routing: Attribute-based routing lets you define routes directly on your controller methods 🎯 .
Database integration: Entity Framework Core is powerful and well-integrated into the ecosystem.

7. Go Gin

package main

import (
    "github.com/gin-gonic/gin"
)

type Todo struct {
    ID        int    `json:"id"`
    Title     string `json:"title"`
    Completed bool   `json:"completed"`
}

var todoList []Todo

func main() {
    r := gin.Default()

    // GET all todos
    r.GET("/todos", func(c *gin.Context) {
        c.JSON(200, todoList)
    })

    // POST a new todo
    r.POST("/todos", func(c *gin.Context) {
        var newTodo Todo
        c.BindJSON(&newTodo)

        // Generate unique ID for the new todo item
        if len(todoList) == 0 {
            newTodo.ID = 1 
        } else { 
            lastID := todoList[len(todoList)-1].ID 
            newTodo.ID = lastID + 1 
        }

        // Append the newly created item to the list of todos.
        todoList = append(todoList, newTodo)

        c.JSON(201, gin.H{
            "message": "New ToDo added successfully",
            "todo": &newToDo,
         })
    })

    r.Run(":8080")
}
Enter fullscreen mode Exit fullscreen mode

Gin is an excellent choice for those who prefer the simplicity and speed of Go. It's lightweight yet fully featured!

Setup: Easy setup using go mod init🚀 .
Routing: Simple route definition in just a few lines of code!
Database integration: GORM makes working with databases enjoyable 💾.

8. Swift Vapor

import Vapor

final class Todo: Content {
    var id: Int?
    var title: String

    init(title: String) {
        self.title = title
    }
}

func routes(_ app: Application) throws {

    // create a new todo item
    app.post("todo") { req -> EventLoopFuture<Todo> in
        let todo = try req.content.decode(Todo.self)
        return todo.save(on: req.db).map { todo }
    }

    // get all todos from the database
    app.get("todos") { req -> EventLoopFuture<[Todo]> in
        return Todo.query(on: req.db).all()
    }

}
Enter fullscreen mode Exit fullscreen mode

Vapor brings the power of Swift to server-side development, and it's almost as delightful as a freshly-baked 🍏 pie!

Setup: A bit more involved due to Xcode setup time.
Routing: Route definition is straightforward and readable.
Database integration: Fluent ORM is powerful and expressive.

9. Kotlin Ktor

data class Todo(val id: Int, val task: String)

val todos = mutableListOf<Todo>()

fun main() {
    embeddedServer(Netty, port = 8080) {
        install(ContentNegotiation) {
            json()
        }

        routing {
            get("/todos") {
                call.respond(todos)
            }

            post("/todos") {
                val todo = call.receive<Todo>()
                todos.add(todo)
                call.respond(HttpStatusCode.Created)
            }

            delete("/todos/{id}") {
                val id = call.parameters["id"]?.toIntOrNull()

                if (id == null || !todos.removeIf { it.id == id }) 
                    call.respond(HttpStatusCode.NotFound)
                 else 
                    call.respond(HttpStatusCode.OK)    
           }
       }  
   }.start(wait = true)
}
Enter fullscreen mode Exit fullscreen mode

Ktor helps you build web apps with Kotlin, combining simplicity with the power of JetBrains' language innovations💡 .

Setup: Requires some initial configuration but manageable.
Routing: Uses DSL for defining routes - clean and elegant🎩 .
Database integration: Exposed library makes working with databases simple yet powerful⚙️ .

10. MarsX (Bonus) 🚀

Hold on to your hats, folks! We've got a brand new framework/language that's so ridiculously easy, even my grandma managed to build the todo list in just 7 minutes!

But here's the catch - it's still in private beta, and you'll have to battle through a waitlist like Black Friday shoppers trying to snag that sweet deal. Want in? Join the frenzy here.

Funny gif of people fighting for deals

<schema>
  <array name="todo">
    <object>
      <string name="title" />
    </object>
  </array>
</schema>
Enter fullscreen mode Exit fullscreen mode

And there you have it!

Nine different frameworks used to build the same app - may your startup grow into a deca unicorn🦄 , no matter which one you choose. Just remember to choose wisely; after all, your framework will dictate your life for the next nine years😉 .

choosewisely

Happy coding!

--

Hi, I'm John, Multi Startup Builder.
I enjoy both coding and marketing.
See all my 20 products here
johnrush.me
my BIO
Say Hi to me On Twitter
Try my website builder

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player