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" }