Module 13 — Deployment
Get your app off localhost and onto the internet.
Overview 📋
Deployment is the process of making your application available on the internet. For the full-stack apps you are building, that means deploying your React frontend (to Netlify) and your Express backend (to Render), with a production PostgreSQL database hosted on Render's managed database service. This module walks through the process end to end.
Why This Matters 💡
A project that lives only on your laptop is not a project — it is a draft. Deploying gives you a real URL to put on your resume, share with employers, and show to users. It also exposes the gap between local development and production environments, which is valuable knowledge. Every developer needs to know how to ship.
Learning Goals 🎯
By the end of this module you should be able to:
- Deploy a React frontend to Netlify from a GitHub repository
- Configure environment variables on Netlify
- Handle client-side routing on Netlify with a
_redirectsfile - Deploy an Express backend to Render
- Connect a Render-hosted backend to a Render-managed PostgreSQL database
- Set production environment variables on Render
- Run production database migrations
Vocabulary 📖
| Term | Definition |
|---|---|
| Deployment | Making an application available on a live server |
| Build | The process of compiling and bundling your app for production |
| Environment variable | A configuration value set outside the code (not hardcoded) |
| CI/CD | Continuous Integration / Continuous Deployment — automatic builds on push |
| CDN | Content Delivery Network — serves static files from servers close to users |
| Managed database | A hosted database service that handles backups, patches, and scaling |
_redirects | A Netlify config file that handles SPA routing |
Core Concepts 🧠
Deploying the React frontend (Netlify)
- Push your project to GitHub
- Log in to Netlify (opens in new tab) and click Add new site → Import an existing project
- Connect your GitHub account and select the repo
- Set build settings:
- Base directory:
client(or wherever your Vite app lives) - Build command:
npm run build - Publish directory:
dist
- Base directory:
- Add environment variables under Site settings → Environment variables
- Deploy
Handle client-side routing:
# client/public/_redirects
/* /index.html 200
Without this, refreshing a React Router page returns a 404 from Netlify.
Deploying the Express backend (Render)
- Push your server code to GitHub
- Log in to Render (opens in new tab) and create a New Web Service
- Connect your GitHub repo
- Set:
- Root directory:
server - Build command:
npm install - Start command:
node index.js
- Root directory:
- Add environment variables (DATABASE_URL, PORT, etc.)
- Deploy
Using a Render PostgreSQL database
- Create a New PostgreSQL database on Render
- Copy the External Database URL
- Add it as
DATABASE_URLin your web service environment variables - Update
config/config.json(or useprocess.env.DATABASE_URLdirectly)
Running migrations in production
# add to your package.json scripts
"migrate": "npx sequelize-cli db:migrate"
Run via Render's shell or as a pre-deploy command.
Pointing the frontend at the production backend
// in your react app, use an environment variable for the api base url
const API_URL = import.meta.env.VITE_API_URL || '/api'
// in netlify environment variables:
// vite_api_url = https://your-app.onrender.com/api
Examples 💻
An Express server that works on any port:
const PORT = process.env.PORT || 3001
app.listen(PORT, () => console.log(`server running on port ${PORT}`))
Render assigns a port via process.env.PORT — your server must use it.
Disabling the Vite proxy in production:
// vite.config.js
export default defineConfig({
server: {
proxy: process.env.NODE_ENV === 'development'
? { '/api': 'http://localhost:3001' }
: undefined,
},
})
Common Mistakes ⚠️
- Committing
.envto GitHub. Render and Netlify both have environment variable UIs — never commit secrets. - Forgetting
_redirectson Netlify. React Router pages will 404 on direct access or refresh without it. - Using
localhostURLs in production. Your React app cannot callhttp://localhost:3001when deployed. UseVITE_API_URLand set it in Netlify. - Not running migrations after deploying a new model. The table does not exist in the production database until you migrate.
- Using a free Render instance and expecting it to stay awake. Free-tier Render services spin down after 15 minutes of inactivity. Warn users or upgrade for production.
Debugging Tips 🔍
- Check the deploy logs on Netlify and Render for build errors.
- On Render, the Logs tab shows runtime
console.logoutput — use it to debug server behavior. - If your frontend is getting CORS errors in production, make sure the
cors()middleware on Express is configured to allow your Netlify domain, or usecors()with no arguments to allow all origins. - Test API routes directly using the Render URL in Postman before debugging the frontend.
- If your frontend is blank after deploy, open browser DevTools → Console — the error message usually points to the issue.
Exercise 🏋️
The exercise for this module is in the class repository:
ttpr-lagcc-spring-2026 → Module 13 Exercise (opens in new tab)
Deploy your full-stack CRUD app from Module 12. Deploy the React frontend to Netlify and the Express backend to Render with a managed PostgreSQL database. Get a working live URL for both. Add the links to your GitHub profile README.
Additional Resources 📚
- Netlify docs (opens in new tab) — official Netlify documentation
- Render docs (opens in new tab) — official Render documentation
- Render — Deploying a Node.js app (opens in new tab) — step-by-step guide
- Netlify — Redirects and rewrites (opens in new tab) — configuring
_redirects - 12factor.net (opens in new tab) — best practices for building deployable apps (skim the config and processes sections)
Recap Checklist ✔️
- My React frontend is deployed on Netlify and accessible via a public URL
- I have a
_redirectsfile so React Router works on Netlify - My Express backend is deployed on Render
- I am using a Render-managed PostgreSQL database in production
- All secrets are in environment variables, not in code
- I ran migrations on the production database
- The frontend can successfully communicate with the production backend