Config SDK
The SDK Configuration Package is a robust and customizable configuration management solution that ensures type safety and runtime validation. By bootstrapping with a predefined schema, it significantly reduces the risk of loading invalid configurations, a common and challenging issue to debug at runtime.
Features
- Type Safety: Ensures full type safety when using TypeScript, including code completion for configuration keys.
- Runtime Validation: Validates configuration values at runtime to catch errors early.
- Extensibility: Allows for the addition of custom configuration fields beyond the required ones.
- SDK Integration: Designed to integrate seamlessly with other SDK packages through interfaces for a cohesive development experience.
Installation
To install this package, you need to have access to the private @sdk
npm scope.
Begin by installing the sdk/config
package using your preferred package manager:
npm install @sdk/config
Quickstart
Vanilla JavaScript/TypeScript
Setting up a compatible configuration container is straightforward. Each container requires certain mandatory configuration values to ensure compatibility with other SDK packages. These include:
HOST
: Specifies the runtime host where the application is running (e.g.,https://example.hexagon.com
).APPLICATION_SLUG
: Defines the application's name in a URL-safe slug (opens in a new tab) format (e.g.,my-hxdr-app
).APPLICATION_VERSION
: Indicates the current version of the application using the Semantic Versioning (opens in a new tab) format (e.g.,1.0.0
).
import { Config } from '@sdk/config';
import { version } from '../package.json';
const myConfig = Config.from({
HOST: 'https://example.hexagon.com',
APPLICATION_SLUG: 'my-hxdr-app',
APPLICATION_VERSION: version,
});
React
For React applications, the package provides a helper provider to make the configuration accessible through the React context.
npm install @sdk/config-react react react-dom
import type { ConfigInterface } from '@sdk/config';
import { ConfigProvider } from '@sdk/config-react';
import { myConfig } from './myConfig';
function App() {
return (
<ConfigProvider config={myConfig}>
<Component />
</ConfigProvider>
);
}
function Component() {
const config = useConfig();
return <ChildComponent config={config} />;
}
function ChildComponent({ config }: { config: ConfigInterface }) {
return <h1>Hello {config.get('APPLICATION_SLUG')}!</h1>;
}
Extending the Configuration
To include custom configuration fields, define a schema for these fields first.
import { ConfigField } from '@sdk/config';
export const myConfigSchema = {
FEATURE_FLAGS: ConfigField.set,
};
By defining a schema, you ensure that these custom fields are validated correctly. Here's an example of how to instantiate the config with the custom schema:
const customConfig = Config.from(
{
HOST: 'https://example.hexagon.com',
APPLICATION_SLUG: 'my-hxdr-app',
APPLICATION_VERSION: version,
FEATURE_FLAGS: 'FLAG_1,FLAG_2',
},
myConfigSchema
);
const featureFlags = customConfig.get('FEATURE_FLAGS');
// Returns: Set('FLAG_1', 'FLAG_2')
Config Field Validation
Config fields are validated at runtime to ensure data integrity and prevent common errors.
import { ConfigField } from '@sdk/config';
export const myConfigSchema = {
SOME_KEY: ConfigField.string,
};
const customConfig = Config.from(
{
// Omitted HOST, APPLICATION_SLUG, APPLICATION_VERSION for brevity
SOME_KEY: 12900, // Incorrect type, should be a string
},
myConfigSchema
);
// Throws ConfigParseError: Expected `string` but got `number` for `SOME_KEY`!
Ensuring Type Safety with Custom Configuration in React
To harness the full potential of type safety for custom configurations in React, it's crucial to inform the React hook about the schema used during configuration initialization. By default, the React hook useConfig
only recognizes mandatory values defined by the SDK. However, by specifying the type of your custom schema as an optional type parameter to the hook, you can extend its awareness to include custom configuration fields, ensuring type safety and improving developer experience.
Firstly, define your custom configuration schema and create a corresponding TypeScript type for it. This step is essential for enabling type checking and autocompletion for your custom configuration fields within your React components.
import { ConfigField, ConfigSchema } from '@sdk/config';
export const myConfigSchema = {
SOME_KEY: ConfigField.string,
};
// Generate a TypeScript type from your custom schema
export type MyConfigSchema = ConfigSchema<typeof myConfigSchema>;
Next, utilize the custom type MyConfigSchema
with the useConfig
hook in your React components. This approach ensures that the useConfig
hook is fully aware of the custom schema, allowing for type-safe access to custom configuration values.
import { useConfig } from '@sdk/config-react';
import { MyConfigSchema } from './myConfigSchema';
function Component() {
// Inform the hook about the custom schema for type-safe access
const config = useConfig<MyConfigSchema>();
// Access custom configuration fields with type safety
const someKey = config.get('SOME_KEY');
// `someKey` is now correctly typed as a string
}
Available ConfigField
Validators
The following validators are available to ensure the correct data types are used:
url
: Validates URL strings.semver
: Validates strings against the Semantic Versioning (opens in a new tab) format.slug
: Validates URL-safe slug strings.string
: Validates generic strings.number
: Validates numeric values.set
: Validates sets of values provided by a comma-separated string ("VAL1,VAL2"
).bool
: Validates boolean values.optionalString
: Validates optional string values (can beundefined
).optionalNumber
: Validates optional numeric values (can beundefined
).
Internally these validators are based on the Zod (opens in a new tab) library. Custom validators can be implemented using Zod as a baseline, returning the result of Zods safeParse
method:
const myUuidValidator: ConfigSchemaField</* Expected input type */ unknown, /* Expected valid output type */ string> = {
validator: (value: unknown): SafeParseReturnType<unknown, string> => {
return z.string().uuid().safeParse(value);
},
};