Backend API for Inkline, a full-stack publishing platform. It provides authentication, post publishing, nested comments, moderation, platform statistics, and administrator account management.
- Node.js 20.9+
- Express 5 and TypeScript
- PostgreSQL
- Prisma 7 with the PostgreSQL adapter
- Better Auth (email/password and Google OAuth)
- Nodemailer for verification and password-reset email
The API runs at http://localhost:3000 by default and supports both maintained local frontends:
| Frontend | Local URL |
|---|---|
| Angular | http://localhost:4200 |
| Next.js | http://localhost:4000 |
- Email/password registration and sign-in
- Google OAuth
- Email verification and password reset
- Cookie-based sessions and role-based authorization
USERandADMINrolesACTIVEandSUSPENDEDaccount states- Post creation, editing, deletion, drafts, publishing, archiving, tags, featured posts, search, sorting, and pagination
- View counting and platform statistics
- Nested comments with edit/delete support
- Administrator comment approval/rejection
- Administrator user search, role changes, suspension, and reactivation
- Immediate session revocation for suspended accounts
- Protection against self-suspension and removal of the final active administrator
prisma/
migrations/ Database migrations
schema/ Split Prisma schema files
src/
helpers/ Pagination and sorting helpers
lib/ Better Auth and Prisma configuration
middlewares/ Authentication and error handling
modules/
comment/ Comment API
post/ Post API
user/ Admin user-management API
scripts/admin.ts Development admin seeder
app.ts Express application
server.ts Database check and HTTP server startup
Create .env in the project root:
PORT=3000
DATABASE_URL=postgresql://postgres:your_password@localhost:5432/blog_app
BETTER_AUTH_SECRET=replace-with-a-long-random-secret
BETTER_AUTH_URL=http://localhost:3000
APP_URL=http://localhost:4200
CLIENT_URLS=http://localhost:4000,http://localhost:4200
APP_USER=your-smtp-email@example.com
APP_PASS=your-smtp-app-password
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secretAPP_URL is the primary frontend used in verification and password-reset links. Set it to http://localhost:4200 while developing the Angular application, or http://localhost:4000 while developing Next.js.
CLIENT_URLS is the comma-separated allowlist shared by Express CORS and Better Auth trusted origins. Keep every frontend that may call the API in this list. Origins must include the protocol and port, must not include a route, and should not have a trailing slash. Do not use a wildcard because authentication depends on credentialed cookies.
For Gmail SMTP, use an app password rather than the normal account password.
Both local frontends can use the same API process. Configure each frontend to use http://localhost:3000 as its API/auth base URL and send cookies with protected requests. The backend enables credentialed CORS only for origins in CLIENT_URLS.
Better Auth receives OAuth callbacks on the API origin and then redirects to the absolute frontend callback URL supplied by the client. For Google OAuth local development, configure this redirect URI in Google Cloud:
http://localhost:3000/api/auth/callback/google
Also add the frontend origins you actively use as authorized JavaScript origins:
http://localhost:4200
http://localhost:4000
Restart the backend after changing .env. If a frontend is omitted from CLIENT_URLS, browser requests may fail with CORS errors or Better Auth may reject the origin. If OAuth lands on a backend /dashboard route, the frontend passed a relative callback URL; pass an absolute URL on the Angular or Next.js origin instead.
-
Install dependencies:
npm install
-
Start PostgreSQL and create the database referenced by
DATABASE_URL. -
Apply migrations and generate the Prisma client:
npx prisma migrate dev npx prisma generate
-
Start the API:
npm run dev
The server verifies the database connection before listening. A Prisma ECONNREFUSED error means PostgreSQL is not reachable at the host/port in DATABASE_URL.
| Command | Purpose |
|---|---|
npm run dev |
Run the API with TSX watch mode |
npm run build |
Generate Prisma Client and compile TypeScript |
npm start |
Run the compiled server from dist/ |
npm run seed:admin |
Create the development admin defined in src/scripts/admin.ts |
npx prisma studio |
Open Prisma Studio |
The current admin seed script contains development-only account values. Change them before running it outside a local environment.
All Better Auth endpoints are mounted under /api/auth/*.
| Method | Route | Access | Purpose |
|---|---|---|---|
GET |
/posts |
Public | List/search/filter/sort posts |
GET |
/posts/stats |
Admin | Platform statistics |
GET |
/posts/my-posts |
User/Admin | Current writer's posts |
GET |
/posts/:postId |
Public | Read a post and approved comments |
POST |
/posts |
User/Admin | Create a post |
PATCH |
/posts/:postId |
Owner/Admin | Update a post |
DELETE |
/posts/:postId |
Owner/Admin | Delete a post |
Common GET /posts query parameters: search, tags, isFeatured, status, authorId, page, limit, sortBy, and sortOrder.
| Method | Route | Access | Purpose |
|---|---|---|---|
GET |
/comments |
Admin | List comments for moderation |
GET |
/comments/author/:authorId |
Public | List an author's comments |
GET |
/comments/:commentId |
Public | Get one comment |
POST |
/comments |
User/Admin | Add a comment or reply |
PATCH |
/comments/:commentId |
Owner/Admin | Edit a comment |
DELETE |
/comments/:commentId |
Owner/Admin | Delete a comment |
PATCH |
/comments/:commentId/moderate |
Admin | Approve or reject a comment |
| Method | Route | Access | Purpose |
|---|---|---|---|
GET |
/users |
Admin | Search/filter/paginate users and activity counts |
PATCH |
/users/:userId |
Admin | Change role or account status |
GET /users accepts search, role, status, page, and limit. Update payloads may contain role: "USER" | "ADMIN" and/or status: "ACTIVE" | "SUSPENDED".
- Protected requests require the Better Auth session cookie and a verified email.
- Ownership checks prevent writers from modifying another writer's content.
- Only admins can view statistics, moderate comments, feature any post, and manage users.
- Suspended accounts cannot create sessions or use protected routes.
- CORS credentials are enabled; browser API requests must use
credentials: "include"(Angular's auth/API clients must enable the equivalent credential option).
npm run build
npm startBefore deployment:
- Set
BETTER_AUTH_URLto the public HTTPS API origin. - Set
APP_URLto the primary public frontend and list every permitted public frontend inCLIENT_URLS. - Add the production Better Auth callback URI to the Google OAuth client.
- Use production values for the database, auth secret, and SMTP credentials.
- Run production migrations as part of the release process.