A dedicated space for Girls' Love stories and the people who love them.
GLify is a community-first discovery platform for Girls' Love (GL) and Yuri media: manga, manhwa, light novels, live-action series, anime, and webtoons. It helps fans find their next favorite story, connect with like-minded readers, share opinions, and take part in welcoming, series-specific communities.
- Discovery — Browse GL works across manga, manhwa, webtoons, light novels, live-action series, and anime
- Search & Filter — Search by title, creator, genre, or tags; filter by format and genre
- Personalized recommendations — Rank unseen and non-favorited works with 35% tag fit and 65% activity from similar members; favorites count twice as much as watched/read activity
- Personal library — Save favorites and mark works watched/read while signed in; activity is stored per account
- Communities — Create or join spaces for specific works or general GL topics (coming soon)
- Responsive — Works on desktop and mobile browsers
Personalized recommendations combine two explainable signals:
- 35% tag fit: the proportion of a member's weighted tag interests present on a candidate work. Favorite works contribute weight 2; watched/read works contribute weight 1.
- 65% similar-member activity: similar members' activity on each candidate, weighted by their match with the current member. A favorite contributes weight 2 and watched/read activity contributes weight 1.
The final score is 0.35 × tag fit + 0.65 × similar-member activity score.
Member similarity uses weighted Jaccard overlap across favorites (2) and
watched/read works (1). Already-favorited and watched/read titles are excluded
from suggestions. When there are no similar members with matching activity,
the interface clearly falls back to tag-fit-only ranking. Users can separately
opt in to let their favorites and watched/read list inform recommendations for
other members; opting out does not disable their own recommendations.
Communities are spaces for members to discuss and connect. There are two types:
- Work communities — Each work has a community that groups all its formats. For example, a community for "Bloom Into You" includes its anime, manga, and light novel in one space. When you click on a work, you can see its community.
- General communities — Not tied to a specific work. These are for broader GL topics, genres, or interests.
In the communities section, you can filter to show only general communities, only work-focused communities, or search for a specific work's community. Members can create new communities.
Communities are not yet implemented. Before opening them publicly, GLify needs community membership and moderation, reporting and spoiler controls, plus secure media storage and upload rules.
Members can create and join general or work-focused communities, chat with fellow fans, publish one opinion per work, and create, like, or favorite ships. Public profiles show eligible favorites, watched/read activity, posted opinions, and favorite ships.
User submissions are screened server-side before publication. OpenAI moderation checks safety for community information, chat messages, ships, ship images, and opinions. A separate relevance review checks that communities fit GLify's yuri/sapphic focus, ship details and images describe the same pairing, and opinions relate to the work being reviewed. Configure OPENAI_API_KEY only in the API service environment.
| Area | Choice | Responsibility |
|---|---|---|
| Web app | React, TypeScript, Vite, CSS | Responsive browser UI and discovery experience |
| Web hosting | Vercel | Global static hosting, preview deployments, and custom domain |
| API | Node.js, TypeScript, Fastify | Application API, moderation, and business logic |
| Content review | OpenAI | Safety moderation and relevance checks for user submissions |
| API hosting | Render | Web service for the TypeScript API |
| Authentication | Clerk | Sign-in and account identity |
| Relational data | Neon Postgres | Catalog, favorites, watched/read activity, and recommendation-sharing preference |
| Graph | Neo4j AuraDB | Synchronized catalog and pseudonymous interactions for recommendations |
| Cache / rate limits | Upstash Redis | Short-lived cache, rate limiting, and ephemeral coordination |
Clerk owns account identities; this app does not copy account records into Neon. Neon owns the catalog, favorites, watched/read activity, and recommendation sharing preferences, while Neo4j is a synchronized graph used for recommendations. If Neo4j is unavailable, library changes remain stored in Neon and the graph can be synchronized again later. The web app and API are separate deployable services. Managed-service credentials belong only in the API environment and must never be exposed to browser code.
For Neo4j AuraDB, set NEO4J_URI, NEO4J_USER, and NEO4J_PASSWORD in
server/.env using the values from Aura's Connect → Drivers instructions.
AURA_INSTANCENAME (for example, Instance01) is just the instance's display
name; it is not a connection setting and the API does not use it.
For local development, CORS_ORIGIN can remain http://localhost:5173. In
Render, set it to the exact deployed frontend origin (no trailing slash) when
using a custom domain. The current Vercel production origin,
https://glify-chi.vercel.app, is also allowed by the API. If a browser origin
is not allowed, production shows a connection error instead of substituting
demo works.
When using a production Vercel deployment, set
VITE_CLERK_PUBLISHABLE_KEY to the Clerk production publishable key and
add the Vercel domain to Clerk's allowed domains.
Requirements: Node.js 20+ and npm.
npm install
npm run devOpen the local URL Vite prints (usually http://localhost:5173). The UI works without database credentials and shows a status message when it is using sample catalog data.
To enable sign-in and sign-up, create a Clerk application and set
VITE_CLERK_PUBLISHABLE_KEY in the repository-root .env.local file. This
frontend variable does not belong in server/.env. Restart Vite after changing
the key. To persist account favorites and watched/read activity, also set CLERK_SECRET_KEY in
server/.env; the server uses it to verify the user's session token.
-
Create a Neon project and copy its pooled or direct connection string.
-
From the repository root, install the API dependencies and create its local environment file:
npm --prefix server install Copy-Item server/.env.example server/.env
-
Set
DATABASE_URLinserver/.envto the full connection string from Neon Console → your project → Connect. Replace the entire example value; do not leave<user>,<password>,<host>, or any other placeholders in it. SetCLERK_SECRET_KEYto the secret key from your Clerk application. -
Create or upgrade the catalog, favorites, watched/read activity, and recommendation-preference tables, then start both apps:
npm run db:setup npm run dev:all
-
Open http://localhost:5173. The catalog API returns approved works (up to 200 per request). Favorites and watched/read activity are available only after signing in and are stored in Postgres per Clerk account. The API health check is at http://localhost:3001/health and the catalog endpoint is http://localhost:3001/api/works.
After configuring DATABASE_URL in server/.env and running npm run db:setup:
npm run db:import-anilist # first page (up to 50 titles)
npm run db:import-anilist -- --page 2 # next page (up to 50 more)The importer only includes titles with "yuri" in their first 6 AniList tags. The public catalog also requires an approved title to have "yuri" or "shoujo ai" in its first 6 tags. Re-running an import page updates records instead of creating duplicates.
After importing titles while the API is already running, run
npm run db:seed-graph to reconcile the entire Neo4j catalog and account
favorites from Neon. The API also performs this reconciliation at startup.
When a signed-in user opens their list or changes a favorite, their Neo4j user
node and FAVORITED / WATCHED relationships are synchronized from Neon.
Clerk remains the identity provider; Neon is the source of truth for account
activity and Neo4j is the recommendation graph. Run npm run db:setup against
the production Neon database before deploying this version so the new tables
exist.
npm run db:filter-yuriRemoves works without "yuri" or "shoujo ai" in their first 6 tags from both Postgres and Neo4j.
| Command | Description |
|---|---|
npm run dev |
Start the Vite frontend dev server |
npm run dev:server |
Start the Fastify API dev server |
npm run dev:all |
Start both frontend and API |
npm run build |
Build the frontend for production |
npm run build:server |
Build the API for production |
npm run typecheck:server |
Type-check the API |
npm --prefix server test |
Test personalized recommendation scoring |
npm run db:setup |
Create or upgrade catalog and account activity tables |
npm run db:import-anilist |
Import yuri-tagged titles from AniList |
npm run db:filter-yuri |
Remove works without yuri in first 6 tags |
npm run db:seed-graph |
Reconcile Neo4j works and account interactions from Neon |
npm run preview |
Preview the production frontend build |
glify/
├── src/ # React frontend (Vite)
│ ├── App.tsx # Main application component
│ ├── catalog.ts # Local sample catalog (fallback)
│ └── styles.css # Global styles
├── server/ # Fastify API (TypeScript)
│ ├── src/
│ │ ├── index.ts # Server entry point
│ │ ├── app.ts # App factory
│ │ ├── config.ts # Environment configuration
│ │ ├── db/ # Database modules (Postgres, Neo4j)
│ │ ├── recommendations/# Weighted recommendation scoring
│ │ ├── plugins/ # Fastify plugins
│ │ └── routes/ # API route handlers, including library/activity
│ └── .env.example # Environment template
├── images/ # Screenshots and visual assets
├── scope.md # Product scope and architecture
└── README.md # This file
- Frontend → Vercel: Import the repository, set the project root to the repository root. Use
npm run buildanddistas the output directory. SetVITE_API_URLto the Render API origin andVITE_CLERK_PUBLISHABLE_KEYto the Clerk publishable key. - API → Render: Deploy
server/as a web service with the service root directory set toserver. Usenpm install,npm run build, andnpm start. ConfigureDATABASE_URL,CORS_ORIGIN,CLERK_SECRET_KEY, and Neo4j credentials in Render's environment settings. - Neon schema: Before deploying this API version, run
npm run db:setuplocally withserver/.envpointed at the production Neon database. This safely creates the watched/read and activity-sharing tables alongside existing catalog/favorites data. - Neo4j AuraDB: Configure the driver URI, username, and password in the API service's environment. The API synchronizes graph works from Neon at startup and updates user favorite/watched relationships as they change.
- Upstash Redis: Configure managed Redis credentials only in the API service when cache/rate-limit features are implemented.
See CONTRIBUTING.md for guidelines on how to contribute to GLify.

