Connecting a frontend to the NPL API
This guide explains how to use an OpenAPI specification to simplify the interface between a frontend and the NPL API. We
will use the npl openapi command and the hey-api/openapi-ts library to generate type-safe TypeScript clients for
your NPL applications.
Prerequisites
- Noumena CLI installed
- Node.js installed
- Basic understanding of TypeScript
Using an example project
To use an example project with an example application containing a frontend and the client generation steps, run
npl init --project-dir my-project --template-url https://github.com/NoumenaDigital/npl-init/archive/refs/heads/frontend.zip
Generate an OpenAPI specification
NPL provides a built-in command to generate OpenAPI specifications from your protocols. From the root of the directory that was just created, run:
npl openapi
This command uses the structure.sourceDir and structure.outputDir parameters defined in npl.yml to locate your NPL
source files and define the output directory for the generated OpenAPI files. You can also specify paths using the
--source-dir and --output-dir command-line arguments.
The npl openapi command analyzes your NPL code and generates a comprehensive OpenAPI specification that includes:
- All protocol endpoints
- Request/response schemas
- Authentication requirements
- Parameter definitions
- Engine root URL
For more details, see NPL Application API.
Install OpenAPI TypeScript generator
Navigate to the frontend module if needed:
cd frontend
Install the hey-api/openapi-ts library to generate type-safe clients:
npm install -D @hey-api/openapi-ts
Generate type-safe client
From the frontend folder, create a client generation script or run directly:
npx @hey-api/openapi-ts -i openapi/*-openapi.* -o ./src/clients/document
This generates:
- TypeScript interfaces for all data models
- Type-safe API client functions
- Request/response type definitions
Note
If the NPL applications contains multiple packages, each corresponding openapi file will require
an individual client generation. In this case, replace package-one with your package names twice
in the text, and use
npx @hey-api/openapi-ts -i openapi/package-one-openapi.* -o ./src/clients/package-one
Using the generated client
Basic client setup
In your frontend, call to the api are made through the client object. The baseUrl must be configured to reach the
NPL Engine.
import { client } from '../clients/document/client.gen' // "document" is the NPL package name; adjust name and path as needed
// For applications on NOUMENA Cloud
const baseUrl = "https://engine-TENANT_SLUG-APP_SLUG.noumenacloud.com"
// For local applications
// const baseUrl = "http://localhost:12000"
client.setConfig({ baseUrl })
For local development, the url would be http://localhost:12000 in ENGINE_DEV_MODE or http://localhost:12001 with a
proxy. In the example project, baseUrl is set parametrically at construction of service DocumentService and
controlled by environment variables.
Making API calls
The generated client provides type-safe methods for all your NPL endpoints:
// Import the function, adjust the function name and the path as needed
import { getDocumentList } from '../clients/document/sdk.gen'
// List all protocol instances
getDocumentList({
client: api,
...withAuthorizationHeader()
})
// Create a new protocol instance
createDocument({
body: {
content: content,
['@parties']: {
editor: {
claims: {
email: [user.email]
}
},
approver: {
claims: {
email: [approverEmail]
}
}
}
},
client: api,
...withAuthorizationHeader()
})
// Calling a permission
documentEdit({
client: api,
path: {
id: documentId
},
body: {
newContent: newContent
},
...withAuthorizationHeader()
})
The withAuthorizationHeader() is a convenience function, defined in the non-generated example code (DocumentService
service), that adds the necessary authorization header to the request:
export class DocumentService {
// ...
withAuthorizationHeader = () => {
return {
headers: { Authorization: `Bearer ${this.authProvider.token}` }
}
}
}
The API call functions are asynchronous and can be awaited
await documentEdit(/* */)
// continuation when the call is completed
and/or used with promise chaining
documentEdit(/* */).then(/* follow-up function call */)
Benefits of using generated clients
- Type Safety: Full TypeScript support with compile-time error checking
- Auto-completion: IDE support for all available endpoints and parameters
- Documentation: Generated JSDoc comments from OpenAPI specs
- Consistency: Standardized error handling and response formats
- Maintainability: Automatic updates when your NPL protocols change
Updating your client
When you update your NPL protocols, regenerate your client:
# 1. From the project root, regenerate OpenAPI spec
npl openapi
# 2. From the frontend module, regenerate client
cd frontend && npx @hey-api/openapi-ts -i openapi/*-openapi.* -o ./src/clients/document
# 3. Update your application code if needed
Run your full stack application
To run your full stack application locally, you need to start both the NPL Engine and the frontend development server.
Start the NPL Engine in DEV_MODE
From your project root directory, start the NPL Engine in development mode:
docker compose up -d --force-recreate --wait
npl deploy --clear
The engine will start on http://localhost:12000 and the embedded OIDC server on http://localhost:11000.
Build and run the frontend
-
Navigate to your frontend directory:
cd frontend -
Install dependencies if you haven't already:
npm install -
Ensure the baseUrl points to the local engine service. In the snippet provided in the Basic client setup section, this amounts to setting
const baseUrl = "http://localhost:12000"If you are using the example application created in section Using an example project, you do not have to change any code for local deployment, your frontend will be configured automatically to use
http://localhost:12000for the baseUrl (connection to the engine) andhttp://localhost:11000for the URL of the OIDC (authentication), and you can proceed to the next step. -
Start the development server:
npm run devThe frontend will typically start on
http://localhost:5173(or another port if 5173 is in use).
Verify the connection
Open your browser to the frontend URL (e.g., http://localhost:5173) and verify that:
- The frontend loads successfully
- You can log in with
DEV MODE credentials
(example:
alicewithpassword123) - You can create and interact with protocol instances
Configuring for other deployment targets and login modes
The frontend example from the npl-init repository, frontend branch can be configured to connect to NPL Engines running locally or on NOUMENA Cloud. Similarly, it supports authenticating through keycloak, to a full OIDC client, or to the OIDC client embedded in the engine running in DEV MODE.
For more details, check out the
README of the npl-init repository, frontend branch,
also available in your repository if you have created your project from the frontend template (same command as above):
npl init --project-dir my-project --templateUrl https://github.com/NoumenaDigital/npl-init/archive/refs/heads/frontend.zip
Next steps
- Deploy your frontend to Noumena Cloud
- Connect any service to the NPL API
- Listen & react to events from the NPL Runtime