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:
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:
First person in the connection.
Second person in the connection.
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
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
| Type | Description | Example |
|---|
colleague | Same company/team | ”Both engineers at TechCorp” |
manager | Manager/report | ”John manages Jane at TechCorp” |
founder | Co-founders | ”Co-founded StartupXYZ in 2020” |
investor | Investor/investee | ”Jane invested $500k in John’s company” |
advisor | Advisor/advisee | ”Jane advises John on product strategy” |
partner | Business partners | ”Co-own consulting firm” |
competitor | Business competitors | ”Both CEOs in same market” |
Personal Relationships
| Type | Description | Example |
|---|
family | Family member | ”Siblings” |
friend | Personal friend | ”College roommates” |
spouse | Married 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