0.1.5 • Published 2 years ago

@biblioteksentralen/cloud-functions-core v0.1.5

Weekly downloads
-
License
ISC
Repository
-
Last release
2 years ago

Core package for Cloud Functions services

This package contains some core functionality for Node.js services running on Google Cloud Run via Cloud Functions, such as logging and error handling.

Development

pnpm install
pnpm test

Publishing the package:

pnpm version patch  # or minor / major
pnpm publish

Initialization

npm install @google-cloud/functions-framework @biblioteksentralen/cloud-functions-core

To create a new Express app for Cloud Functions:

import { http } from "@google-cloud/functions-framework";
import { createApp } from "@biblioteksentralen/cloud-functions-core";
import { z } from "zod";

const projectId = z.string().parse(process.env.GCP_PROJECT_ID);
const app = createApp({ projectId });
http("my-cloud-function", app);

This configures the app with a logging middleware that configures Pino for Cloud Logging and includes trace context. The logger is added to the Express Request context, so it can be used in all request handlers like so:

app.get("/", async (req, res, next) => {
  req.log.info(`Hello log!`);
  // ...
});

The app is also registered with the Cloud Functions framework.

Tip: Formatting logs with pino-pretty in development

When developing, pino-pretty can be used to format logs in a more human readable way. Since cloud-functions-core configures Pino for Google Cloud Logging, we need to explicitly tell pino-pretty about which keys we use with the flags --messageKey and --levelKey. Here's a package.json example (remember to npm i -D pino-pretty):

{
  "scripts": {
    "dev": "functions-framework --target=fun-service --signature-type=http | pino-pretty --colorize --messageKey message --levelKey severity"
  }
}

It's also possible to create a .pino-prettyrc file if the script definition becomes too convoluted.

Error handling

This package provides an errorHandler middleware for error handling in Express 4 inspired by How to Handle Errors in Express with TypeScript. It includes the express-async-errors polyfill to support throwing errors from async request handlers, a feature that will be supported natively in Express 5.

import { http } from "@google-cloud/functions-framework";
import { createApp, errorHandler } from "@biblioteksentralen/cloud-functions-core";
import { z } from "zod";

const projectId = z.string().parse(process.env.GCP_PROJECT_ID);
const app = createApp({ projectId });
http("my-cloud-function", app);

// Define routes, middlewares, etc...
// app.get("/", defaultRequestHandler);
// ...

// Error handler should be added at the very end, after all other handlers.
app.use(errorHandler);

The package provides AppError, a base class to be used for all known application errors (that is, all errors we throw ourselves).

import {
  AppError,
  RequestHandler
} from "@biblioteksentralen/cloud-functions-core";

export const defaultRequestHandler: RequestHandler = async (req, res, next) => {
  // ...
  throw new AppError('🔥 Databasen har brent ned');
  // ...
};

As long as errors are thrown before writing the response has started, a JSON error response is produced on the form:

{ "error": "🔥 Databasen har brent ned" }

By default, errors based on AppError are considered operational and displayed in responses. If an error should not be displayed, set isOperational: false when constructing the error:

throw new AppError('🔥 Databasen har brent ned', { isOperational: false });

This results in a generic error response (but the original error is still logged):

{ "error": "Internal server error" }

A generic error response will also be shown for any unknown error, that is, any error that is not based on AppError) is thrown. All errors, both known and unknown, are logged.

By default, errors use status code 500. To use another status code:

throw new AppError('💤 Too early', { httpStauts: 425 });

The package also provides a few subclasses of AppError for common use cases, such as ClientRequestError (yields 400 response) and Unauthorized (yields 401 response).

throw new Unauthorized('🔒 Unauthorized');

Pub/Sub helpers

The package provides a helper function to parse and validate Pub/Sub messages delivered through push delivery.

The package is agnostic when it comes to which schema parsing/validation library to use. We like Zod for its usability, and have added support for it in the errorHandler, but it's not very performant (https://moltar.github.io/typescript-runtime-type-benchmarks/), so you might pick something else if performance is important (if you do, remember to throw ClientRequestError when validation fails).

import { z } from "zod";
import {
  parsePubSubMessage,
  Request,
  Response,
} from "@biblioteksentralen/cloud-functions-core";

const messageSchema = z.object({
  table: z.string(),
  key: z.number(),
});

app.post("/", async (req: Request, res: Response) => {
  const message = parsePubSubMessage(req, (data) => messageSchema.parse(JSON.parse(data)));
  // ...
});
0.1.2

2 years ago

0.1.1

2 years ago

0.1.4

2 years ago

0.1.3

2 years ago

0.1.5

2 years ago

0.1.0

2 years ago

0.0.4

2 years ago

0.0.3

2 years ago

0.0.2

2 years ago

0.0.1

2 years ago