Release notes v0.0.171

Another release packed with a bunch of dependency updates, fixes and of course some features. Although we have some breaking changes. That is not the major reason for these release notes. Read on to check what it is.

The small changes

  • AppError#format know supports formatting all kinds of primitives in addition to Error and AppError instances. This is useful when an underlying library throws a string instead of an Error.
  • We enabled running Compas tests on Node.js 17. In the near future we will also run our lint & benchmarks on Node.js 17. Note that even though we run our tests there, it is not officially supported.
  • Code generators now print out an error if enabledGroups contain a group that is not included in the known structure. This can help spot typo's or left over artifacts.
  • Include a React import in the common/reactQuries.tsx file, this isn't used but required in some environments.

The breaking changes

Let's start with a breaking change in the way uploaded files are represented in the backend. We updated Formidable to 2.0.1 after being on a canary version for too long. This introduced some property renames:

  • ctx.validatedFiles.xxx.path -> ctx.validatedFiles.xxx.filepath
  • ctx.validatedFiles.xxx.name -> ctx.validatedFiles.xxx.originalFilename
  • ctx.validatedFiles.xxx.type -> ctx.validatedFiles.xxx.mimetype

The easiest way to update these changes, is to globally search for ctx.validatedFiles and trace your usages from there. Note that you also need to regenerate this the validator know also checks on these new properties.

The second breaking change has to do with the AbortSignal and support for it in Axios & react-query. All api client functions now optionally accept a partial AxiosRequestConfig, currently limited to signal, that is passed to Axios directly. This also affects the generated react-query based hooks. Luckily for us, react-query now supports creating an AbortSignal for us in the queryFn, so we use that to provide to the api client functions. This also means that we don't have to add cancelToken to the UseQueryOptions, so that is removed as well.

The features

After already having a way to extend a Compas structure with an imported OpenAPI specification via app.extendWithOpenApi. It is now possible to convert a Compas structure as to the OpenAPI v3.0.3 specification. Currently, the minimal set is implemented to create a correct and complete definition (with most Compas features covered). Later on, we want to extend the exporter with more OpenAPI specific features (e.g. security methods/tags per endpoints, base urls, various http schema's, etc.), but those need more configuration (within projects) and most likely vary a lot between project implementations.

As said before most Compas types are implemented, such as:

  • HTTP methods: GET, PUT, POST, PATCH, DELETE
  • Params, Query, Body and Response types
    • References are hoisted to OpenAPI component objects
    • Including file upload and download support
  • Spec information:
    • Title, version, description
  • Pass-through settings (for now only servers)
  • Compas groups
    • When no groups provided all groups within the inputPath structure are exposed
    • When a type is referenced but not part of one of the enabledGroups, the reference is resolved and included in the output. This is the same behaviour as all other generate calls that accept an enabledGroups option.

To enable this feature, the following code snippet could be applied:

// new or existing App instance
const app = new App({ verbose: true });

await app.generateOpenApi({
  // Path that is previously used as `outputDirectory`
  inputPath: "./src/generated",
  outputFile: "./docs/openapi.json",
  enabledGroups: ["exampleGroup"],
  openApiOptions: {
    version: "1.0.0",
    title: "Title of specification",
    description: "Lorem Ipsum",
    servers: [
      {
        url: "https://locationToApi",
      },
    ],
  },
});

This will write the OpenAPI based specification to docs/openapi.json. To expose this file via a custom route, you can use something like:

// gen
R.get("/docs/openapi", "docsOpenAPI").response({ spec: T.any() });

// src
const openapiSpec = JSON.parse(
  await readFile(pathJoin(process.cwd(), "docs/openapi.json"), "utf-8"),
);

groupHandeler.docsOpenAPI = async (ctx, next) => {
  ctx.body = { spec: openapiSpec };

  return next();
};

In closing

We are now able to import & export & import OpenAPI specs again and again. We hope you use this feature wisely 😉 The next release, if we don't need a bug fix release (A), is going to contain JWT based sessions backed by @compas/store. If you have any usages not covered by the current sessions and want Compas to support them, please add it to Issue 1280 and if you are curious what the new implementation looks like, feel free to check out out the PR.