Skip to main content

Tech Stack

Cluster is built with modern, well-supported technologies chosen for developer experience, performance, and long-term maintainability.

Frontend

TechnologyVersionPurpose
React18+UI framework
TypeScript5+Type safety
Vite5+Build tool and dev server
TanStack Query5+Server state management
Zustand4+Client state management
Tailwind CSS3+Utility-first styling
Radix UILatestAccessible component primitives
React Flow11+Canvas/node visualization
MSAL.js3+Azure AD authentication

Key Libraries

TanStack Query

Manages all server state—API calls, caching, and synchronization:

const { data: annotations } = useQuery({
queryKey: ['annotations', studyId],
queryFn: () => api.annotations.list({ studyId }),
});

Zustand

Minimal client state for UI concerns:

const useCanvasStore = create((set) => ({
selectedNodes: [],
setSelectedNodes: (nodes) => set({ selectedNodes: nodes }),
}));

React Flow

Powers the clustering/affinity mapping canvas:

<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
nodeTypes={nodeTypes}
/>

Backend

TechnologyVersionPurpose
Node.js20+ LTSRuntime
Express4+HTTP framework
TypeScript5+Type safety
Drizzle ORMLatestDatabase access
Zod3+Request validation
@azure/msal-nodeLatestAzure AD authentication
@microsoft/microsoft-graph-clientLatestSharePoint integration

Key Libraries

Drizzle ORM

Type-safe database access with excellent PostgreSQL support:

const annotations = await db
.select()
.from(annotationsTable)
.where(eq(annotationsTable.orgId, orgId))
.orderBy(desc(annotationsTable.createdAt));

Zod

Request validation with TypeScript inference:

const createAnnotationSchema = z.object({
motivation: z.array(annotationMotivationSchema),
targets: z.array(targetSchema),
bodyText: z.string().optional(),
});

type CreateAnnotationInput = z.infer<typeof createAnnotationSchema>;

Database

TechnologyVersionPurpose
PostgreSQL15+Primary database
Drizzle KitLatestMigrations

Why PostgreSQL?

  • JSONB support — Store W3C JSON-LD annotations efficiently
  • GIN indexes — Fast queries on JSONB fields
  • Array types — Native support for motivation arrays
  • Mature ecosystem — Excellent tooling and hosting options

Schema Highlights

-- Annotations with W3C JSON-LD storage
CREATE TABLE annotations (
id UUID PRIMARY KEY,
org_id UUID NOT NULL REFERENCES organizations(id),
motivation annotation_motivation[] NOT NULL,
jsonld JSONB NOT NULL, -- Complete W3C annotation
created_at TIMESTAMPTZ DEFAULT NOW()
);

-- GIN index for JSONB queries
CREATE INDEX idx_annotations_jsonld ON annotations USING GIN(jsonld);

External Services

Microsoft Graph API

Access SharePoint files using delegated permissions:

PermissionTypePurpose
User.ReadDelegatedRead user profile
Files.Read.AllDelegatedRead SharePoint files
Sites.Read.AllDelegatedList SharePoint sites

Azure Active Directory

OAuth 2.0 / OpenID Connect authentication:

  • Frontend: MSAL.js popup/redirect flow
  • Backend: Token validation and on-behalf-of flow

Development Tools

ToolPurpose
pnpmPackage manager (workspaces)
tsxTypeScript execution
Drizzle StudioDatabase GUI
ESLintCode linting
PrettierCode formatting

Optional Services

Meilisearch

Full-text search across annotations:

const results = await meilisearch.index('annotations').search(query, {
filter: `orgId = "${orgId}"`,
limit: 20,
});

Redis

Session storage for multi-instance deployments:

app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
}));

Build Outputs

Frontend

Static files served by any web server:

dist/
├── index.html
├── assets/
│ ├── index-[hash].js
│ └── index-[hash].css
└── img/

Backend

Node.js application:

dist/
├── index.js
├── routes/
├── services/
└── db/

Docker Images

Official images available on GitHub Container Registry:

# API server
docker pull ghcr.io/cluster-research-solutions/cluster-api:latest

# Web frontend
docker pull ghcr.io/cluster-research-solutions/cluster-web:latest

Next Steps