Skip to content

feat: Implement Backend for Hacker Teams #60

@NoelVarghese2006

Description

@NoelVarghese2006

Summary

Implement the backend logic, storage, and API endpoints to allow hackers to manage groups (teams). Hackers should be able to create, join, view, update, leave, and delete teams.

This implementation must support multi-user team membership, enforce a maximum team size of 4, and automatically delete teamswhen empty.

The feature must follow existing patterns used in applications and schedule.

What to build

1. DB Migrations

Create a new migration file (e.g., 0000XX_create_teams.up.sql and .down.sql) inside cmd/migrate/migrations/.

Up Migration

CREATE TABLE teams (
    id            UUID        PRIMARY KEY DEFAULT gen_random_uuid(),
    name          TEXT        NOT NULL,
    code          TEXT        NOT NULL UNIQUE, -- Used for joining teams
    created_at    TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at    TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Join table for team membership (many-to-many)
CREATE TABLE team_members (
    team_id  UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
    user_id   TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    PRIMARY KEY (team_id, user_id)
);

-- Optional indexes
CREATE INDEX idx_team_members_user_id ON team_members(user_id);
CREATE INDEX idx_team_members_team_id ON team_members(team_id);

Down: Drop the table.

DROP TABLE IF EXISTS team_members;
DROP TABLE IF EXISTS teams;

2. Store Layer

Create internal/store/teams.go.

  • Define the Team struct matching the database schema.
  • Implement the TeamsStore struct with the following methods:
    • Create(ctx context.Context, team *Team, userID string) error
    • GetByID(ctx context.Context, id string) (*Team, error)
    • GetByUserID(ctx context.Context, userID string) (*Team, error)
    • AddMember(ctx context.Context, teamID, userID string) error
    • RemoveMember(ctx context.Context, teamID, userID string) error
    • GetMembers(ctx context.Context, teamID, string) ([]User, error)
    • Update(ctx context.Context, team *Team) error
    • Delete(ctx context.Context, id string) error

Important Logic

  • When creating a team:

    • Generate a random 6-character code
    • Insert team
    • Automatically add creator to team_members
  • Enforce team size limit (4 users max):

    • Check member count before inserting into team_members
  • When removing a member:

    • Delete membership row
    • If team has 0 members → delete team
  • Members should only be able to add/remove themselves, not other users.

Update internal/store/storage.go:

  • Add the Teams interface definition.
  • Add Teams to the Storage struct.
  • Initialize it in NewStorage.

Update internal/store/mock_store.go:

  • Add Teams to NewMockStore
  • Add a MockTeamStore interface
  • Add the functions.

3. API Handlers

  • Create cmd/api/teams.go.
    • Implement the following handlers attached to the application struct:
      • createTeamHandler: Create a new team, auto-add the creator, and fail if the user is already associated with a team.
      • getTeamHandler: Fetch the team details for the currently authenticated user.
      • joinTeamHandler: Join a team via invite code; fails if the code is invalid, the team is at capacity, or the user is already in a team.
      • leaveTeamHandler: Remove the current user from their team and trigger a team deletion if they were the final member.
      • updateTeamHandler: Update the team's name, restricted strictly to active members of that team.
      • deleteTeamHandler: (Optional) Allow any member to manually dissolve the team, or rely on the leaveTeamHandler auto-delete logic.
    • Include Swagger Documentation

4. Wire Routes

  • Update [cmd/api/api.go]:
    • Inside the AuthRequiredMiddleware team (likely near /applications), add the route team:
r.Route("/teams", func(r chi.Router) {
    r.Post("/", app.createTeamHandler)
    r.Get("/me", app.getTeamHandler)

    r.Post("/join", app.joinTeamHandler)
    r.Delete("/leave", app.leaveTeamHandler)

    r.Put("/{teamID}", app.updateTeamHandler)
    r.Delete("/{teamID}", app.deleteTeamHandler) // optional
})

5. Testing

  • Create cmd/api/teams_test.go.

Team Creation
✅ Successfully create team
❌ Fail if user already in team

Joining
✅ Join with valid code
❌ Invalid code
❌ id != caller's id
❌ Team full (>=4 members)
❌ Already in a team

Leaving
✅ Leave team successfully
✅ Last member leaves → team deleted
❌ id != caller's id

Updating
✅ Member can update team
❌ Non-member cannot update

Authorization
❌ Access team without membership

Notes

Do NOT add a team_id column directly to users
Use team_members as the source of truth
Ensure consistency with existing store + handler patterns
Generate team code in backend (not user-provided)

This design is subject to change. If you have alternative ideas or would like to implement this feature differently, please discuss with Caleb and update this issue accordingly. — Noel

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions