NodeJSInstalling & Setting Up Express

Installing & Setting Up Express

Getting an Express project running is a few commands — but the setup choices you make now (module system, dev tooling, project layout) shape everything after. This page walks through a clean, modern setup using ES modules and a sensible structure, and flags the small decisions that save pain later.

Initialize the project

Bash
mkdir my-api && cd my-api
npm init -y                 # create package.json
npm install express         # add Express as a dependency
`express` goes in `dependencies`, not `devDependencies`
Express is needed at runtime, so a plain `npm install express` (which adds it to `dependencies`) is correct. Only build/test-time tools belong in `devDependencies`. If you're unsure of the distinction, see [Dependency Types](/nodejs/dependencies-types).
Choose ES modules

Add "type": "module" to package.json so you can use import syntax (the modern standard) instead of require:

package.json

JSON
{
  "name": "my-api",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node server.js",
    "dev": "node --watch server.js"
  },
  "dependencies": {
    "express": "^5.0.0"
  }
}
`import` requires `"type": "module"` (or a `.mjs` file)
Without `"type": "module"`, Node treats `.js` files as CommonJS and `import` statements throw `SyntaxError: Cannot use import statement outside a module`. Either set `"type": "module"` (recommended) or use `const express = require('express')`. Don't mix the two styles in one file. The module-system details are in [CommonJS vs ESM](/nodejs/module-wrapper).
Your entry file

server.js

JS
import express from 'express'

const app = express()
const PORT = process.env.PORT || 3000

app.get('/', (req, res) => {
  res.send('Express is running!')
})

app.listen(PORT, () => {
  console.log(`Server listening on http://localhost:${PORT}`)
})
Read the port from the environment
`process.env.PORT || 3000` lets the hosting platform inject a port in production while defaulting to 3000 locally. Hardcoding a port breaks many deployment environments (they assign one and expect you to use it). Configuration via environment variables is standard practice — see [Environment Variables](/nodejs/environment-variables) later in the tutorial.
Auto-restart during development

Bash
# Built into modern Node (18.11+) — no dependency needed:
node --watch server.js

# The classic third-party tool (more features, older Node):
npm install --save-dev nodemon
npx nodemon server.js
`node --watch` replaces nodemon for most needs
Node now ships a built-in `--watch` flag that restarts the process when files change — covering the main reason people installed `nodemon`. Use `--watch` for simple projects; reach for `nodemon` only if you need its extra config (watch globs, delays, exec hooks). Either way, this is dev-only — production runs plain `node` (or a process manager).
A sensible project structure

Text
my-api/
├── package.json
├── server.js            # entry: create app, listen
├── app.js              # configure app: middleware + routes (no listen)
├── routes/
│   ├── users.js        # express.Router() modules
│   └── posts.js
├── middleware/
│   └── auth.js
├── controllers/        # request handlers, separated from routing
└── .env                # local config (gitignored!)
Separate `app` (configuration) from `server` (listening)
A common pattern: `app.js` builds and exports the configured Express app (middleware + routes), and `server.js` imports it and calls `listen`. This separation makes the app **testable** — your tests import `app` and make requests against it without binding a real port. We'll use [Router modules](/nodejs/express-router) to keep routes out of one giant file.
Essential .gitignore

.gitignore

Text
node_modules/
.env
*.log
Never commit `node_modules` or `.env`
`node_modules` is huge and reproducible from `package-lock.json` (`npm ci` rebuilds it), so committing it bloats the repo for no benefit. `.env` typically holds secrets (API keys, DB passwords) — committing it leaks credentials into git history, which is hard to fully scrub. Add both to `.gitignore` before your first commit.
Verify it works

Bash
npm run dev
# then in another terminal:
curl http://localhost:3000
# → Express is running!
Express is running!
Next
Build a real first app with multiple routes and JSON: [Your First Express App](/nodejs/express-first-app).