koa#Middleware TypeScript Examples
The following examples show how to use
koa#Middleware.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: app.dev.ts From nodestatus with MIT License | 6 votes |
createMiddleware = async (name: string, publicPath: string): Promise<Middleware> => {
const vite = await createViteServer({
root: path.dirname(require.resolve(`${name}/package.json`)),
server: {
hmr: {
port: ++port
},
middlewareMode: 'html'
}
});
return async (ctx, next) => {
const { url } = ctx;
if (url.startsWith('/api') || !url.startsWith(publicPath)) {
await next();
} else {
await c2k(vite.middlewares)(ctx, next);
}
};
}
Example #2
Source File: watchServedFilesMiddleware.ts From web with MIT License | 6 votes |
/**
* Sets up a middleware which tracks served files and sends a reload message to any
* active browsers when any of the files change.
*/
export function watchServedFilesMiddleware(fileWatcher: FSWatcher, rootDir: string): Middleware {
return async (ctx, next) => {
await next();
if (ctx.response.status !== 404) {
let filePath = getRequestFilePath(ctx.url, rootDir);
// if the request ends with a / it might be an index.html, check if it exists
// and watch it
if (filePath.endsWith('/')) {
filePath += 'index.html';
}
// watch file if it exists
fs.stat(filePath, (err, stats) => {
if (!err && !stats.isDirectory()) {
fileWatcher.add(filePath);
}
});
}
};
}
Example #3
Source File: serveFilesMiddleware.ts From web with MIT License | 6 votes |
/**
* Creates multiple middleware used for serving files.
*/
export function serveFilesMiddleware(rootDir: string): Middleware[] {
const koaStaticOptions: KoaStaticOptions = {
hidden: true,
defer: true,
brotli: false,
gzip: false,
setHeaders(res) {
res.setHeader('cache-control', 'no-cache');
},
};
// the wds-root-dir parameter indicates using a different root directory as a path relative
// from the regular root dir or as an absolute path
const serveCustomRootDirMiddleware: Middleware = async (ctx, next) => {
if (isOutsideRootDir(ctx.path)) {
const { normalizedPath, newRootDir } = resolvePathOutsideRootDir(ctx.path, rootDir);
await send(ctx, normalizedPath, { ...koaStaticOptions, root: newRootDir });
return;
}
return next();
};
// serve static files from the regular root dir
return [serveCustomRootDirMiddleware, koaStatic(rootDir, koaStaticOptions)];
}
Example #4
Source File: pluginMimeTypeMiddleware.ts From web with MIT License | 6 votes |
/**
* Sets up a middleware which allows plugins to resolve the mime type.
*/
export function pluginMimeTypeMiddleware(logger: Logger, plugins: Plugin[]): Middleware {
const mimeTypePlugins = plugins.filter(p => 'resolveMimeType' in p);
if (mimeTypePlugins.length === 0) {
// nothing to transform
return (ctx, next) => next();
}
return async (context, next) => {
await next();
if (context.status < 200 || context.status >= 300) {
return undefined;
}
for (const plugin of mimeTypePlugins) {
const result = await plugin.resolveMimeType?.(context);
const type = typeof result === 'object' ? result.type : result;
if (type) {
logger.debug(`Plugin ${plugin.name} resolved mime type of ${context.path} to ${type}`);
context.type = type;
}
}
};
}
Example #5
Source File: pluginFileParsedMiddleware.ts From web with MIT License | 6 votes |
/**
* Calls fileParsed hook on plugins.
*/
export function pluginFileParsedMiddleware(plugins: Plugin[]): Middleware {
const fileParsedPlugins = plugins.filter(p => 'fileParsed' in p);
if (fileParsedPlugins.length === 0) {
// nothing to call
return (ctx, next) => next();
}
return async (context, next) => {
await next();
if (context.status < 200 || context.status >= 300) {
return undefined;
}
for (const plugin of fileParsedPlugins) {
plugin.fileParsed?.(context);
}
};
}
Example #6
Source File: etagCacheMiddleware.ts From web with MIT License | 6 votes |
/**
* Returns 304 response for cacheable requests if etag matches
*/
export function etagCacheMiddleware(): Middleware {
return async (ctx, next) => {
await next();
if (!ctx.body || ctx.status === 304) {
return;
}
if (!['GET', 'HEAD'].includes(ctx.method)) {
return;
}
if (ctx.status < 200 || ctx.status >= 300) {
return;
}
// let koa check if the respone is still fresh this means
// the etags of the request and response match, so the browser
// still has a fresh copy so it doesn't need to re-download it
if (ctx.fresh) {
ctx.status = 304;
ctx.response.remove('content-type');
ctx.response.remove('content-length');
}
// don't send last-modified since it doesn't do microseconds, we already
// generate an etag based on the timestamp from the filesystem
ctx.response.remove('last-modified');
};
}
Example #7
Source File: basePathMiddleware.ts From web with MIT License | 6 votes |
/**
* Creates middleware which strips a base path from each request
*/
export function basePathMiddleware(basePath: string): Middleware {
const pathToStrip = basePath.endsWith('/')
? basePath.substring(0, basePath.length - 1)
: basePath;
return (ctx, next) => {
if (ctx.url.startsWith(pathToStrip)) {
ctx.url = ctx.url.replace(pathToStrip, '');
}
return next();
};
}
Example #8
Source File: adapter.ts From farrow with MIT License | 6 votes |
adapter = (httpPipeline: HttpPipeline): Middleware => {
return (ctx, next) => {
return httpPipeline.handle(ctx.req, ctx.res, {
onLast: () => {
return next()
},
})
}
}
Example #9
Source File: web.ts From nodestatus with MIT License | 6 votes |
modifyOrder: Middleware = async ctx => {
const { order = [] } = ctx.request.body as { order: number[] };
if (!order.length) {
ctx.status = 400;
ctx.body = createRes(1, 'Wrong request');
return;
}
await handleRequest(ctx, updateOrder(order.join(',')));
}
Example #10
Source File: web.ts From nodestatus with MIT License | 6 votes |
removeServer: Middleware = async ctx => {
const { username = '' } = ctx.params;
if (!username) {
ctx.status = 400;
ctx.body = createRes(1, 'Wrong request');
return;
}
await handleRequest(ctx, deleteServer(username));
}
Example #11
Source File: web.ts From nodestatus with MIT License | 6 votes |
addServer: Middleware = async ctx => {
const data = ctx.request.body;
if (!data) {
ctx.status = 400;
ctx.body = createRes(1, 'Wrong request');
return;
}
if (Object.hasOwnProperty.call(data, 'data')) {
try {
const d = JSON.parse(data.data);
await handleRequest(ctx, bulkCreateServer(d));
} catch (error: any) {
ctx.status = 400;
ctx.body = createRes(1, 'Wrong request');
}
} else {
await handleRequest(ctx, createServer(data));
}
}
Example #12
Source File: web.ts From nodestatus with MIT License | 6 votes |
setServer: Middleware = async ctx => {
const { username } = ctx.request.body;
const { data } = ctx.request.body;
if (!username || !data) {
ctx.status = 400;
ctx.body = createRes(1, 'Wrong request');
return;
}
if (username === data.username) delete data.username;
await handleRequest(ctx, updateServer(username, data));
}
Example #13
Source File: web.ts From nodestatus with MIT License | 5 votes |
removeEvent: Middleware = async ctx => {
if (ctx.params.id) {
await handleRequest(ctx, deleteEvent(Number(ctx.params.id)));
} else {
await handleRequest(ctx, deleteAllEvents());
}
}
Example #14
Source File: web.ts From nodestatus with MIT License | 5 votes |
queryEvents: Middleware = async ctx => {
const size = Number(ctx.query.size) || 10;
const offset = Number(ctx.query.offset) || 0;
await handleRequest(ctx, readEvents(size, offset).then(([count, list]) => ({ count, list })));
}
Example #15
Source File: historyApiFallbackMiddleware.ts From web with MIT License | 5 votes |
/**
* Serves index.html when a non-file request within the scope of the app index is made.
* This allows SPA routing.
*/
export function historyApiFallbackMiddleware(
appIndex: string,
rootDir: string,
logger: Logger,
): Middleware {
const resolvedAppIndex = path.resolve(appIndex);
const relativeAppIndex = path.relative(rootDir, resolvedAppIndex);
const appIndexBrowserPath = `/${toBrowserPath(relativeAppIndex)}`;
const appIndexBrowserPathPrefix = path.dirname(appIndexBrowserPath);
return (ctx, next) => {
if (ctx.method !== 'GET' || path.extname(ctx.path)) {
// not a GET, or a direct file request
return next();
}
if (!ctx.headers || typeof ctx.headers.accept !== 'string') {
return next();
}
if (ctx.headers.accept.includes('application/json')) {
return next();
}
if (!(ctx.headers.accept.includes('text/html') || ctx.headers.accept.includes('*/*'))) {
return next();
}
if (!ctx.url.startsWith(appIndexBrowserPathPrefix)) {
return next();
}
// rewrite url and let static serve take it further
logger.debug(`Rewriting ${ctx.url} to app index ${appIndexBrowserPath}`);
ctx.url = appIndexBrowserPath;
return next();
};
}
Example #16
Source File: pluginServeMiddleware.ts From web with MIT License | 5 votes |
/**
* Sets up a middleware which allows plugins to serve files instead of looking it up in the file system.
*/
export function pluginServeMiddleware(logger: Logger, plugins: Plugin[]): Middleware {
const servePlugins = plugins.filter(p => 'serve' in p);
if (servePlugins.length === 0) {
// nothing to serve
return (ctx, next) => next();
}
return async (context, next) => {
for (const plugin of servePlugins) {
const response = await plugin.serve?.(context);
if (typeof response === 'object') {
if (response.body == null) {
throw new Error(
'A serve result must contain a body. Use the transform hook to change only the mime type.',
);
}
context.body = response.body;
if (response.type != null) {
context.type = response.type;
} else {
context.type = path.extname(path.basename(context.path));
}
if (response.headers) {
for (const [k, v] of Object.entries(response.headers)) {
context.response.set(k, v);
}
}
logger.debug(`Plugin ${plugin.name} served ${context.path}.`);
context.status = 200;
return;
} else if (typeof response === 'string') {
context.body = response;
context.type = path.extname(path.basename(context.path));
logger.debug(`Plugin ${plugin.name} served ${context.path}.`);
context.status = 200;
return;
}
}
return next();
};
}
Example #17
Source File: web.ts From nodestatus with MIT License | 5 votes |
getListServers: Middleware = async ctx => {
await handleRequest(ctx, readServersList().then(data => data.sort((x, y) => y.order - x.order)));
}
Example #18
Source File: app.dev.ts From nodestatus with MIT License | 5 votes |
middlewares: Record<string, Middleware> = {}
Example #19
Source File: createMiddleware.ts From web with MIT License | 5 votes |
/**
* Creates middlewares based on the given configuration. The middlewares can be
* used by a koa server using `app.use()`:
*/
export function createMiddleware(
config: DevServerCoreConfig,
logger: Logger,
fileWatcher: FSWatcher,
) {
const middlewares: Middleware[] = [];
middlewares.push(async (ctx, next) => {
logger.debug(`Receiving request: ${ctx.url}`);
await next();
logger.debug(`Responding to request: ${ctx.url} with status ${ctx.status}`);
});
// strips a base path from requests
if (config.basePath) {
middlewares.push(basePathMiddleware(config.basePath));
}
// adds custom user's middlewares
for (const m of config.middleware ?? []) {
middlewares.push(m);
}
// watch files that are served
middlewares.push(watchServedFilesMiddleware(fileWatcher, config.rootDir));
// serves 304 responses if resource hasn't changed
middlewares.push(etagCacheMiddleware());
// adds etag headers for caching
middlewares.push(koaEtag());
// serves index.html for non-file requests for SPA routing
if (config.appIndex) {
middlewares.push(historyApiFallbackMiddleware(config.appIndex, config.rootDir, logger));
}
const plugins = config.plugins ?? [];
middlewares.push(pluginFileParsedMiddleware(plugins));
middlewares.push(pluginTransformMiddleware(logger, config, fileWatcher));
middlewares.push(pluginMimeTypeMiddleware(logger, plugins));
middlewares.push(pluginServeMiddleware(logger, plugins));
middlewares.push(...serveFilesMiddleware(config.rootDir));
return middlewares;
}
Example #20
Source File: pluginTransformMiddleware.ts From web with MIT License | 4 votes |
/**
* Sets up a middleware which allows plugins to transform files before they are served to the browser.
*/
export function pluginTransformMiddleware(
logger: Logger,
config: DevServerCoreConfig,
fileWatcher: FSWatcher,
): Middleware {
const cache = new PluginTransformCache(fileWatcher, config.rootDir);
const transformPlugins = (config.plugins ?? []).filter(p => 'transform' in p);
if (transformPlugins.length === 0) {
// nothing to transform
return (ctx, next) => next();
}
return async (context, next) => {
// The cache key is the request URL plus any specific cache keys provided by plugins.
// For example plugins might do different transformations based on user agent.
const cacheKey =
context.url +
(await Promise.all(transformPlugins.map(p => p.transformCacheKey?.(context))))
.filter(_ => _)
.join('_');
const result = await cache.get(cacheKey);
if (result) {
context.body = result.body;
for (const [k, v] of Object.entries(result.headers)) {
context.response.set(k, v);
}
logger.debug(`Serving cache key "${cacheKey}" from plugin transform cache`);
return;
}
await next();
if (context.status < 200 || context.status >= 300) {
return;
}
try {
// ensure response body is turned into a string or buffer
await getResponseBody(context);
let disableCache = false;
let transformedCode = false;
for (const plugin of transformPlugins) {
const result = await plugin.transform?.(context);
if (typeof result === 'object') {
disableCache = result.transformCache === false ? true : disableCache;
if (result.body != null) {
context.body = result.body;
transformedCode = true;
logger.debug(`Plugin ${plugin.name} transformed ${context.path}.`);
}
if (result.headers) {
for (const [k, v] of Object.entries(result.headers)) {
context.response.set(k, v);
}
}
} else if (typeof result === 'string') {
context.body = result;
transformedCode = true;
logger.debug(`Plugin ${plugin.name} transformed ${context.path}.`);
}
}
if (transformedCode && !disableCache) {
logger.debug(`Added cache key "${cacheKey}" to plugin transform cache`);
const filePath = getRequestFilePath(context.url, config.rootDir);
cache.set(
filePath,
context.body,
context.response.headers as Record<string, string>,
cacheKey,
);
}
} catch (error) {
if (error instanceof RequestCancelledError) {
return undefined;
}
context.body = 'Error while transforming file. See the terminal for more information.';
context.status = 500;
if (error.name === 'PluginSyntaxError') {
logger.logSyntaxError(error);
return;
}
if (error.name === 'PluginError') {
logger.error(error.message);
return;
}
throw error;
}
};
}