DEV Community

John Rush
John Rush

Posted on Edited on

I built a todo app using 9 different languages

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

Top comments (14)

Collapse
 
efpage profile image
Eckehard • • Edited

You could also add VanJS to the party, which is pretty cool. The ToDo-app in VanJS needs only 17 lines of code:

const TodoItem = ({text}) => {
  const done = van.state(false), deleted = van.state(false)
  return van.bind(deleted,
    d => d ? null : div(
      input({type: "checkbox", checked: done, onclick: e => done.val = e.target.checked}),
      van.bind(done, done => done ? strike(text) : span(text)),
      a({onclick: () => deleted.val = true}, "❌"),
    )
  )
}

const TodoList = () => {
  const inputDom = input({type: "text"})
  const dom = div(inputDom,
    button({onclick: () => van.add(dom, TodoItem({text: inputDom.value}))}, "Add"),
  )
  return dom
}
Enter fullscreen mode Exit fullscreen mode

You can try it on jsfiddle

Collapse
 
johnrushx profile image
John Rush •

I will make a new article with all the recommendations from the comments.
VanJS looks cool

Collapse
 
egomesbrandao profile image
Emmanuel G. Brandão •

Why not share the repositories on Github?

Collapse
 
johnrushx profile image
John Rush •

good point, will do it for next articles, I haven't really used github for small code snippets, I make them mostly in replit, marsx or other online ides

Collapse
 
egomesbrandao profile image
Emmanuel G. Brandão •

I'm a DevOps guy, and I'm interested in build a simple app to use in my demos of build a pipeline or a cloud infrastructure... this examples, maybe, could be one!

Collapse
 
fen1499 profile image
Fen •

Really cool idea, I've been thinking about something similar for quite some time but never really got to do it

Collapse
 
johnrushx profile image
John Rush •

same languages? or you think there are more I should have covered?

Collapse
 
fen1499 profile image
Fen •

Not as many languages, I was thinking about four or five. One that I thought about and is not in the list is elixir

Thread Thread
 
johnrushx profile image
John Rush •

hah, funny part, I had it, but It felt like too many, so I decided to not include it :D

defmodule Todo do
  defstruct [:id, :title, :completed]

  def create(attrs \\ %{}) do
    %Todo{
      id: attrs[:id] || generate_id(),
      title: attrs[:title],
      completed: false
    }
  end

  defp generate_id(), do: :crypto.strong_rand_bytes(8)

end

todos = [
   Todo.create(title: "Task1"),
   Todo.create(title: "Task2"),
   Todo.create(title: "Task3")
]

Enum.each(todos, fn todo ->
  IO.puts "#{todo.id} - #{todo.title}"
end)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
alexanderisora profile image
Alexander Isora 🦄 •

so in marsx you will just import the todolist microapp and use it?

Collapse
 
johnrushx profile image
John Rush •

in the example here, I've actually built it from scratch, and itwas like 9 lines.
If you import, then it's 1 line only.

Collapse
 
bretbernhoft profile image
Bret Bernhoft •

Would you recommend Next.js over React.js as a building tool? I've been hearing good things about Next.js and would love to give it a spin soon. Thank you for the information and programming demos.

Collapse
 
johnrushx profile image
John Rush •

nextjs looks really good, im building devhunt.org on it

Collapse
 
johnrushx profile image
John Rush •

credits to fireship for all my inspirations