Middlewares
Middleware is a method that is called after a request has been received by the server and before a response is sent to that request. Essentially, every request handler (or Router) is a middleware itself.
Writing your own middleware
Here is example of middleware:
import { RequestHandler } from '@shelepuginivan/lunatic' // only for TypeScript
const myMiddleware: RequestHandler = (req, _res, next) => {
console.log(`Handled ${req.method} request at ${Date.now()}`)
next() // call next middleware
}
app.use(myMiddleware)
You can specify request method and path that certain middleware should handle (see Routing):
import { RequestHandler } from '@shelepuginivan/lunatic'
const blockTrace: RequestHandler = (_req, res) => {
res.status(405).json({ message: 'TRACE is not allowed' })
}
app.trace(blockTrace) // blocks all TRACE requests
Built-in middlewares
Lunatic comes bundled with commonly used middlewares "out of the box". You don't need to install any package, just import them.
bodyParser
bodyParser
is a middleware allows you to retrieve the body of the request. It supports text/plain
and application/json
body types. Other content types, such as multipart/form-data
will be ignored.
Body object can be accessed with req.body
property.
import { bodyParser } from '@shelepuginivan/lunatic'
app.use(bodyParser)
app.post('/', (req, res) => {
console.log(req.body)
res.status(200).json({ message: 'received body' })
})
app.listen(8000)
cookieParser
cookieParser
is a middleware allows you to retrieve request cookies:
import { cookieParser } from '@shelepuginivan/lunatic'
app.use(cookieParser)
app.post('/', (req, res) => {
console.log(req.cookies)
res.status(200).json({ message: 'received cookies' })
})
app.listen(8000)
If request had no Cookie
HTTP header, field req.cookies
will be undefined
.
cors
cors
is a middleware that allows CORS requests.
import { cors } from '@shelepuginivan/lunatic'
app.use(cors())
app.post('/', (req, res) => {
res.status(200).json({ message: 'now CORS is supported!' })
})
app.listen(8000)
cors
is a configurable middleware, meaning that it accepts options
object:
import { cors, CorsOptions } from '@shelepuginivan/lunatic'
const corsOptions: CorsOptions = {
origin: 'http://localhost:3000',
credentials: true,
}
app.use(cors(corsOptions))
app.post('/', (req, res) => {
res.status(200).json({ message: 'now CORS is supported!' })
})
app.listen(8000)
CORS options
If you are using TypeScript, you can import interface CorsOptions
:
import { CorsOptions } from '@shelepuginivan/lunatic'
origin
- allowed request originboolean
- set totrue
to enable any origin,false
to disable CORS requestsstring
- sets a specific origin, e.g.http://localhost:3000
RegExp
- allows CORS request for origins that matches this regular expressionstring[]
- allows CORS request for origins presented in arrayRegExp[]
- allows CORS request for origins, that match one of presented regular expressions(origin: string) => boolean
- predicate function, should returntrue
if CORS is allowed, otherwisefalse
methods
- allowed request methodsHttpMethod
- specific HTTP methodHttpMethod[]
- array of allowed HTTP methods
allowedHeaders
- allowed request headersstring
- specific headerstring[]
- array of allowed headers
credentials
boolean
- allow or disallow credentials respectively. Note that if origin is set to*
(as by default), credentials won't be included
exposedHeaders
- response headers that can be accessed in browser JavaScriptstring
- specific headerstring[]
- array of exposed headers
maxAge
- configuresAccess-Control-Max-Age
headernumber
- (seconds) -Access-Control-Max-Age
value
corsErrorStatus
- response status if CORS is not allowed for this request. Default:403
number
- HTTP status code
preflightSuccessStatus
- response status for preflight OPTIONS request. Default:204
number
- HTTP status code
formParser
formParser
is a middleware that parses multipart/form-data
. Other content types are ignored.
Text input values can be retrieved with req.body
, and uploaded files with req.files
.
import { formParser, UploadedFile } from '@shelepuginivan/lunatic'
import { writeFile } from 'fs/promises'
app.use(formParser)
app.post('/', async (req, res) => {
console.log(req.body)
const [uploadedFile] = (req.files as Record<'file', UploadedFile[]>).file
await writeFile('new.txt', uploadedFile.data)
await res.status(200).json({ message: 'received form data' })
})
app.listen(8000)
UploadedFile
UploadedFile
is the interface where files are available for retrieval from a request. It has the following properties:
data
: Buffer - content of uploaded filefilename
: string - name of uploaded filemimetype
: string - MIME-type of uploaded file
Fields of req.files
objects are always of type UploadedFile[]
, even if a single file has been uploaded. This is done to standardise form processing for different requests.
notFound
notFound
is a small middleware that returns 404 Not Found
status code if the request did not match any route.
import { notFound } from '@shelepuginivan/lunatic'
app.post('/', (req, res) => {
res.status(200).json({ message: 'this IS found' })
})
app.use(notFound) // should be the last to check all routes before sending 404
app.listen(8000)
By default, if no route matched the request, server will respond with 501 Not Implemented
status.
serveStatic
serveStatic
is a middleware that allows to send static files from specified directory.
import { serveStatic } from '@shelepuginivan/lunatic'
import { join } from 'path'
const staticDir = join(__dirname, 'static')
app.use('/static', serveStatic(staticDir))
app.listen(8000)
In this example, all requests to /static
and /static/*
will be handled by serveStatic
. It will send a file like as follows:
-
Request path
/static/style.css
-> File<staticDir>/style.css
-
Request path
/static/images/d430849b.png
-> File<staticDir>/images/d430849b.png
etc.
If requested file does not exist, it will respond with 404 Not Found
status.
serveStatic
is a configurable middleware, meaning that it accepts options
object:
import { serveStatic, ServeStaticOptions } from '@shelepuginivan/lunatic'
import { join } from 'path'
const serveStaticOptions: ServeStaticOptions = {
dotfiles: 'forbid',
index: 'index.html',
lastModified: false,
}
app.use('/static', serveStatic(join(__dirname, 'static'), serveStaticOptions))
app.listen(8000)
ServeStaticOptions
If you are using TypeScript, you can import interface ServeStaticOptions
:
import { ServeStaticOptions } from '@shelepuginivan/lunatic'
dotfiles
- how should server respond when client attempts to get dotfile (file which name starts with.
, e.g..htaccess
). Default:'ignore'
'allow'
- respond with200 OK
status and send dotfile'forbid'
- respond with403 Forbidden
status and not send dotfile'ignore'
- respond with404 Not Found
status and not send dotfile, i.e. act like file does not exist
etag
- allow or disableETag
header. Default:true
boolean
- allow or disableETag
header respectively
index
- how should server respond if request path is a directory. Default:'index.html'
.string
- certain filename that should be sentfalse
- set if you want to disable index files at all
lastModified
- allow or disableLast-Modified
header. Default:true
boolean
- allow or disableLast-Modified
header respectively
The value of the ETag header is set as a hash of the last modification time of the file