A modern, feature-rich todo application built with Ruby on Rails and GraphQL. This application allows users to manage todos with subtasks, filter by status, and interact with the data through both a traditional REST API and a GraphQL API.
- Create: Add new todos with titles
- Read: View all todos with filtering options (All, Pending, Completed)
- Update: Mark todos as completed or update their titles
- Delete: Remove unwanted todos
- Create nested subtasks for any todo item
- Subtasks inherit the same CRUD functionality as parent todos
- Parent-child relationship maintained through the database
- Full GraphQL implementation with Relay-style mutations
- Interactive GraphiQL explorer available at
/graphiql - Supported operations:
- Query todos and subtasks
- Create, update, and delete todos
- Create subtasks
- Ruby version: Ruby 3.2.3 with Rails 8.0.2
- Database: PostgreSQL
- Frontend: Tailwind CSS, Stimulus.js, Turbo
- API: REST and GraphQL (with Relay-style mutations)
- Testing: RSpec, Factory Bot
The application uses GraphQL with Relay-style mutations:
- Input types follow naming convention like
CreateTodoInput - Mutations require nested input structure:
{ input: { input: { actual data } } } - All mutations (createTodo, updateTodo, deleteTodo, createSubtask) use Relay-style format
- Base mutation class is
GraphQL::Schema::RelayClassicMutation
- Ruby 3.2.3
- PostgreSQL
- Node.js and Yarn
- Clone the repository
- Install dependencies:
bundle install - Setup the database:
rails db:create db:migrate db:seed - Start the server:
rails server - Visit
http://localhost:3000in your browser
Run the test suite with:
bundle exec rspec
This application is deployed on Heroku. The production version can be accessed at: https://dynamic-todos-993640c2aaf6.herokuapp.com
- Ensure your changes are committed to Git
- Deploy to Heroku:
git push heroku main - Run database migrations if needed:
heroku run rails db:migrate
Fetch all todos:
query {
todos {
id
title
completed
subtasks {
id
title
completed
}
}
}Create a new todo:
mutation {
createTodo(input: {
input: {
title: "New Todo"
completed: false
}
}) {
todo {
id
title
completed
}
errors
}
}Create a subtask:
mutation {
createSubtask(input: {
input: {
title: "New Subtask"
parentId: "1"
}
}) {
todo {
id
title
}
errors
}
}