Understanding the Problem
Up to this point, we've been developing and testing our Express authentication application locally on our own computer. While this is perfect for development, eventually we need to make our application available to everyone on the internet. This process of making an application available online is called deployment.
Deployment involves several key challenges:
- Finding a hosting service that can run our Node.js application
- Setting up a production database (PostgreSQL instead of SQLite)
- Configuring environment variables for the production environment
- Building and starting the application in a production environment
- Making our application resilient and maintainable in production
In this phase, we'll deploy our application to Render, a cloud platform that offers free hosting for web services and databases. Render makes it relatively easy to deploy Express applications, but there are several important steps and considerations we need to address.
Planning the Solution
- Set up a package.json file at the project root
- Create a Render.com account
- Create a PostgreSQL database instance on Render
- Create a new web service on Render
- Configure build and start commands
- Set up environment variables
- Deploy the application
- Test the deployed application
- Understand ongoing maintenance requirements
Implementing the Solution
1. Set Up a package.json File at the Project Root
When deploying to Render, we need a package.json file at the root of our project (outside of both the backend and frontend folders). This file will define scripts that Render will use to install dependencies and start our application.
// Navigate to the project root (outside of backend and frontend)
cd ..
npm init -y
Now, let's modify the package.json to include the necessary scripts:
// package.json (in the project root)
{
"name": "your-project-name",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"install": "npm --prefix backend install backend",
"dev:backend": "npm install --prefix backend start",
"sequelize": "npm run --prefix backend sequelize",
"sequelize-cli": "npm run --prefix backend sequelize-cli",
"start": "npm start --prefix backend",
"build": "npm run --prefix backend build"
},
"keywords": [],
"author": "",
"license": "ISC"
}
These scripts tell Render:
- install: Run npm install in the backend folder
- dev:backend: (optional) Start the backend in development mode
- sequelize and sequelize-cli: Run Sequelize commands in the backend folder
- start: Start the backend server
- build: Run the build script in the backend folder
Commit these changes to your main branch:
git add .
git commit -m "Add root package.json for deployment"
git checkout main
git merge dev
git push origin main
2. Set Up a Render.com Account
If you don't already have a Render.com account, you'll need to create one:
- Go to Render.com and click "Get Started"
- Sign up with GitHub (recommended) to easily connect your repositories
- Follow the instructions to complete your registration and verify your account
3. Create a PostgreSQL Database Instance
Now, let's create a PostgreSQL database that our application will use in production:
- From your Render dashboard, click "New +" and select "PostgreSQL"
- Give your database a name (e.g., "app-academy-projects")
- Choose the region closest to you
- Leave the other fields with their default values
- Click "Create Database"
After the database is created (which may take a few minutes), Render will display important information about your database, including:
- The hostname
- The username and password
- Connection URLs
Take note of the "Internal Database URL" - we'll need this later.
4. Create a New Web Service
Next, let's create a web service to host our Express application:
- From your Render dashboard, click "New +" and select "Web Service"
- Connect your GitHub repository
- If you don't see your repository, click "Configure Account" for GitHub in the sidebar to connect your GitHub account
- Find your project repository and click "Connect"
5. Configure the Web Service
Now, configure the web service with the following settings:
- Name: Choose a name for your service (this will be part of your URL)
- Region: Choose the same region as your database
- Branch: main
- Root Directory: Leave blank (default is the root of your repo)
- Environment: Node
- Build Command: Enter the following command (all on one line):
npm install && npm run build && npm run sequelize --prefix backend db:seed:undo:all && npm run sequelize --prefix backend db:migrate:undo:all && npm run sequelize --prefix backend db:migrate && npm run sequelize --prefix backend db:seed:all
This build command:
- Installs dependencies
- Runs the build script
- Undoes all seed data and migrations
- Runs all migrations to set up the database schema
- Seeds the database with initial data
Note: This build command is designed for development and testing. For a final production deployment, you might want to remove the "undo" steps to preserve your data:
npm install && npm run build && npm run sequelize --prefix backend db:migrate && npm run sequelize --prefix backend db:seed:all
- Start Command: npm start
6. Set Up Environment Variables
Scroll down to the "Environment Variables" section and add the following variables:
- JWT_SECRET: Click "Generate" to create a secure random value
- JWT_EXPIRES_IN: 604800 (or the value from your local .env file)
- NODE_ENV: production
- SCHEMA: Choose a custom schema name (use snake_case)
- DATABASE_URL: Copy the "Internal Database URL" from your PostgreSQL database instance
Make sure "Auto-Deploy" is set to "Yes" in the Advanced section. This will automatically redeploy your application when you push to the main branch.
7. Deploy the Application
Click "Create Web Service" to start the deployment process. This will take some time (usually 10-15 minutes) as Render:
- Builds your application according to the build command
- Sets up the environment variables
- Starts your application using the start command
You can monitor the progress in the logs. Once the deployment is complete, Render will provide a URL where your application is accessible (e.g., https://your-app-name.onrender.com).
8. Test the Deployed Application
Let's test our deployed API to make sure everything is working correctly:
- First, test the CSRF token route: https://your-app-name.onrender.com/api/csrf/restore
- You should see a JSON response with a CSRF token
- Use this token to test your login endpoint using a tool like Postman or a fetch request in a browser console
Example fetch request to test login:
fetch('https://your-app-name.onrender.com/api/session', {
method: 'POST',
headers: {
"Content-Type": "application/json",
"XSRF-TOKEN": "your-csrf-token-here"
},
body: JSON.stringify({ credential: 'Demo-lition', password: 'password' })
}).then(res => res.json()).then(data => console.log(data));
Test other endpoints similarly to ensure they're all working correctly.
9. Understand Ongoing Maintenance
With Render's free tier, there's an important limitation to be aware of: your PostgreSQL database instance will be deleted after 90 days. To keep your application running, you'll need to create a new database instance before that happens.
Set a calendar reminder for 85 days from now (to give yourself some buffer) with these steps:
- Create a new PostgreSQL database instance on Render
- Update the DATABASE_URL environment variable in your web service with the new URL
- Manually trigger a new deployment
This will ensure your application continues to run smoothly without data loss.
Review the Solution
We've successfully deployed our Express authentication application to Render! Let's review what we've accomplished:
- Set up a project structure that works well for deployment
- Created a PostgreSQL database instance for production use
- Configured a web service to host our Express application
- Set up environment variables for the production environment
- Created build and start commands to handle deployment
- Successfully deployed and tested our application
- Understood the maintenance requirements for our deployed application
Deployment Architecture
Our deployment architecture consists of two main components:
- PostgreSQL Database: Stores our user data and other application information
- Web Service: Runs our Express application, handling HTTP requests and interacting with the database
This is a simple but effective architecture for many web applications. As your application grows, you might consider more complex architectures with separate services for different parts of your application, content delivery networks (CDNs) for static assets, and caching layers for improved performance.
Development vs. Production Environments
It's important to understand the key differences between our development and production environments:
| Aspect | Development (Local) | Production (Render) |
|---|---|---|
| Database | SQLite (file-based) | PostgreSQL (server-based) |
| Environment | development | production |
| Hosting | Local machine | Render cloud platform |
| Error Visibility | Detailed (includes stack traces) | Limited (no stack traces) |
| Security Settings | More relaxed (e.g., CORS enabled) | Stricter (e.g., secure cookies required) |
Real-world Deployment Considerations
In real-world applications, deployment often involves additional considerations:
- Continuous Integration/Continuous Deployment (CI/CD): Automating testing and deployment to ensure quality and reduce manual work
- Monitoring and Logging: Setting up tools to track application performance and errors
- Scaling: Adjusting resources based on traffic and usage patterns
- Backup and Recovery: Ensuring data is regularly backed up and can be restored if needed
- Security Updates: Keeping dependencies and the environment up to date with security patches
While our deployment is relatively simple, these principles are still important to consider, especially as your application grows.
Common Issues and Solutions
- Build Failures: If your build fails, check the logs for specific errors. Common issues include syntax errors, missing dependencies, or incorrect build commands.
- Database Connection Errors: Verify that your DATABASE_URL environment variable is correct and that your application is properly configured to use it.
- Missing Environment Variables: Double-check that all required environment variables are set correctly in Render.
- CORS Issues: If your frontend application can't connect to your API, you might need to configure CORS settings for the production environment.
- Schema Issues: Make sure your SCHEMA environment variable is set and that your migrations and models are correctly using it.
Debugging in Production
When issues occur in production, you have several tools at your disposal:
- Render Logs: Check the logs in your Render dashboard for error messages and application output.
- Manual Testing: Use tools like Postman or browser fetch requests to test endpoints directly.
- Database Inspection: Connect to your PostgreSQL database to verify data and schema.
- Local Replication: Try to replicate the issue locally by setting up similar environment variables.
Remember, debugging in production requires a careful, methodical approach to avoid introducing new issues or causing downtime.
Conclusion
Congratulations! You've successfully built and deployed a complete Express authentication application. This application provides a solid foundation for any web project that requires user authentication. You can now build frontend applications that use this API for authentication, or extend the API with additional features specific to your application.
Throughout this tutorial, you've learned how to:
- Set up an Express application with security middleware
- Create API routes for a RESTful service
- Implement error handling for a robust API
- Build a complete user authentication system with JWT
- Validate user inputs to ensure data quality and security
- Deploy an Express application to a cloud platform
These skills are fundamental to full-stack web development and will serve you well in building a wide variety of web applications.
Next Steps
Now that you have a working authentication API, here are some ways you might extend or build upon it:
- Frontend Development: Build a React or other frontend application that consumes this API
- Additional Features: Add features like password reset, email verification, or social login
- User Profiles: Extend the User model with additional profile information
- Authorization: Implement role-based access control for different types of users
- API Extensions: Add domain-specific API endpoints for your application's core functionality
Whatever path you choose, the authentication system you've built provides a solid foundation for your application's security needs.