A TypeScript Express backend for the SkillSwap application, using Prisma for database access, JWT authentication, real-time WebSocket messaging, and optional email sending via Resend.
- Express API with versioned routes
- PostgreSQL database via Prisma
- JWT access and refresh token handling
- Email/password signup with auto-login after verification
- Google signup/login with verified Google emails
- Socket.io chat/socket support
- Email templates and Resend integration
- Cloudinary profile image uploads
- Health/readiness endpoints
- Redis-compatible rate limiting when
REDIS_URLis configured - API reference notes for mobile/client integration
- Skill favorites, saved searches, and search history
- Booking conflict checks
- Admin moderation audit CSV export
- Environment-driven configuration
- Node.js
>=20 <25 - PostgreSQL-compatible database
- npm
- Copy environment variables:
cp .env.example .env- Update
.envwith your own values:
DATABASE_URLJWT_ACCESS_SECRETJWT_REFRESH_SECRET- email provider settings
- Google OAuth settings if you enable Google signup/login
- application URLs
- Install dependencies:
npm install- Generate Prisma client:
npm run db:generate- Run database migrations:
npm run db:migrate- Start development server:
npm run devThe API runs by default on http://localhost:4000.
Use this when you want to run the backend without manually running npm commands.
- Install and open Docker Desktop.
- Make sure
.envexists and contains your realDATABASE_URLand secrets. - Build and start the backend:
docker compose up --buildThe API runs on:
http://localhost:4000Stop it with:
docker compose downThe compose file also starts a local Redis container on port 6379. By default, Docker Compose sets REDIS_URL=redis://redis:6379, so rate limits use Redis locally.
Build the app:
npm run buildStart the server:
npm startFor production, make sure the required environment variables are provided by your host or deployment platform.
You can keep everything on Render if you want one provider/bill:
- Create a Render PostgreSQL database.
- Create a Render Key Value instance for Redis-compatible rate limiting.
- Create a Render Web Service for this backend.
- Add the PostgreSQL connection string as
DATABASE_URL. - Add the Key Value internal Redis URL as
REDIS_URL. - Add the rest of the required environment variables.
- Set the health check path to
/health.
Render free web services can spin down after inactivity, so the first request after a quiet period may be slow.
If deploying this Docker image to a host, the container start command runs:
npm run db:deploy && npm startThat means Prisma migrations run before the server starts. Make sure the production database migration baseline is fixed before using this in production.
npm run dev— start the TypeScript development servernpm run build— compile TypeScript todistnpm start— run the compiled production servernpm run db:generate— generate Prisma clientnpm run db:migrate— run Prisma migrationsnpm run db:deploy— apply migrations in productionnpm run db:push— push Prisma schema to the databasenpm run db:studio— open Prisma Studionpm run db:seed— seed local demo datanpm run check— generate Prisma client, build, and run tests
- Health check:
http://localhost:4000/health - OpenAPI spec:
docs/openapi.yaml - Route reference:
docs/api.md
POST /api/v1/auth/registercreates a pending registration and sends a verification code.POST /api/v1/auth/verify-emailcreates the user, marks the account verified, returnsaccessTokenandrefreshToken, and sets the refresh-token cookie.POST /api/v1/auth/googleaccepts a Google ID token or authorization code. If the email is new, it creates a verified account and signs the user in. If the user already exists, it signs them in. Google-authenticated users do not need a separate email verification step.
Required:
DATABASE_URLREDIS_URL(recommended for production rate limiting)JWT_ACCESS_SECRETJWT_REFRESH_SECRETGOOGLE_CLIENT_ID(required for Google signup/login)GOOGLE_CLIENT_SECRET(required when exchanging Google authorization codes)CLIENT_URLAPI_URLAPP_NAMESUPPORT_EMAILRESEND_API_KEY(required for Resend email senders)RESEND_FROM_EMAILRESEND_FROM_NAMERESEND_REPLY_TOCLOUDINARY_CLOUD_NAMECLOUDINARY_API_KEYCLOUDINARY_API_SECRET
Optional / defaults:
ACCESS_TOKEN_EXPIRES_IN— default15mREFRESH_TOKEN_EXPIRES_IN— default7dGOOGLE_REDIRECT_URI— default should match your frontend Google callback, for examplehttp://localhost:3000/auth/google/callbackPORT— default4000HOST— default0.0.0.0BOOKINGS_PAGE_URLSIGN_IN_URLAPP_HOME_URLMAIL_FROM_EMAILMAIL_FROM_NAMEVERIFY_ACCOUNT_URLVERIFY_EMAIL_CHANGE_URLRESET_PASSWORD_URLVERIFY_ACCOUNT_SUCCESS_URLVERIFY_ACCOUNT_FAILURE_URLVERIFY_EMAIL_CHANGE_SUCCESS_URLVERIFY_EMAIL_CHANGE_FAILURE_URLRESEND_TEMPLATE_*values for optional hosted email templates
.envis intentionally ignored by Git.- Keep secrets out of version control and configure them through your deployment platform.
- If you do not provide specific email template IDs, the app will use local templates.
CLIENT_URLmay include one or more allowed origins separated by commas for CORS.- JWT secrets must be at least 32 characters.
docs/api.mdis the hand-written route reference you can use to fill an external Swagger/OpenAPI tool.
.gitignorealready excludes.env- Existing app uses
dotenv/configand expects the variables listed above README.mdand.env.exampleare now present for onboarding and deployment