Noodle is a student productivity platform in active development — the goal is one place for notes, flashcards, task management, grade tracking, and calendar. Think Notion meets Anki but purpose-built for university life.
Why I starred it
The premise is familiar — "why are students using five different apps?" — but what made me stop on this one was the stack. This isn't a side project with a raw SQLite file and some client-side state. It's a full production-grade setup: tRPC v11, Drizzle ORM with Neon Postgres, Clerk for auth, Upstash Redis, and Resend for transactional email. For a pre-MVP app, the engineering foundations are unusually solid.
12k stars for something explicitly marked as pre-MVP also suggests the idea hit a nerve.
How it works
The app is a Next.js 14 project under src/, split into (site) (landing, auth) and (dashboard)/app (the actual product). Route protection is handled in src/middleware.ts with Clerk:
const isProtectedRoute = createRouteMatcher(['/app(.*)']);
export default clerkMiddleware((auth, req) => {
if (isProtectedRoute(req)) {
auth().protect();
}
});
Clean. Any route under /app requires auth, everything else is public.
The data layer is in src/db/index.ts. They're using Drizzle over Neon's serverless HTTP driver — not WebSockets, just HTTP:
const sql = neon(env.DATABASE_URL);
export const db = drizzle(sql, { schema, logger: env.NODE_ENV === 'development' });
This is the right call for a Next.js app on serverless infrastructure. Neon's HTTP driver avoids connection pool exhaustion that bites you when you spin up 50 concurrent lambda invocations.
The schema lives in src/db/schema/modules.ts and uses drizzle-zod to derive Zod validators directly from the table definition:
export const modulesTable = pgTable('modules', {
id: uuid('id').primaryKey().unique().defaultRandom().notNull(),
user_id: text('user_id').notNull(),
name: text('name').notNull(),
icon: text('icon').default('default').notNull(),
color: text('color').default('default').notNull(),
archived: boolean('archived').default(false).notNull(),
credits: integer('credits').default(0).notNull(),
...
});
export const insertModuleSchema = createInsertSchema(modulesTable).extend({ ... });
The tRPC context in src/server/trpc.ts injects db, resend, and redis into every procedure. The protectedProcedure middleware calls currentUser() from Clerk and throws UNAUTHORIZED if it's missing — one place to enforce auth across all routers.
The most interesting file in the whole repo is the early access email validator in src/server/routers/early-access.ts. They wrote a heuristic spam email scorer from scratch instead of reaching for a library:
function scoreEmail(email: string): number {
let score = 0;
// penalizes known automated domains (@slack.com, @github.com, @hubspot.com...)
// penalizes usernames with bot keywords: 'noreply', 'admin', 'alerts'...
// penalizes high number density in username
// penalizes gibberish via vowel/consonant ratio
// penalizes alternating letters-and-numbers patterns
return score;
}
function isLikelyHuman(email: string, threshold = 30): boolean {
return scoreEmail(email) < threshold;
}
isEmailGibberish checks the vowel-to-consonant ratio: if a username has zero vowels, or more than 8 consonants with fewer than 2 vowels, it scores as gibberish. It's not foolproof but it's genuinely thoughtful — better than a regex blocklist alone, cheaper than an external service.
The environment config uses @t3-oss/env-nextjs with Zod schemas for each variable. Type-safe env access at build time, validated at startup. If DATABASE_URL is missing, you get a clear error before the app boots.
Using it
Self-hosting requires Neon, Clerk, Upstash, and Resend accounts. Migrations run via Drizzle Kit:
bun run db:generate
bun run db:migrate
bun run dev
There's no Docker setup, so you're on your own for local orchestration of the external services.
Rough edges
The README is honest: the preview screenshot is a UI mockup, not the current product. The MVP features are limited to notes and flashcards. The flashcard generation from notes (described as AI-powered) isn't implemented yet.
There are no tests. Zero. The @happy-dom/global-registrator dev dependency is there, suggesting they planned for it, but the test directory doesn't exist.
The drizzle/0000_funny_johnny_blaze.sql migration only creates two tables: early_access and modules. That's the entire data model so far — most of the platform's planned features don't have schemas yet.
The README also hasn't been updated to reflect the switch from SQLite/Turso (listed in repo topics) to Neon Postgres. The code is Neon. The topics say Turso/SQLite. Minor but worth knowing if you're reading the docs.
Bottom line
Not usable as a student tool today — it's genuinely pre-MVP. Worth watching if you're interested in how a well-structured Next.js + tRPC + Drizzle app is organized, because the engineering groundwork is better than most projects twice its maturity. The email scoring code alone is worth five minutes of your time.
