Install Installation guide In terminal npm i express HTTP server Basic server
// index.js
const express = require('express')
const app = express()
const path = require('path')
const PORT = process.env.PORT || 5000
app.listen(PORT, () => console.log(`Server started on port ${PORT}`))
Static files
const path = require('path')
app.use(express.static(path.join(__dirname, 'public'), {
index: false,
extensions:['html'] // no need to put html extension
}))
app.use(express.static('some other directory')) // looks up files in the order static directories are set
// manually serve files
app.get('/', (req, res) => res.sendFile(path.join(__dirname, 'public', 'index.html')))
To start a server run node index.js Hot restart Every update we do to our project files requires manual server restart with Ctrl + C & node index.js To avoid it we may install nodemon tool and launch our server via it In terminal npm i -D nodemon to install the package as a development dependency Add into package.json following scripts
"scripts": {
"start": "node index",
"dev": "nodemon index"
},
Launch server via npm run dev Now the server will be restarted automatically on every file update API documentation https://expressjs.com/en/4x/api.html Middleware Express is a routing and middleware web framework Express app is a series of middleware function calls Middleware functions can execute any code Can change the request or response objects End the request-response cycle Call the next middleware function in the stack app.use() Mounts the specified middleware function(s) at the specified path Middleware function is executed when the base of the requested path matches path. Middleware function parameters MW function has access to request & response objects and has the next function, which calls the next middleware function
app.use('/user/:id', (req, res, next) => {
console.log('Request Type:', req.method)
next()
})
Application level middleware
const timeLogs = (req, res, next) => {
console.log(`URL hit: ${req.protocol}://${req.get('host')}${req.originalUrl} on ${new Date().toString()}`)
next()
}
app.use(timeLogs)
Every time the app receives a request, it prints the log message to the terminal Middleware in a separate file Add custom property to the request object
// /middleware/hitTime
const hitTime = (req, res, next) => {
req.hitTime = new Date()
console.log(`added time to request object`)
next()
}
module.exports = hitTime
// index.js
const addHitTimeToReqObj = require('./middleware/hitTime')
app.use('/checkAddedHitTimeToRequest', addHitTimeToReqObj)
Middleware function will be triggered when a user hit the url http://localhost:5000/checkAddedHitTimeToRequest We can show this newly added object on the screen adding HTTP GET request route.
app.get(
'/checkAddedHitTimeToRequest',
(req, res) => res.send(req.hitTime.toDateString() + req.hitTime.toLocaleTimeString())
)
Middleware with parameters We can not pass parameters into a middleware function directly, because in app.use() we have to pass only a reference to a middleware function That's why we need to create a helper function, which can accept arguments, and return a middleware function with passed parameters
const mwWithOpt = (flag = true) => {
return function (req, res, next) {
if (flag) console.log('triggered')
next()
}
}
app.use(mwWithOpt(false))
Will not see 'hi' in console, because we passed false param to a middleware function None of further middleware functions are triggered if false is passed, because next() is never called next() vs next('route') next()
app.get('/next',
(req, res, next) => {
console.log('app.get("/next") - 01')
next()
},
(req, res, next) => {
console.log('app.get("/next") - 02')
next()
}
)
app.use(function(req, res, next) {
console.log('app.use() - 03')
next()
})
app.get('something_else', function(req, res, next) {
console.log('app.get("/something_else") - 04')
next()
})
app.get('/next', function(req, res, next) {
console.log('app.get("/next") - 05')
next()
})
Hit http://localhost:5000/next and get in console
app.get("/next") - 01
app.get("/next") - 02
app.use() - 03
app.get("/next") - 05
next('route') next('route') passes control to the next matching route (GET, POST, PUT, DELETE methods).
app.get('/next',
(req, res, next) => {
console.log('app.get("/next") - 01')
next('route')
},
(req, res, next) => {
console.log('app.get("/next") - 02')
next()
}
)
app.use(function(req, res, next) {
console.log('app.use() - 03')
next()
})
app.get('something_else', function(req, res, next) {
console.log('app.get("/something_else") - 04')
next()
})
app.get('/next', function(req, res, next) {
console.log('app.get("/next") - 05')
next()
})
Hit http://localhost:5000/next and get in console.
app.get("/next") - 01
app.use() - 03
app.get("/next") - 05
Routing Routes HTTP GET requests to the specified path with the specified callback functions. app.get()
app.get('/get', (req, res) => res.send('<h1>get request for /get url</h1>'))
Hit http://localhost:5000/get and get rendered text on a screen. app.post()
app.post('/post', (req, res) => res.send(hi'))
We can make a post request from node file.
// /axios.js
const axios = require('axios');
axios.post('http://localhost:3000/post')
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
When we execute node axios.js we get hi in console Also can send the request from the browser's console and get the same result
fetch('/post', { method: 'POST' })
.then(response => response.text())
.then(msg => console.log(msg))
There might be a security restriction and we should provide some content-security-policy parameters into request headers by middleware function in our http server file.
app.use(function(req, res, next) {
res.header("Content-Security-Policy", "default-src 'self'")
next()
})
app.all()
app.all('/all', (req, res, next) => {
res.send('<h1>all</h1>')
console.log('middleware1 for all http requests')
next() // pass control to the next handler
})
app.all('/all', (req, res, next) => console.log('middleware2 for all http requests'))
app.all('/all', (req, res, next) => console.log('middleware3 for all http requests'))
Note, we can have multiple middleware functions for the same path. Last middleware will not be triggered, because next() function was not called at the 2nd function Multiple functions in one method Comma separated
app.get(
'/hi',
function (req, res, next) {
console.log('Hello')
next()
},
function (req, res) {
console.log('Hello again')
res.send('Hello!')
}
)
In array
const a = (req, res, next) => { console.log('a'); next(); }
const b = (req, res, next) => { console.log('b'); next(); }
const c = (req, res) => { console.log('c'); res.send('Hello from c!'); }
app.get('/path1', [a, b, c])
app.get('/path2', a, [b, c])
Path accepts RegExp
// http://localhost:5000/asdfhljljalasd73
app.get(
/.*73$/,
(req, res) => res.send('<h1>any url ending with "73" matching regexp /.*73$/</h1>')
)
// http://localhost:5000/about?fav_language=HTML&vehicle1=Bike&vehicle2=Car
app.get(
//about.*/,
(req, res, next) => {
console.log(req.query) // { fav_language: 'HTML', vehicle1: 'Bike', vehicle2: 'Car' }
next()
}
)
Dynamic route & req.params
// http://localhost:5000/users/34/books/8989
app.get(
'/users/:userId/books/:bookId',
(req, res) => res.send(req.params) // { "userId": "34", "bookId": "8989" }
)
// hyphen (-) and the dot (.) are interpreted literally
// http://localhost:5000/flights/LAX-SFO
app.get(
'/flights/:from-:to',
(req, res) => res.send(req.params) // { "from": "LAX", "to": "SFO" }
)
// regexp can be used in parentheses ()
// http://localhost:5000/user/42
app.get(
'/user/:userId(\d+)',
(req, res) => res.send(req.params) // {"userId": "42"}
)
Chainable routing
app.route('/book')
.get((req, res, next) => res.send('Get a random book'))
.post((req, res) => res.send('Add a book'))
.put((req, res) => res.send('Update the book'))
Router We can extract routes and middlewares to a separate files with router objects.
// /animals/routerFile.js
// creates a router as a module, loads a middleware function in it,
// defines routes, and mounts the router module on a path in the main app
var express = require('express')
var animalRouter = express.Router()
// middleware that is specific to this router
animalRouter.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
// define the cats page route
animalRouter.get('/', (req, res) => res.send('main page for animals'))
// define the dogs page route
animalRouter.get('/dogs', (req, res) => res.send('page for dogs'))
module.exports = animalRouter
// index.js
// load the router module in the app
const routerAnimals = require('./animals/routerFile.js')
// http://localhost:5000/animals
// http://localhost:5000/animals/dogs
app.use('/animals', routerAnimals)
Res methods res.json()
app.get('/json', (req, res) => res.json({id: 1, name: 'John'}))
res.send() Same as res.json()
app.get('/send', (req, res) => res.send({id: 1, name: 'John'}))
File download starts.
app.get('/send-buffer', (req, res) => res.send(Buffer.from('whoop')))
app.get('/send', (req, res) => res.send('<p style="color:red">some html</p>'))
Note, it just returns partially rendered page, without styling. To enable styles need to adjust content-security-policy .
app.use(function(req, res, next) {
res.header("Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline';")
next()
})
res.status()
app.get('/send-error-status-ver1', (req, res) => res.status(404).send('404: Sorry, we cannot find that!'))
app.get('/send-error-status-ver2', (req, res) => res.sendStatus(404))
res.redirect()
app.get('/redirect', (req, res) => res.redirect(301, 'https://google.com'))
res.sendFile() Put file /911.pdf in the root folder File will be opened in the browser Same way can serve html files into the browser
const path = require('path')
app.get('/sendFile/:file', (req, res) => {
console.log(req.params)
res.sendFile(path.join(__dirname, req.params.file))
})
res.download() File will be downloaded.
app.get('/download', (req, res) => res.download('911.pdf'))
res.render() Renders a view from the template engine app Use Pug rendering app for example Install npm install pug Create /views/index.pug file as in example
// viewsindex.pug
html
head
title= title
body
h1(style={'color': 'red'})= message
app.set('view engine', 'pug')
app.get('/pug', (req, res) => {
res.render('index', { title: 'Hey', message: 'Hello there!' })
})
res.end()
app.get('/end', (req, res) => res.status(404).end("<h1 style='color:red;'> Error! </h1>"))
res.set() Creates or replaces header
app.use((req, res, next) => {
res.set('xxx', '1')
res.set('xxx', '2')
res.set('xxx', '3')
res.set('xxx', '4').send('check the response header')
// only xxx: 4 will be in header
res.set('Content-Type', 'text/plain')
// multiple headers
res.set({
'Content-Type': 'text/plain',
'Content-Length': '123',
ETag: '12345'
})
next()
})
res.append() Appends value to the HTTP response header field.
app.get('/append', (req, res) => {
res.append('xxx', '1')
res.append('xxx', '2')
res.append('xxx', '3')
res.append('xxx', '4').send('check the response header')
// all 4 properties will be listed in response header
next()
})
POST request from html form and fetch https://antonarbus.com/posts/post_requests Serve React static folder from Express
const express = require('express')
const path = require('path')
const app = express()
const PORT = 3010
// React static files from 'build' folder
app.use(express.static(path.join(__dirname, '..', 'build')))
// not needed, just an exampe, how to serve spcific file for the url
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, '..', 'build', 'index.html'))
})
// all other routes go via React routes to index.html
app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname, '..', 'build', 'index.html'))
})
app.listen(PORT, () => console.log(`Server started on port ${PORT}`))
Route from React to Express APIs
// server.js
const express = require('express')
const app = express()
app.get('/hi', (req, res) => {
res.json({ message: 'hi!' })
})
app.listen(3001, () => {
console.log('server started on port 3001')
})
// app.js
import React from 'react'
function App () {
const [data, setData] = React.useState(null)
React.useEffect(() => {
fetch('/hi')
.then((res) => res.json())
.then((data) => setData(data.message))
}, [])
return (
<>
<p>{!data ? 'Loading...' : data}</p>
</>
)
}
export default App
Add proxy into package.json
{
"name": "quotation.app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"concurrently": "^7.2.1",
"express": "^4.18.1",
"nodemon": "^2.0.16",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"client": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"lint": "eslint '**/*.js'",
"eject": "react-scripts eject",
"server": "nodemon server.js",
"dev": "concurrently "npm run server" "npm run client""
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"eslint": "^8.17.0",
"eslint-config-standard": "^17.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.2.1",
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.30.0"
},
"proxy": "http://localhost:3001"
}