fastify
Version:
Fast and low overhead web framework, for Node.js
471 lines (404 loc) • 11.9 kB
text/typescript
/* eslint no-unused-vars: 0 */
/* eslint no-undef: 0 */
// This file will be passed to the TypeScript CLI to verify our typings compile
import * as fastify from '../../fastify'
import * as http from 'http'
import * as http2 from 'http2'
import { readFileSync } from 'fs'
// were importing cors using require, which causes it to be an `any`. This is done because `cors` exports
// itself as an express.RequestHandler which is not compatible with the fastify TypeScript types
const cors = require('cors')
{
// http
const h1Server = fastify()
// https
const h1SecureServer = fastify({
https: {
cert: readFileSync('path/to/cert.pem'),
key: readFileSync('path/to/key.pem')
}
})
// http2
const h2Server = fastify({ http2: true })
// secure http2
const h2SecureServer = fastify({
http2: true,
https: {
cert: readFileSync('path/to/cert.pem'),
key: readFileSync('path/to/key.pem')
}
})
const h2AllowH1SecureServer = fastify({
http2: true,
https: {
allowHTTP1: true,
cert: readFileSync('path/to/cert.pem'),
key: readFileSync('path/to/key.pem')
}
})
// logger true
const logAllServer = fastify({ logger: true })
logAllServer.addHook('onRequest', (req, res, next) => {
console.log('can access req', req.headers)
next()
})
// other simple options
const otherServer = fastify({
ignoreTrailingSlash: true,
bodyLimit: 1000,
maxParamLength: 200
})
// custom types
interface CustomIncomingMessage extends http.IncomingMessage {
getDeviceType: () => string;
}
const customServer: fastify.FastifyInstance<http.Server, CustomIncomingMessage, http.ServerResponse> = fastify()
customServer.use((req, res, next) => {
console.log('can access props from CustomIncomingMessage', req.getDeviceType())
})
interface CustomHttp2IncomingMessage extends http2.Http2ServerRequest {
getDeviceType: () => string;
}
const customHttp2Server: fastify.FastifyInstance<http2.Http2Server, CustomHttp2IncomingMessage, http2.Http2ServerResponse> = fastify()
customHttp2Server.use((req, res, next) => {
console.log('can access props from CustomIncomingMessage', req.getDeviceType())
})
}
const server = fastify({
https: {
cert: readFileSync('path/to/cert.pem'),
key: readFileSync('path/to/key.pem')
},
http2: true
})
// Third party middleware
server.use(cors())
// Custom middleware
server.use('/', (req, res, next) => {
console.log(`${req.method} ${req.url}`)
})
/**
* Test various hooks and different signatures
*/
server.addHook('preHandler', function (req, reply, next) {
this.log.debug('`this` is not `any`')
if (req.body.error) {
next(new Error('testing if middleware errors can be passed'))
} else {
// `stream` can be accessed correctly because `server` is an http2 server.
console.log('req stream', req.req.stream)
console.log('res stream', reply.res.stream)
reply.code(200).send('ok')
}
})
server.addHook('onRequest', function (req, res, next) {
this.log.debug('`this` is not `any`')
console.log(`${req.method} ${req.url}`)
next()
})
server.addHook('onResponse', function (res, next) {
this.log.debug('`this` is not `any`')
this.log.debug({ code: res.statusCode }, 'res has a statusCode')
setTimeout(function () {
console.log('response is finished after 100ms?', res.finished)
next()
}, 100)
})
server.addHook('onSend', function (req, reply, payload, next) {
this.log.debug('`this` is not `any`')
console.log(`${req.req.method} ${req.req.url}`)
next()
})
server.addHook('onClose', (instance, done) => {
done()
})
server.addHook('onRoute', (opts) => {
})
const schema: fastify.RouteSchema = {
body: {
type: 'object'
},
querystring: {
type: 'object'
},
params: {
type: 'object'
},
headers: {
type: 'object'
},
response: {
200: {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
},
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
const opts: fastify.RouteShorthandOptions<http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse> = {
schema,
beforeHandler: [
(request, reply, next) => {
request.log.info(`before handler for "${request.raw.url}" ${request.id}`)
next()
}
],
schemaCompiler: (schema: Object) => () => {},
bodyLimit: 5000,
logLevel: 'trace',
config: { }
}
// Chaining and route definitions
server
.route({
method: 'GET',
url: '/route',
handler: (req, reply) => {
reply.send({ hello: 'route' })
},
beforeHandler: (req, reply, done) => {
req.log.info(`before handler for "${req.req.url}" ${req.id}`)
done()
}
})
.get('/req', function (req, reply) {
reply.send(req.headers)
})
.get<{ foo: number }>('/req', function ({ query, headers }, reply) {
const foo: number = query.foo
reply.send(headers)
})
.get('/', opts, function (req, reply) {
reply.header('Content-Type', 'application/json').code(200)
reply.send({ hello: 'world' })
})
.get('/status', function (req, reply) {
reply.status(204).send()
})
.get('/promise', opts, function (req, reply) {
const promise = new Promise(function (resolve, reject) {
resolve({ hello: 'world' })
})
reply.header('content-type', 'application/json').code(200).send(promise)
})
.get('/return-promise', opts, function (req, reply) {
const promise = new Promise(function (resolve, reject) {
resolve({ hello: 'world' })
})
return promise
})
.get('/stream', function (req, reply) {
const fs = require('fs')
const stream = fs.createReadStream(process.cwd() + '/examples/plugin.js', 'utf8')
reply.code(200).send(stream)
})
.get('/redirect', function (req, reply) {
reply.redirect('/other')
reply.redirect(301, '/something')
})
.post('/', opts, function (req, reply) {
reply
.headers({ 'Content-Type': 'application/json' })
.send({ hello: 'world' })
})
.head('/', {}, function (req, reply) {
reply.send()
})
.delete('/', opts, function (req, reply) {
reply.send({ hello: 'world' })
})
.patch('/:id', opts, function (req, reply) {
req.log.info(`incoming id is ${req.params.id}`)
reply.send({ hello: 'world' })
})
.route({
method: ['GET', 'POST', 'PUT'],
url: '/multi-route',
handler: function (req, reply) {
reply.send({ hello: 'world' })
}
})
.route({
method: 'GET',
url: '/with-config',
config: { foo: 'bar' },
handler: function (req, reply) {
reply.send(reply.context.config)
}
})
.register(function (instance, options, done) {
instance.get('/route', opts, function (req, reply) {
reply.send({ hello: 'world' })
})
done()
}, { prefix: 'v1', hello: 'world' })
.all('/all/no-opts', function (req, reply) {
reply.send(req.headers)
})
.all('/all/with-opts', opts, function (req, reply) {
reply.send(req.headers)
})
// Generics example
interface Query {
foo: string
bar: number
}
interface Params {
foo: string
}
interface Headers {
'X-Access-Token': string
}
interface Body {
foo: {
bar: {
baz: number
}
}
}
// Query, Params, Headers, and Body can be provided as generics
server.get<Query, Params, Headers, Body>('/', ({ query, params, headers, body }, reply) => {
const bar: number = query.bar
const foo: string = params.foo
const xAccessToken: string = headers['X-Access-Token']
const baz: number = body.foo.bar.baz
reply.send({ hello: 'world' })
})
// Default values are exported for each
server.get<fastify.DefaultQuery, Params>('/', ({ params }, reply) => {
const foo: string = params.foo
reply.send({ hello: 'world' })
})
// Using decorate requires casting so the compiler knows about new properties
server.decorate('utility', () => {})
server.hasDecorator('utility')
server.hasRequestDecorator('utility')
server.hasReplyDecorator('utility')
// Define a decorated instance
interface DecoratedInstance extends fastify.FastifyInstance<http2.Http2SecureServer, http2.Http2ServerRequest, http2.Http2ServerResponse> {
utility: () => void
}
// Use the custom decorator. Could also do "let f = server as DecoratedInstance"
(server as DecoratedInstance).utility()
// Decorating a request or reply works in much the same way as decorate
interface DecoratedRequest extends fastify.FastifyRequest<http2.Http2ServerRequest> {
utility: () => void
}
interface DecoratedReply extends fastify.FastifyReply<http2.Http2ServerResponse> {
utility: () => void
}
server.get('/test-decorated-inputs', (req, reply) => {
(req as DecoratedRequest).utility();
(reply as DecoratedReply).utility()
})
server.setNotFoundHandler((req, reply) => {
})
server.setErrorHandler((err, request, reply) => {
reply.send(err)
})
server.listen(3000, err => {
if (err) throw err
const address = server.server.address()
if (typeof address === 'object') {
server.log.info(`server listening on ${address.port}`)
} else {
server.log.info(`server listening on ${address}`)
}
})
server.listen(3000, '127.0.0.1', err => {
if (err) throw err
})
server.listen(3000, '127.0.0.1', 511, err => {
if (err) throw err
})
server.listen('/tmp/sock', err => {
if (err) throw err
})
server.listen(3000)
.then((address: string) => console.log(address))
server.listen(3000, '127.0.0.1')
.then((address: string) => console.log(address))
server.listen(3000, '127.0.0.1', 511)
.then((address: string) => console.log(address))
server.listen('/tmp/sock')
.then((address: string) => console.log(address))
// http injections
server.inject({ url: '/test' }, (err: Error, res: fastify.HTTPInjectResponse) => {
server.log.debug(err)
server.log.debug(res.payload)
})
server.inject({ url: '/testAgain' })
.then((res: fastify.HTTPInjectResponse) => console.log(res.payload))
server.setSchemaCompiler(function (schema: object) {
return () => true
})
server.addSchema({})
server.addContentTypeParser('*', (req, done) => {
done(null, {})
})
server.addContentTypeParser(['foo/bar'], (req, done) => {
done(null, {})
})
server.addContentTypeParser('foo/bar', {}, (req, done) => {
done(null, {})
})
server.addContentTypeParser(['foo/bar'], {}, (req, done) => {
done(null, {})
})
server.addContentTypeParser('foo/bar', { bodyLimit: 20 }, (req, done) => {
done(null, {})
})
server.addContentTypeParser('foo/bar', { parseAs: 'string' }, (req, body: string, done) => {
done(null, {})
})
server.addContentTypeParser('foo/bar', { parseAs: 'buffer', bodyLimit: 20 }, (req, body: Buffer, done) => {
done(null, {})
})
server.addContentTypeParser('foo/bar', async (req: http2.Http2ServerRequest) => [])
if (typeof server.hasContentTypeParser('foo/bar') !== 'boolean') {
throw new Error('Invalid')
}
server.printRoutes()
server.ready(function (err) {
if (err) throw err
})
server.ready(function (err: Error, done: Function) {
done(err)
})
server.ready(function (err: Error, context: fastify.FastifyInstance<http2.Http2SecureServer, http2.Http2ServerRequest, http2.Http2ServerResponse>, done: Function) {
server.log.debug(context)
done(err)
})
server.ready()
.then((context) => {
server.log.debug(context)
})
.catch((err) => {
server.log.error(err)
})
server.after(function (err) {
if (err) throw err
})
server.after(function (err: Error, done: Function) {
done(err)
})
server.after(function (err: Error, context: fastify.FastifyInstance<http2.Http2SecureServer, http2.Http2ServerRequest, http2.Http2ServerResponse>, done: Function) {
server.log.debug(context)
done(err)
})
{
const server: fastify.FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse> = fastify({
logger: process.env.NODE_ENV === 'dev'
})
}