This project is being built by a team at Blueprint, a student organization at the University of California, Berkeley building software pro bono for nonprofits.
Check your installation of node and pnpm:
node -v
pnpm -vWe strongly recommend using a Node version manager like nvm (for Mac) or nvm-windows (for Windows) to install Node.js. If you don't plan on switching between different Node versions, you can alternatively get a prebuilt installer from the Node.js website for an easier approach. Make sure to get Node version 20 and up, the latest LTS version should be sufficient.
After installing Node, you most likely have npm installed as well (check by running npm -v). If you have npm installed, simply run npm install -g pnpm to install pnpm. If your command line does not recognize npm as a command, refer to this article to troubleshoot.
Additional resources:
-
Clone the repo & install dependencies
- Clone this repo
- using SSH (recommended)
git clone git@github.com:calblueprint/zero-abuse-project.git
- using HTTPS
git clone https://github.com/calblueprint/zero-abuse-project.git
- using SSH (recommended)
- Enter the cloned directory
cd zero-abuse-project - Install project dependencies. This command installs all packages from
package.json.pnpm install
- Clone this repo
-
Set up secrets:
- In the project's root directory (
zero-abuse-project/), create a new file named.env.local - Copy
example.envinto.env.localand fill in the Supabase values. - Get
NEXT_PUBLIC_SUPABASE_URLandNEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEYfrom the Supabase project settings (or from Blueprint's internal Notion). - In the Supabase dashboard, open Connect, select the Transaction pooler, copy its URI, replace the password placeholder, and save it as
DATABASE_URL. The app uses node-postgres through Drizzle and queues commands on each connection without query pipelining. The configured URI is used unchanged. Each app instance uses at most one connection and closes it after five idle seconds. URL-encode any special characters in the password.DATABASE_URLis a privileged server secret. Never rename it with aNEXT_PUBLIC_prefix or import the Drizzle client into a Client Component. - Set
NEXT_PUBLIC_SITE_URLtohttp://localhost:3000locally and to the deployed origin in production.
- In the project's root directory (
For Vercel, add DATABASE_URL to both Production and Preview environment variables and redeploy after changing it. SUPABASE_URL (an https://...supabase.co API address) does not replace the PostgreSQL connection URI. Verify a successful login and authenticated library load on the preview before merging; an unauthenticated login-page check does not exercise the database.
Helpful resources
- VSCode (recommended)
- Open the
zero-abuse-projectproject in VSCode. - Install recommended workspace VSCode extensions. You should see a pop-up on the bottom right to "install the recommended extensions for this repository".
- Open the
In the project directory, run:
pnpm devThen, navigate to http://localhost:3000 to launch the web application.
Supabase hosts the PostgreSQL database and also provides services such as authentication, storage, realtime updates, and a browser-safe Data API. Drizzle is the typed TypeScript layer used by our server code to define the database schema, write SQL-like queries, and create repeatable SQL migrations. Drizzle does not replace Supabase; it connects to the PostgreSQL database that Supabase manages.
The data clients in this project have different jobs:
actions/supabase/client.tscreates the browser client. It is appropriate when a request should use the signed-in user's Supabase Auth session and Row Level Security.actions/supabase/server.tscreates the cookie-based server client used by Server Components, Server Actions, and Route Handlers.db/index.tsuses Drizzle over a direct PostgreSQL connection. It is for trusted server-side code only. The connection can bypass Row Level Security, so every server action or route using it must enforce authorization.
The current db/schema.ts and db/relations.ts were introspected from the
existing Supabase tables. The initial migration in drizzle/ is a commented
baseline: it describes the pre-existing tables but will not recreate them.
Run pnpm db:migrate once to record that baseline in Drizzle's migration log.
From now on, treat the Drizzle schema in the repository as the source of truth:
-
Change and export tables in
db/schema.tsrather than editing them manually in the Supabase dashboard. -
Generate a SQL migration from the schema changes:
pnpm db:generate
-
Review the generated SQL in
drizzle/, then apply it:pnpm db:migrate
Commit both the schema change and its generated migration. Useful commands:
pnpm db:check # validate generated migrations
pnpm db:pull # re-introspect tables created manually outside Drizzle
pnpm db:studio # open Drizzle Studio to inspect data
pnpm db:push # push a schema directly; use only for disposable/local workImport db only from server-side modules:
import { db } from "@/db";
import { items } from "@/db/schema";
const allItems = await db.select().from(items);