Zum Hauptinhalt springen

Express JS

https://www.youtube.com/watch?v=SccSCuHhOw0

:todo auth in node:

https://www.passportjs.org/docs/

create your own server with express.js (& node.js)

useful:

lsof -Pi :8080 // find whatever is blocking port 8080 -> kill PID

install

npm init
npm i express

nmp i --save-dev nodemon  

in package.json, add "scripts":

{"devStart": "nodemon server.js}
  • fails with .ejs fix: nodemon -e js,ejs,css,html server.js

with server.js the server we are working with.

basics

make express listen on port 8080

const express = require('express')
const app = express()
app.listen(8080)

express to handle common html requests

  • app.get
  • app.post
  • app.put
  • app.delete
  • app.patch

example that listens to '/' (on port 8080)

app.get('/', (req, res) =>{
console.log("logs to console in server-terminal")
res.send('sends data to user')
})

send Status code 500 for Internal Server Error:

  • res.sendStatus(500) : sends code 500, Internal Server Error

or we can chain together methods:

  • res.status(500).send("custom error message")

we can even send some json to, for example some API

res.json( {message: "Error"} )

send some file to download for client

res.download("./pdf/example.pdf")

render out a (html) file

res.render('index')
  • default path for those view-files is folder views/index.html

view engine

npm install pug --save | or | npm install ejs here with ejs rename .html to .ejs -> views/index.ejs

const express = require('express')
const app = express()
app.set('view engine*, 'ejs')
app.get('/', (req, res) =>{
res.render('index', {info: "object with any info you want to pass to client .ejs"})
})
app.listen(8080)

inside of the view we can access that object
<% means to run this enclosed code serverside %>
<%= to output it on the page =%>

<body>
<%= info =%>
</body>

in the above case if info would not be defined -> error.
Workarrond: locals is always defined, can also pass default value

<%= locals.info || 'Default value' =%>

routers

to manage big routing paths, encapsulating becomes a necessity. To create for example a seperate file for all User related paths:

const express = require('express')
const app = express()
app.get("/", (req, res) =>{
res.render("index")
})
app.get("/posts", (req, res) =>{
res.send("Post History")
})
app.get("/posts/new", (req, res) =>{
res.send("User New Form")
})
...
const userRouter =require('./routes/game)
app.use('/game', userRouter)
app.listen(8080)

standard is folder routes with for example routes/posts.js, routes/game.js ...

const express = require('express')
const router = express.Router()
router.get("/move", (req, res) =>{
res.send("Player move")
})
router.get("/new", (req, res) =>{
// we dont need the full path /game/new, only the relative one
res.send("New game state")
})

module.exports = router

advanced routing

in routes/users.js
create a new user:

router.post("/", (req, res) =>{
res.send("Create User")
})

get any user (pulling id from URL). Since id could be anything dynamic we catch it with "/:varname"

router.get("/:userID", (req, res) =>{
req.params.userID =req.params.userID + "access like so"
res.send("Get User With ID ${req.params.userID}")
})
  • NOTE: top gets parsed before bottom means, if we use dynamic "/:id" first nothing else becomes reachable!

update user with put:

router.put("/:name", (req, res) =>{
res.send("Update User with ID ${req.params.name}"
})

delete user with delete:

delete.put("/:name", (req, res) =>{
res.send("Delete User with ID ${req.params.name}"
})

since router.get(), router.put(), router.delete() is a verry common pattern we can chain them instead:

router
.route("/:id").
.get((req, res) =>{
console.log(req.user) // was set in the middleware below
res.send("Get user: ${req.user} with id: ${req.params.id}"
})
.put((req, res) =>{
res.send("Update user: ${req.params.id}"
})
.delete((req, res) =>{
res.send("Delete user: ${req.params.id}"
})

router.param

Runs whenever any of the "/:id" routings above triggers:

const userStorage[{name: "Kyle"}, {name: "Sally}

router.param("id", (req, res, next, n)=>{
req.user = userStorage[id]
console.log(n)
next()
})

param is midleware running between the request beeing sent to the server and the actual response (res.send("Get..."))

  • Without a next(), where the next, pending function gets called, the script would stop after the console.log(n)
  • benefits include writing the code to "calculate" req.user only once and not 3 times for each .get, .put, .delete...

middleware example

https://www.youtube.com/watch?v=lY6icfhap2o for more indepth view.

  • middleware always uses the next() part where .get, .put... mostly dont use it.

this logger just console logs the url that this request originaly comes from:

function logger(req, res, next){
console.log(req.originalUrl)
next()
}

app.use(logger)

put this app.use(logger) above the rest if you would want to log for every request. Since middleware like every other routing runs **top to bottom!**

to just log for destinct requests. we could put multiple middleware functions there.

app.get("/", logger, logger (req, res) =>{
res.render("index", {text: "default"})
})

rendering static files

to serve out static files like a plain index.html we dont need a full route. Instead we can call the express.static. By convenction public folder ./public/index.html , ./public/css/style.css with:

app.use(express.static("public"))

parsing form/JSON data with middleware

we need to parse the information sent to your server by forms or JSON requests like api calls.

<body>
<form action="/users" method="POST">
<label> First Name
<input type="text" name="firstNameXXX" value= <%= locals.firstNameXXX %>" />
</label>
<button type="submit">Abschicken</button>
</form>
<body>
    router.get("/new", (req, res) => {
res.render("users/new", {firstName: "show in client as default"})
})

router.post("/", (req, res) =>{
res.body.firstNameXXX
})
app.use(express.urlencoded({extended: true}))
  • urlencoded allows us to access data coming from forms. The {extended: true} is just there to avoid some warnings.
  • express.json() for json requests.