Create a REST API client
PURISTA provides the client builder, which allows you to create a zero dependency, fetch based REST API client.
The generated client will map the http exposed service commands, similar to the service
in the context of commands and subscriptions.
The client builder requires the service definitions as input - see Export Service Definitions.
const client = new HttpClient()
// client.[SERVICE_NAME].v[SERVICE_VERSION].[COMMAND_NAME]
const result = client.foo.v1.bar(payload, parameter)
ClientBuilder
The client builder is very modular to give you the full control.
const config = config = {
version: '1.11.0', // PURISTA version
definitionPath: './definitions', // path of *.json files with definitions
outputPath: './dist', // output folder
httpClient: {
buildAs: 'both', // 'esm' | 'commonjs' | 'both'
clientName: 'HttpClient' // class name of client
},
}
const clientBuilder = new ClientBuilder(config)
// generate the source files
await clientBuilder.generateHttpClient(defnitions)
// cleanup the builder and remove event listeners
clientBuilder.destroy()
Logging
The client builder emits some information, which can be used for logging or for direct cli output.
const config = {
definitionPath: './definitions',
outputPath: './dist',
httpClient: {
buildAs: 'both',
clientName: 'HttpClient',
},
}
const clientBuilder = new ClientBuilder(config)
clientBuilder.on('error', (...args) => console.error(...args))
clientBuilder.on('warn', (...args) => console.warn(...args))
clientBuilder.on('info', (...args) => console.info(...args))
clientBuilder.on('success', (...args) => console.info(...args))
clientBuilder.on('start', (...args) => console.log(...args))
// generate the source files
await clientBuilder.generateHttpClient(defnitions)
// cleanup the builder and remove event listeners
clientBuilder.destroy()
Config file
Instead of using a hardcoded config, or the need to implement config persistence, the client builder comes with some handy functions.
The config file is a simple json file.
{
"version": "1.11.0",
"definitionPath": "./definitions",
"outputPath": "./dist",
"package": {
"name": "@company/http-client",
"description": "my custom client",
"private": "true"
},
"httpClient": {
"buildAs": "both",
"clientName": "HttpClient"
},
}
Write the config file
You can simply export the current configuration.
const config = {
definitionPath: './definitions',
outputPath: './dist',
httpClient: {
buildAs: 'both',
clientName: 'HttpClient',
},
}
const clientBuilder = new ClientBuilder(config)
await clientBuilder.writeConfig()
clientBuilder.destroy()
The .writeConfig
method has an optional parameter.
Per default, the function will try to store the config in purista.client.json
in the current users folder.
Here you can provide a custom folder.
Load the config file
You can load a json config file with .loadConfig()
.
Per default, the function will try to load the config from purista.client.json
in the current users folder.
The method as an optional parameter, where you can provide a custom file location.
Create package
const config = {
version: '1.11.0', // PURISTA version
definitionPath: './definitions', // path of definitions
outputPath: './dist', // output folder
httpClient: {
buildAs: 'both', // 'esm' | 'commonjs' | 'both'
clientName: 'HttpClient' // class name of client
},
}
Complete code
This small code snipped can be used to create your client.
import { ClientBuilder } from '@purista/core'
const generate = async ()=> {
const clientBuilder = new ClientBuilder()
clientBuilder.on('error', (...args) => console.error(...args))
clientBuilder.on('warn', (...args) => console.warn(...args))
clientBuilder.on('info', (...args) => console.info(...args))
clientBuilder.on('success', (...args) => console.info(...args))
clientBuilder.on('start', (...args) => console.log(...args))
// load the config from purista.client.json in current working directory
await clientBuilder.loadConfig()
try {
// load the definitions from exported json files
const defnitions = await clientBuilder.loadDefinitionFiles()
// clear the output folder
await clientBuilder.cleanDistFolder()
// generate the source files
await clientBuilder.generateHttpClient(defnitions)
// add a index.ts with exports to the source files
await clientBuilder.createIndex()
// add a package.json
await clientBuilder.createPackageJson()
// compile the source files
await clientBuilder.build()
} catch (error) {
console.error(error)
} finally {
// cleanup the builder and remove event listeners
clientBuilder.destroy()
}
}
generate()
{
"version": "1.11.0",
"definitionPath": "./definitions",
"outputPath": "./dist",
"package": {
"name": "@company/http-client",
"description": "my custom client",
"private": "true"
},
"httpClient": {
"buildAs": "both",
"clientName": "HttpClient"
},
}
Add your definitions as JSON files in the ./definitions
subfolder.
TIP
In a monorepo, you can directly use the definitions via imports.
import { pingV1Service } from './backend/src/service/ping/v1/index.js'
import { fooV1Service } from './backend/src/service/foo/v1/index.js'
import { barV1Service } from './backend/src/service/bar/v1/index.js'
// ....
// load the definitions from exported json files
const defnitions = await clientBuilder.loadDefinitionFiles()
// use definitions from imported service builders
const serviceBuilders = [pingV1Service, fooV1Service, barV1Service]
const defnitions = await clientBuilder.getDefinitionsFromServiceBuilders(serviceBuilders)
// ....
You can simply run this code:
tsx generate.ts