Skip to main content

Overview

The connections module manages relationships between identified persons (e.g., colleagues, family, business partners). Import path:
import { api } from '@/convex/_generated/api';

Queries

listAll

Retrieve all connection records. Type signature:
listAll: () => Promise<Connection[]>
Usage:
import { useQuery } from 'convex/react';
import { api } from '@/convex/_generated/api';

function ConnectionsGraph() {
  const connections = useQuery(api.connections.listAll);
  
  return (
    <div>
      <h2>All Connections ({connections?.length})</h2>
      {connections?.map(conn => (
        <ConnectionEdge key={conn._id} connection={conn} />
      ))}
    </div>
  );
}
Returns: All connection records in the database.

getForPerson

Retrieve all connections involving a specific person. Type signature:
getForPerson: (args: {
  personId: Id<"persons">;
}) => Promise<Connection[]>
Parameters:
personId
Id<'persons'>
required
Person document ID.
Usage:
function PersonConnections({ personId }: { personId: Id<"persons"> }) {
  const connections = useQuery(api.connections.getForPerson, { personId });
  
  return (
    <div>
      <h3>Connections ({connections?.length})</h3>
      {connections?.map(conn => (
        <div key={conn._id}>
          <strong>{conn.relationshipType}</strong>: {conn.description}
        </div>
      ))}
    </div>
  );
}
Returns: All connections where the person appears as either personAId or personBId, indexed for fast lookup.

Mutations

create

Create a new connection between two persons. Type signature:
create: (args: {
  personAId: Id<"persons">;
  personBId: Id<"persons">;
  relationshipType: string;
  description: string;
}) => Promise<Id<"connections">>
Parameters:
personAId
Id<'persons'>
required
First person in the connection.
personBId
Id<'persons'>
required
Second person in the connection.
relationshipType
string
required
Type of relationship. Common values:
  • "colleague" - Work at same company
  • "manager" - Manager/report relationship
  • "founder" - Co-founders
  • "investor" - Investor/investee relationship
  • "family" - Family member
  • "friend" - Personal friend
  • "advisor" - Advisor/advisee
  • "competitor" - Business competitor
  • "partner" - Business partner
  • "other" - Other relationship
description
string
required
Human-readable description of the relationship (e.g., “Both work at TechCorp, Jane reports to John”).
Returns: Convex document ID of the created connection. Usage:
import { useMutation } from 'convex/react';
import { api } from '@/convex/_generated/api';

function LinkPersons({ personA, personB }: { 
  personA: Id<"persons">; 
  personB: Id<"persons">; 
}) {
  const createConnection = useMutation(api.connections.create);
  
  const handleLink = async () => {
    await createConnection({
      personAId: personA,
      personBId: personB,
      relationshipType: 'colleague',
      description: 'Both work at TechCorp as senior engineers'
    });
  };
  
  return <button onClick={handleLink}>Link as Colleagues</button>;
}

Schema

interface Connection {
  _id: Id<"connections">;
  _creationTime: number;
  personAId: Id<"persons">;      // Indexed by_person_a
  personBId: Id<"persons">;      // Indexed by_person_b
  relationshipType: string;      // e.g., "colleague", "family"
  description: string;           // Human-readable description
}
Indexes:
  • by_person_a - Fast lookup of connections where person is A
  • by_person_b - Fast lookup of connections where person is B

Real-Time Graph Updates

Connections automatically sync to all connected clients:
import { useQuery } from 'convex/react';
import { api } from '@/convex/_generated/api';

function ConnectionsGraph() {
  const connections = useQuery(api.connections.listAll);
  const persons = useQuery(api.persons.listAll);
  
  // Build graph from connections
  const graph = useMemo(() => {
    const nodes = persons?.map(p => ({ id: p._id, label: p.name })) || [];
    const edges = connections?.map(c => ({
      source: c.personAId,
      target: c.personBId,
      label: c.relationshipType
    })) || [];
    return { nodes, edges };
  }, [persons, connections]);
  
  return <ForceGraph data={graph} />;
}

Detecting Connections from Research

