I built a todo app using 9 different languages
John Rush
Posted on June 9, 2023
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! 🤯
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:
- Next.js
- Ruby on Rails
- Python Django
- PHP Laravel
- Java Spring
- C# .NET
- Go Gin
- Swift Vapor
- Kotlin Ktor
- 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>
)
}
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 %>
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 %}
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>
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;
}
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
}
}
}
}
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")
}
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()
}
}
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)
}
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.
<schema>
<array name="todo">
<object>
<string name="title" />
</object>
</array>
</schema>
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😉 .
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
Posted on June 9, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.