Skip to content

Inferred Targets

@nx-plugin-openapi/core ships an Nx plugin (createNodesV2, “Project Crystal”) that reads client definition files named openapi-client.json. From each of them it infers:

  • a client project with a cached generate target
  • an update-spec target if the definition has a url
  • the types, api and core libs if split is configured

You don’t need a project.json.

nx.json
{
"plugins": [
{
"plugin": "@nx-plugin-openapi/core/plugin",
"options": {
"generateTargetName": "generate",
"updateSpecTargetName": "update-spec",
"tags": [],
"dependentTargets": ["build", "typecheck"]
}
}
]
}

All options are optional:

OptionDefaultDescription
generateTargetNamegenerateName of the inferred generate target
updateSpecTargetNameupdate-specName of the inferred update-spec target
tags[]Tags added to every inferred project
dependentTargets["build", "typecheck"]Targets of the split libs that run generate first

Use one openapi-client.json per client. All paths are relative to the file.

libs/shared/petstore/openapi-client.json
{
"$schema": "../../../node_modules/@nx-plugin-openapi/core/src/plugin/openapi-client.schema.json",
"name": "petstore",
"spec": "petstore.openapi.json",
"url": "https://petstore3.swagger.io/api/v3/openapi.json",
"headers": { "Authorization": "Bearer ${PETSTORE_TOKEN}" },
"generator": "openapi-tools",
"generatorOptions": { "generatorName": "typescript-angular" },
"split": {
"aliases": {
"types": "@acme/petstore-types",
"api": "@acme/petstore-api",
"core": "@acme/petstore-core"
}
},
"tags": ["scope:shared"]
}
FieldRequiredDescription
namenoClient name. Project names derive from it. Default: the directory name
specyesLocal spec file. This committed file is the source of truth
urlnoRemote spec URL. Adds the update-spec target
headersnoRequest headers for update-spec. Supports ${ENV_VAR}
formatnoraw (default) or json, see update-spec
generatornoopenapi-tools (default), hey-api or a custom plugin package
generatorOptionsnoOptions passed to the generator plugin
outputnoGenerator output dir. Default: src, or generated with split
split.aliaseswith splitImport alias of the types, api and core libs
split.targetRootnoDir receiving the libs. Default: the definition dir
tagsnoTags added to all projects of this client, e.g. scope:shared

For the definition above, nx show projects lists:

ProjectRootTags
petstorelibs/shared/petstorescope:shared
petstore-typeslibs/shared/petstore/typesscope:shared, type:types
petstore-apilibs/shared/petstore/apiscope:shared, type:api
petstore-corelibs/shared/petstore/corescope:shared, type:util

Without split, the client project holds the generated code itself and gets the type:api tag.

Use the fixed type:* tags in @nx/enforce-module-boundaries. Models (type:types) import nothing, core (type:util) imports models, and services (type:api) import both:

eslint.config.js
depConstraints: [
{ sourceTag: 'type:types', onlyDependOnLibsWithTags: ['type:types'] },
{ sourceTag: 'type:util', onlyDependOnLibsWithTags: ['type:types', 'type:util'] },
{ sourceTag: 'type:api', onlyDependOnLibsWithTags: ['type:types', 'type:util', 'type:api'] },
];

Runs the generate-api executor with cache: true:

  • inputs: the definition file and the spec file (plus generatorOptions.configFile if set). The executor hasher also hashes the spec content.
  • outputs: the output dir and, with split, the src dir of every lib.
  • with split, the built-in split post-processor runs after generation.
Terminal window
nx run petstore:generate

Runs the update-spec executor with cache: false. It downloads url into spec. It is not a dependency of generate. Refresh the spec on purpose and commit it.

Terminal window
nx run petstore:update-spec # write the spec
nx run petstore:update-spec --check # fail if the remote spec differs (CI)

Every split lib has an implicit dependency on the client project. Its dependentTargets (default build and typecheck) depend on the client’s generate target, so nx build petstore-api generates first.

The plugin adds this dependency with the '...' spread, so it keeps the dependsOn that other plugins infer, e.g. ^build:

{ "dependsOn": ["...", { "projects": ["petstore"], "target": "generate" }] }

Nx merges inferred projects with existing project.json files of the same root. Explicit configuration wins:

  • A project.json in the client dir can override or extend the generate target. If it sets a name, the libs reference that name.
  • A project.json in a lib dir (e.g. for a build target) is merged with the inferred tags and dependencies.

The split post-processor only writes into <lib>/src. Files such as project.json or tsconfig.json next to it are kept.