🚀 Setting up our API with Express

Every great API starts with a solid foundation. In this lesson, we'll create a basic Express server, set up proper environment variable handling with type safety, and organize our code for scalability and testability.


Creating the Express Application

Why Separate Server from Startup?

Before diving into code, let's understand why we separate our Express app definition from the server startup:

<aside> 🎯

Benefits of Separation:

The Server Module

<aside> 📝

File: src/server.ts

</aside>

import express from 'express'

// Create Express application
const app = express()

// Health check endpoint - always good to have!
app.get('/health', (req, res) => {
  res.status(200).json({
    status: 'OK',
    timestamp: new Date().toISOString(),
    service: 'Habit Tracker API',
  })
})

// Export the app for use in other modules (like tests)
export { app }

// Default export for convenience
export default app

<aside> 💡

Key Design Decisions:

The Entry Point

<aside> 📝

File: src/index.ts

</aside>

import { env } from '../env.ts'
import app from './server.ts'

// Start the server
app.listen(env.PORT, () => {
  console.log(`Server running on port ${env.PORT}`)
  console.log(`Environment: ${[env.APP](<http://env.APP>)_STAGE}`)
})

<aside> ⚡

Why This Approach Works:


Environment Variable Management

The Problem with Raw process.env

// ❌ Problems with raw process.env
const port = process.env.PORT || 3000        // Type is string | undefined
const dbUrl = process.env.DATABASE_URL       // Could be undefined!
const jwtSecret = process.env.JWT_SECRET     // No validation

// Runtime errors waiting to happen:
app.listen(port)  // TypeError if PORT is not a number
db.connect(dbUrl) // Error if DATABASE_URL is missing

Type-Safe Environment Handling