In this tutorial series, we build a coding agent that you can assign tasks on GitHub. In this part 1.1, we do the following:
Set up React + Vite frontend
Set up Python FastAPI backend
Design and implement database schema
Create basic API endpoints for agents
I mentioned the AI prompts used to implement tasks.
Set up React + Vite frontend
For this demo, I used React + Vite to create a frontend project. Run the below command to create a react-ts project:
npm create vite@latest frontend -- --template react-ts
Install the dependencies required:
npm install @tanstack/react-query @tanstack/react-query-devtools axios zustand
Set up Python FastAPI backend
- Create a folder named backed
mkdir backend
- cd into it
cd backend
- setup virtual env
python3 -m venv venv
source venv/bin/activate
- Install requirements
pip install fastapi uvicorn sqlalchemy psycopg2-binary pydantic websockets python-dotenv
Design and implement database schema
Let’s use Supabase for this tutorial. You will need to create an account on Supabase. Run the below script in your Supabase SQL Editor.
-- Users table
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
-- Agents table
CREATE TABLE agents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
provider VARCHAR(50) NOT NULL, -- claude, codex, etc.
instructions TEXT,
runtime_id UUID,
created_at TIMESTAMP DEFAULT NOW()
);
-- Issues table (GitHub issues synced)
CREATE TABLE issues (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
github_issue_id INTEGER,
title TEXT NOT NULL,
description TEXT,
status VARCHAR(50) DEFAULT 'backlog',
assignee_id UUID REFERENCES agents(id),
created_at TIMESTAMP DEFAULT NOW()
);
-- Agent tasks queue
CREATE TABLE agent_task_queue (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
issue_id UUID REFERENCES issues(id),
agent_id UUID REFERENCES agents(id),
status VARCHAR(50) DEFAULT 'queued', -- queued, dispatched, running, completed, failed
priority INTEGER DEFAULT 5,
created_at TIMESTAMP DEFAULT NOW()
);
-- Runtimes table
CREATE TABLE agent_runtimes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255),
provider VARCHAR(50),
status VARCHAR(50) DEFAULT 'offline',
last_heartbeat TIMESTAMP
);
This is what you see after a successful run.
are these tables enough to create an agent? may be. Our goal is to setup minimal configuration to create an agent and run the agent in the background, inspired by the Multica’s architecture. You can learn more about Multica’s architecture on Deepwiki..
Create basic API endpoints for agents
Let’s get started with backend files. To setup basic API endpoints, you need to create the following files:
- main.py
Create main.py in the backend folder and the following code:
from fastapi import FastAPI, WebSocket
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173"],
allow_methods=["*"],
allow_headers=["*"],
)
# Database models and routes
from routes import agents, issues, tasks, websocket
app.include_router(agents.router, prefix="/api/agents", tags=["agents"])
app.include_router(issues.router, prefix="/api/issues", tags=["issues"])
app.include_router(tasks.router, prefix="/api/tasks", tags=["tasks"])
- routes/agents.py
Create routes folder and add a file named agents.py in the routes folder.
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from database import get_db
router = APIRouter()
class AgentCreate(BaseModel):
name: str
provider: str # claude, codex, etc.
instructions: str = ""
@router.post("/")
async def create_agent(agent: AgentCreate):
# Create agent in database
# Return created agent
pass
@router.get("/")
async def list_agents():
# Return all agents
pass
@router.get("/{agent_id}")
async def get_agent(agent_id: str):
# Return specific agent
pass
- routes/issues.py
Create a file named issues.py and add the below code
class IssueCreate(BaseModel):
github_issue_id: int
title: str
description: str = ""
@router.post("/")
async def create_issue(issue: IssueCreate):
# Create issue in database
# Optionally sync with GitHub API
pass
@router.post("/{issue_id}/assign")
async def assign_issue(issue_id: str, agent_id: str):
# Assign issue to agent
# Enqueue task for agent
# This triggers the agent execution flow
pass
- routes/tasks.py
Create a file named tasks.py and add the following code
@router.get("/claim")
async def claim_task(runtime_id: str):
# Daemon calls this to claim next available task
# Use FOR UPDATE SKIP LOCKED for concurrent safety
pass
@router.post("/{task_id}/start")
async def start_task(task_id: str):
# Mark task as running
# Broadcast via WebSocket
pass
@router.post("/{task_id}/complete")
async def complete_task(task_id: str, result: dict):
# Mark task as completed
# Update issue status if needed
pass
coding-agent is open source and available at thinkthroo/coding-agent
About me:
Hey, my name is ramunarasinga. Email: ramunarasinga@gmail.com
Tired of AI slop?
I spent 3+ years studying OSS codebases and wrote 350+ articles on what makes them production-grade. I built
Codebase architecture skills, inspired by best OSS projects.
















