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
Top comments (14)
You could also add VanJS to the party, which is pretty cool. The ToDo-app in VanJS needs only 17 lines of code:
You can try it on jsfiddle
I will make a new article with all the recommendations from the comments.
VanJS looks cool
Why not share the repositories on Github?
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
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!
Really cool idea, I've been thinking about something similar for quite some time but never really got to do it
same languages? or you think there are more I should have covered?
Not as many languages, I was thinking about four or five. One that I thought about and is not in the list is elixir
hah, funny part, I had it, but It felt like too many, so I decided to not include it :D
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.
nextjs looks really good, im building devhunt.org on it
credits to fireship for all my inspirations
so in marsx you will just import the todolist microapp and use it?
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.