A minimal to-do list app: Rails GraphQL API + React frontend. Split into api/ and ui/,
mirroring the ignitionapp layout at a much smaller scale.
| Part | Tech |
|---|---|
api/ |
Rails 8.1 (API-only), PostgreSQL, graphql-ruby 2.6, rack-cors |
ui/ |
React 19, TypeScript, Vite 8, Apollo Client 4 |
Ports: API on 3010, UI on 5173.
- Ruby 3.4.x
- Node 22+
- PostgreSQL running locally
cd api && bundle install && bin/rails db:setupcd ui && npm installTwo terminals:
cd api && bin/rails servercd ui && npm run devThen open http://localhost:5173.
Single endpoint: POST http://localhost:3010/graphql.
query todos: [Todo!]!
query todo(id: ID!): Todo
mutation createTodo(input: { title: String! })
mutation updateTodo(input: { id: ID!, title: String, completed: Boolean })
mutation deleteTodo(input: { id: ID! })
Mutations return an errors: [String!]! field carrying validation messages, rather than
raising top-level GraphQL errors.
Example:
curl -s -X POST http://localhost:3010/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{ todos { id title completed } }"}'There is no GraphiQL UI (the Rails app is API-only). Use curl, or point a GraphQL client such as Altair or Apollo Sandbox at the endpoint.
api/
app/graphql/
api_schema.rb # schema root
types/query_type.rb # todos, todo
types/mutation_type.rb # createTodo, updateTodo, deleteTodo
types/todo_type.rb
mutations/ # one file per mutation
app/models/todo.rb
config/initializers/cors.rb
ui/
src/apolloClient.ts # Apollo client, reads VITE_GRAPHQL_URL
src/index.css # colour tokens and shared classes (.field)
src/todos/ # one folder per feature
TodoList.tsx # the list + form
queries.ts # queries, mutations, Todo type
src/App.tsx
Frontend conventions: keep a feature's component and its queries together under
src/<feature>/, style text inputs with the shared .field class, and use the colour
tokens in src/index.css rather than literal colour values.
api:CORS_ORIGINS(defaulthttp://localhost:5173),PORT(default3010)ui:VITE_GRAPHQL_URLinui/.env(defaulthttp://localhost:3010/graphql)
No auth, no GraphQL codegen, no Docker, and no hosted CI — bin/ci runs setup and a gem
audit locally. The API has RSpec specs; the UI has no test runner yet. TypeScript types for
GraphQL operations are hand-written in ui/src/todos/queries.ts; swap in graphql-codegen
when the schema grows.