Automatically create connections when research agents discover relationships:
# backend/agents/linkedin_agent.py
class LinkedInAgent:
    async def extract_connections(self, person_id: str, profile_html: str):
        # Parse HTML for connections
        connections = self.parse_connections(profile_html)
        
        for conn in connections:
            # Check if connected person exists
            other_person_id = await self.db.find_person_by_name(conn["name"])
            if other_person_id:
                # Create connection record
                await self.convex.mutation(
                    "connections:create",
                    personAId=person_id,
                    personBId=other_person_id,
                    relationshipType="colleague",
                    description=f"Connected on LinkedIn, {conn['context']}"
                )

Querying Connection Networks

Find all 2nd-degree connections:
function SecondDegreeConnections({ personId }: { personId: Id<"persons"> }) {
  const allConnections = useQuery(api.connections.listAll);
  
  // Find 1st degree connections
  const firstDegree = allConnections?.filter(
    c => c.personAId === personId || c.personBId === personId
  ) || [];
  
  // Extract their IDs
  const firstDegreeIds = new Set(
    firstDegree.flatMap(c => [c.personAId, c.personBId])
  );
  firstDegreeIds.delete(personId); // Remove self
  
  // Find 2nd degree connections
  const secondDegree = allConnections?.filter(c => {
    const isConnected = firstDegreeIds.has(c.personAId) || firstDegreeIds.has(c.personBId);
    const notFirstDegree = c.personAId !== personId && c.personBId !== personId;
    return isConnected && notFirstDegree;
  }) || [];
  
  return (
    <div>
      <h3>2nd Degree Connections ({secondDegree.length})</h3>
      {secondDegree.map(conn => (
        <ConnectionCard key={conn._id} connection={conn} />
      ))}
    </div>
  );
}

Connection Types Reference

Professional Relationships

TypeDescriptionExample
colleagueSame company/team”Both engineers at TechCorp”
managerManager/report”John manages Jane at TechCorp”
founderCo-founders”Co-founded StartupXYZ in 2020”
investorInvestor/investee”Jane invested $500k in John’s company”
advisorAdvisor/advisee”Jane advises John on product strategy”
partnerBusiness partners”Co-own consulting firm”
competitorBusiness competitors”Both CEOs in same market”

Personal Relationships

TypeDescriptionExample
familyFamily member”Siblings”
friendPersonal friend”College roommates”
spouseMarried couple”Married since 2015”

Bidirectional Connections

Connections are undirected by default. For directed relationships (e.g., manager → report), use descriptive text:
// Manager → Report (directed)
await createConnection({
  personAId: managerId,
  personBId: reportId,
  relationshipType: 'manager',
  description: 'John (A) manages Jane (B) at TechCorp'
});

// Colleague (undirected)
await createConnection({
  personAId: person1,
  personBId: person2,
  relationshipType: 'colleague',
  description: 'Both work at TechCorp as engineers'
});

Visualization Example (React Flow)

import ReactFlow, { Node, Edge } from 'reactflow';
import { useQuery } from 'convex/react';
import { api } from '@/convex/_generated/api';

function ConnectionsGraphView() {
  const persons = useQuery(api.persons.listAll);
  const connections = useQuery(api.connections.listAll);
  
  const nodes: Node[] = persons?.map(p => ({
    id: p._id,
    data: { label: p.name },
    position: p.boardPosition
  })) || [];
  
  const edges: Edge[] = connections?.map((c, i) => ({
    id: c._id,
    source: c.personAId,
    target: c.personBId,
    label: c.relationshipType,
    type: 'smoothstep'
  })) || [];
  
  return <ReactFlow nodes={nodes} edges={edges} />;
}

Best Practices

Use getForPerson for person-centric views (leverages indexes)
Use descriptive relationshipType values for filtering
Include context in description (where, when, how)
Avoid duplicate connections (check before creating)
Don’t create self-connections (personAId === personBId)
Don’t store sensitive relationship details in descriptions

Future Enhancements

  • Strength scoring - Add strength: number field (0.0-1.0)
  • Timestamps - Track when relationship was discovered
  • Sources - Link to intel fragments that revealed the connection
  • Verification - Add verified: boolean for manual review
  • Bidirectional inference - Auto-create reciprocal connections

Build docs developers (and LLMs) love