Basic web server
routes/root.js
const random = require("./data");
module.exports = async function (fastify, opts) {
fastify.get("/", async function (request, reply) {
return random();
});
};
app.js
module.exports = async function (fastify, opts) {
fastify.get("/", (request, reply) => {
reply.status(200);
return "ok";
});
fastify.post("/", (request, reply) => {
reply.status(405); // 405 is method not allowed
return "ng";
});
};
npm install --save-dev fastify-static
app.js
const path = require('path')
const AutoLoad = require('fastify-autoload')
const dev = process.env.NODE_ENV !== 'production'
const fastifyStatic = dev && require('fastify-static')
module.exports = async function (fastify, opts) {
if (dev) {
fastify.register(fastifyStatic, {
root: path.join(__dirname, 'public')
})
}
fastify.register(AutoLoad, {
dir: path.join(__dirname, 'plugins'),
options: Object.assign({}, opts)
})
fastify.register(AutoLoad, {
dir: path.join(__dirname, 'routes'),
options: Object.assign({}, opts)
})
fastify.setNotFoundHandler((request, reply) => {
if (request.method !== 'GET') {
reply.status(405)
return 'Method Not Allowed\n'
}
return 'Not Found\n'
})
}
routes/root.js
module.exports = async (fastify, opts) => {
fastify.get('/', async (request, reply) => {
return reply.sendFile('hello.html')
})
}
npm install point-of-view handlebars
app.js
const path = require('path')
const AutoLoad = require('fastify-autoload')
const pointOfView = require('point-of-view')
const handlebars = require('handlebars')
module.exports = async function (fastify, opts) {
fastify.register(pointOfView, {
engine: { handlebars },
root: path.join(__dirname, 'views'),
layout: 'layout.hbs'
})
fastify.register(AutoLoad, {
dir: path.join(__dirname, 'plugins'),
options: Object.assign({}, opts)
})
fastify.register(AutoLoad, {
dir: path.join(__dirname, 'routes'),
options: Object.assign({}, opts)
})
fastify.setNotFoundHandler((request, reply) => {
if (request.method !== 'GET') {
reply.status(405)
return 'Method Not Allowed\n'
}
return 'Not Found\n'
})
}
routes/root.js
module.exports = async (fastify, opts) => {
fastify.get('/', async (request, reply) => {
return reply.view('index.hbs' /** optional object as second param of data to use in the template, {someKey: 'value'} */)
})
}
layout.hbs
const data = require("../../stream");
module.exports = async function (fastify, opts) {
fastify.get("/", async function (request, reply) {
// You can simply just return the stream
return data();
});
};
Hacker News stream example
const hnLatestStream = require('hn-latest-stream')
module.exports = async (fastify, opts) => {
fastify.get('/', async (request, reply) => {
const { amount = 10, type = 'html' } = request.query
if (type === 'html') reply.type('text/html')
if (type === 'json') reply.type('application/json')
return hnLatestStream(amount, type)
})
}
const { promisify } = require("util");
const { boat } = require("../../model");
const read = promisify(boat.read);
module.exports = async function (fastify, opts) {
fastify.get("/:id", async function (request, reply) {
const { id } = request.params;
reply.type("application/json");
try {
const data = await read(id);
return reply.send(data);
} catch (error) {
if (error.message === "not found") {
return reply.notFound();
}
throw error;
}
});
};
const { promisify } = require("util");
const { boat } = require("../../model");
const { uid } = boat;
const read = promisify(boat.read);
const create = promisify(boat.create);
module.exports = async function (fastify, opts) {
const { notFound } = fastify.httpErrors;
fastify.get("/:id", async (request, reply) => {
const { id } = request.params;
reply.type("application/json");
try {
const data = await read(id);
reply.send(data);
} catch (error) {
if (error.message === "not found") throw notFound();
throw error;
}
});
fastify.post("/", async (request, reply) => {
const { data } = request.body;
const id = uid();
await create(id, data);
reply.status(201);
return { id };
});
};
const { promisify } = require("util");
const { boat } = require("../../model");
const read = promisify(boat.read);
const del = promisify(boat.del);
module.exports = async function (fastify, opts) {
const { notFound } = fastify.httpErrors;
fastify.get("/:id", async function (request, reply) {
const { id } = request.params;
try {
const data = await read(id);
reply.send(data);
} catch (error) {
if (error.message === "not found") throw notFound();
throw error;
}
});
fastify.delete("/:id", async (request, reply) => {
const { id } = request.params;
try {
await del(id);
reply.status(204);
} catch (error) {
if (error.message === "not found") throw notFound();
throw error;
}
});
};
npm install got@11
routes/root.js
const got = require('got')
const {
BICYCLE_SERVICE_PORT = 4000, BRAND_SERVICE_PORT = 5000
} = process.env
const bicycleSrv = `http://localhost:${BICYCLE_SERVICE_PORT}`
const brandSrv = `http://localhost:${BRAND_SERVICE_PORT}`
module.exports = async function (fastify, opts) {
const { httpErrors } = fastify
fastify.get('/:id', async function (request, reply) {
const { id } = request.params
try {
const [ bicycle, brand ] = await Promise.all([
got(`${bicycleSrv}/${id}`).json(),
got(`${brandSrv}/${id}`).json()
])
return {
id: bicycle.id,
color: bicycle.color,
brand: brand.name,
}
} catch (err) {
if (!err.response) throw err
if (err.response.statusCode === 404) {
throw httpErrors.notFound()
}
if (err.response.statusCode === 400) {
throw httpErrors.badRequest()
}
throw err
}
})
}
const got = require("got");
const { BOAT_SERVICE_PORT, BRAND_SERVICE_PORT } = process.env;
const boatSvc = `http://localhost:${BOAT_SERVICE_PORT}`;
const brandSvc = `http://localhost:${BRAND_SERVICE_PORT}`;
module.exports = async function (fastify, opts) {
const { notFound, badRequest } = fastify.httpErrors;
fastify.get("/:id", async function (request, reply) {
const { id } = request.params;
if (!id || !/^\d+$/.test(id)) throw badRequest();
try {
const boat = await got
.get(`${boatSvc}/${id}`, { timeout: 500, retry: 0 })
.json();
console.log("boat", boat);
const brand = await got
.get(`${brandSvc}/${boat?.brand}`, { timeout: 500, retry: 0 })
.json();
console.log("brand", brand);
return {
id: boat?.id,
color: boat?.color,
brand: brand?.name,
};
} catch (error) {
console.log("error", error);
if (!error.response) throw error;
if (error.response.statusCode === 404) throw notFound();
if (error.response.statusCode === 400) throw badRequest();
throw error;
}
});
};
npm install fastify-reply-from
plugins/reply-from.js
const fp = require('fastify-plugin')
module.exports = fp(async function (fastify, opts) {
fastify.register(require('fastify-reply-from'), {
errorHandler: false
})
})
routes/root.js
module.exports = async function (fastify, opts) {
fastify.get('/', async function (request, reply) {
const { url } = request.query
try {
new URL(url)
} catch (err) {
throw fastify.httpErrors.badRequest()
}
return reply.from(url)
})
}
Using streams
const { Readable } = require('stream')
async function * upper (res) {
for await (const chunk of res) {
yield chunk.toString().toUpperCase()
}
}
module.exports = async function (fastify, opts) {
fastify.get('/', async function (request, reply) {
const { url } = request.query
try {
new URL(url)
} catch (err) {
throw fastify.httpErrors.badRequest()
}
return reply.from(url, {
onResponse (request, reply, res) {
reply.send(Readable.from(upper(res)))
}
})
})
}
npm install fastify-http-proxy
app.js
const sensible = require("fastify-sensible");
const proxy = require('fastify-http-proxy')
module.exports = async function (fastify, opts) {
fastify.register(sensible);
fastify.register(proxy, {
upstream: 'https://news.ycombinator.com/'
})
}
With authorization token
const proxy = require('fastify-http-proxy')
const sensible = require('fastify-sensible')
module.exports = async function (fastify, opts) {
fastify.register(sensible)
fastify.register(proxy, {
upstream: 'https://news.ycombinator.com/',
async preHandler(request, reply) {
if (request.query.token !== 'abc') {
throw fastify.httpErrors.unauthorized()
}
}
})
}
routes/bicycle/index.js
const { promisify } = require('util')
const { bicycle } = require('../../model')
const { uid } = bicycle
const read = promisify(bicycle.read)
const create = promisify(bicycle.create)
const update = promisify(bicycle.update)
const del = promisify(bicycle.del)
module.exports = async (fastify, opts) => {
const { notFound } = fastify.httpErrors
const dataSchema = {
type: 'object',
required: ['brand', 'color'],
additionalProperties: false,
properties: {
brand: {type: 'string'},
color: {type: 'string'}
}
}
const bodySchema = {
type: 'object',
required: ['data'],
additionalProperties: false,
properties: {
data: dataSchema
}
}
const idSchema = { type: 'integer' }
const paramsSchema = { id: idSchema }
fastify.post('/', {
schema: {
body: bodySchema,
response: {
201: {
id: idSchema
}
}
}
}, async (request, reply) => {
const { data } = request.body
const id = uid()
await create(id, data)
reply.code(201)
return { id }
})
fastify.post('/:id/update', {
schema: {
body: bodySchema,
params: paramsSchema
}
}, async (request, reply) => {
const { id } = request.params
const { data } = request.body
try {
await update(id, data)
reply.code(204)
} catch (err) {
if (err.message === 'not found') throw notFound()
throw err
}
})
fastify.get('/:id', {
schema: {
params: paramsSchema,
response: {
200: dataSchema
}
}
}, async (request, reply) => {
const { id } = request.params
try {
return await read(id)
} catch (err) {
if (err.message === 'not found') throw notFound()
throw err
}
})
fastify.put('/:id', {
schema: {
body: bodySchema,
params: paramsSchema
}
}, async (request, reply) => {
const { id } = request.params
const { data } = request.body
try {
await create(id, data)
reply.code(201)
return { }
} catch (err) {
if (err.message === 'resource exists') {
await update(id, data)
reply.code(204)
} else {
throw err
}
}
})
fastify.delete('/:id', {
schema: {
params: paramsSchema
}
}, async (request, reply) => {
const { id } = request.params
try {
await del(id)
reply.code(204)
} catch (err) {
if (err.message === 'not found') throw notFound()
throw err
}
})
}
const express = require('express')
const app = express()
const router = express.Router()
const { PORT = 3000 } = process.env
router.get('/', (req, res) => {
setTimeout(() => {
if (Array.isArray(req.query.un)) {
return res.send((req.query.un.map((str) => str.toUpperCase())))
}
return res.send((req.query.un || '').toUpperCase())
}, 1000)
})
app.use(router)
app.listen(PORT, () => {
console.log(`Express server listening on ${PORT}`)
})
const { promisify } = require('util')
const { boat } = require('../../model')
const { uid } = boat
const read = promisify(boat.read)
const create = promisify(boat.create)
const del = promisify(boat.del)
module.exports = async (fastify, opts) => {
const { notFound } = fastify.httpErrors
fastify.post('/', {
schema: {
body: {
type: 'object',
required: ['data'],
properties: {
data: {
type: 'object',
required: ['color', 'brand'],
properties: {
color: {type: 'string'},
brand: {type: 'string'}
}
}
}
}
}
}, async (request, reply) => {
const { data } = request.body
const id = uid()
await create(id, data)
reply.code(201)
return { id }
})
fastify.delete('/:id', async (request, reply) => {
const { id } = request.params
try {
await del(id)
reply.code(204)
} catch (err) {
if (err.message === 'not found') throw notFound()
throw err
}
})
fastify.get('/:id', {
schema: {
response: {
200: {
color: { type: 'string' },
brand: { type: 'string' },
}
}
}
}, async (request, reply) => {
const { id } = request.params
try {
return await read(id)
} catch (err) {
if (err.message === 'not found') throw notFound()
throw err
}
})
}
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use((req, res, next) => {
if (req.socket.remoteAddress === "111.34.55.211") {
const error = new Error("Forbidden");
error.status = 403;
return next(error);
}
return next();
})
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
plugins/deny.js
const fp = require('fastify-plugin');
module.exports = fp(async (fastify, opts) => {
fastify.addHook('onRequest', async (request) => {
if(request.ip === "211.133.33.113") {
throw fastify.httpErrors.forbidden();
}
})
})
node -e "http.get('http://localhost:3000/bicycle/1', ({headers}) => console.log(headers))"
node -e "http.get('http://localhost:3000/bicycle/3', (res) => res.setEncoding('utf8').once('data', console.log))"
node -e "http.request('http://localhost:3000/bicycle/1', { method: 'post'}, ({statusCode}) \
=> console.log(statusCode)).end()"
node -e "http.request('http://localhost:3000/bicycle', { method: 'post', headers: {'content-type': 'application/json'}}, (res) => res.setEncoding('utf8').once('data', console.log.bind(null, res.statusCode))).end(JSON.stringify({data: {brand: 'Gazelle', color: 'red'}}))"
node -e "http.request('http://localhost:3000/bicycle/3/update', { method: 'post', headers: {'content-type': 'application/json'}}, (res) => console.log(res.statusCode)).end(JSON.stringify({data: {brand: 'Ampler', color: 'blue'}}))"
node -e "http.request('http://localhost:3000/bicycle/99', { method: 'put', headers: {'content-type': 'application/json'}}, (res) => console.log(res.statusCode)).end(JSON.stringify({data: {brand: 'VanMoof', color: 'black'}}))"
node -e "http.request('http://localhost:3000/bicycle/1', { method: 'delete', headers: {'content-type': 'application/json'}}, (res) => console.log(res.statusCode)).end()"
node -e "http.createServer((_, res) => (res.setHeader('Content-Type', 'text/plain'), res.end('hello world'))).listen(5000)"