@biblioteksentralen/cloud-run-core
Version:
Core package for NodeJS services using Cloud Run
191 lines (177 loc) • 6.1 kB
TypeScript
import pino, { Logger, ChildLoggerOptions, Bindings } from 'pino';
export { Bindings, ChildLoggerOptions, Logger } from 'pino';
import { Application, RouterOptions, IRouter, Request as Request$1, RequestHandler } from 'express';
export { Handler, IRouter, NextFunction, RequestHandler, Response, RouterOptions } from 'express';
import { HttpTerminator } from 'http-terminator';
import { TypedEmitter } from 'tiny-typed-emitter';
import * as Sentry from '@sentry/node';
type CloudRunServiceOptions = {
router: Application;
logger: Logger;
port: number;
name: string;
instanceId: string;
useSentry?: boolean;
};
interface CloudRunServiceEvents {
shutdown: () => void;
}
declare class CloudRunService extends TypedEmitter<CloudRunServiceEvents> {
readonly name: string;
readonly instanceId: string;
readonly router: Application;
readonly port: number;
readonly logger: Logger;
private readonly useSentry?;
protected httpTerminator?: HttpTerminator;
private state;
constructor({ router, logger, port, name, instanceId, useSentry }: CloudRunServiceOptions);
start(): Promise<void>;
stop(): Promise<void>;
}
type SentryOptions = Sentry.NodeOptions;
interface CreateServiceOptions {
/**
* The port to listen on. Defaults to the PORT environment variable, or 8080.
*/
port?: number;
/**
* The Google Cloud project ID. Used in logs to connect logs to traces.
*/
projectId?: string;
/**
* Options to be passed to the root Pino logger.
*/
logOptions?: ChildLoggerOptions;
/**
* Additional bindings to be added to the root Pino logger.
*/
logBindings?: Bindings;
/**
* Whether to parse incoming request bodies using
* [body-parser](https://www.npmjs.com/package/body-parser). Defaults to true.
*/
parseBody?: boolean;
/**
* Maximum incoming request body size. Defaults to "512mb". Only used if
* `parseBody` is true.
*/
requestLimit?: string;
/**
* Sentry options. If set, Sentry will be configured for error reporting and tracing.
*/
sentry?: SentryOptions;
/**
* Whether the service should handle automatically handle process events
* (SIGTERM, SIGINT, uncaughtException). Defaults to true, but can be disabled
* if more fine-grained process control is needed.
*/
handleProcessEvents?: boolean;
/**
* Socket timeout in milliseconds (default: none).
*/
timeoutMsecs?: number;
}
declare const createService: (name: string, options?: CreateServiceOptions) => CloudRunService;
declare const createRouter: (options?: RouterOptions) => IRouter;
declare enum HttpCode {
OK = 200,
ACCEPTED = 202,
NO_CONTENT = 204,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
NOT_FOUND = 404,
REQUEST_TIMEOUT = 408,
INTERNAL_SERVER_ERROR = 500,
SERVICE_UNAVAILABLE = 503
}
type AppErrorOptions = ErrorOptions & {
httpCode?: HttpCode;
isOperational?: boolean;
};
declare class AppError extends Error {
/**
* HTTP code to be used when responding to the client.
*/
readonly httpCode: HttpCode;
/**
* Whether the error is operational, i.e. whether it is safe to return the error message to the client.
*/
readonly isOperational: boolean;
constructor(message: string, options?: AppErrorOptions);
}
type ErrorDetail = {
code: string;
message?: string;
};
declare class ClientRequestError extends AppError {
/**
* Request body that caused the error
*/
request?: unknown;
/**
* List of validation issues
*/
details?: ErrorDetail[];
constructor(message: string, args?: AppErrorOptions & {
request?: unknown;
details?: ErrorDetail[];
});
}
declare class Unauthorized extends AppError {
constructor(message: string, args?: AppErrorOptions);
}
declare const isAppError: (error: unknown) => error is AppError;
declare function assertIsError(error: unknown): asserts error is Error;
declare function parsePubSubMessage(req: Request$1): PubSubMessage;
type PubSubMessage = {
data: string;
metadata: {
deliveryAttempt?: number;
messageId: string;
subscription: string;
publishTime?: string;
attributes?: Record<string, unknown>;
orderingKey?: string;
};
};
declare const logLevels: readonly ["fatal", "error", "warn", "info", "debug", "trace", "silent"];
type LogLevel = (typeof logLevels)[number];
/**
* Pino logger that formats logs as JSON for ingest by Cloud Logging when running in GCP Cloud Run,
* and uses pino-pretty for human readability otherwise.
*/
declare const logger: pino.Logger<never, boolean>;
declare global {
namespace Express {
interface Request {
log: Logger;
}
}
}
type LoggerProps = {
projectId?: string;
bindings?: Bindings;
options?: ChildLoggerOptions;
};
declare const createPinoLoggerMiddleware: ({ projectId, options, bindings }: LoggerProps) => RequestHandler;
interface Request extends Request$1 {
/**
* Pino Logger configured for Google Cloud Logging
*/
log: Logger;
}
declare function parseTraceparent(traceparent: string | undefined): {
traceId: string | undefined;
spanId: string | undefined;
};
/**
* Get the client IP address when behind a GCP load balancer.
*
* When behind a load blancer, the client IP address is the penultimate element in the
* comma separated x-forwarded-for header.
* Ref: https://cloud.google.com/load-balancing/docs/https#x-forwarded-for_header
*/
declare const getClientIpAddress: (req: Request$1) => string | undefined;
declare const isCloudRunEnvironment: () => boolean;
export { AppError, ClientRequestError, CloudRunService, type CreateServiceOptions, HttpCode, type LogLevel, type PubSubMessage, type Request, type SentryOptions, Unauthorized, assertIsError, createPinoLoggerMiddleware, createRouter, createService, getClientIpAddress, isAppError, isCloudRunEnvironment, logger, parsePubSubMessage, parseTraceparent };