Managing Secrets with Node.js SDK
This guide covers how to manage secrets using the Julep Node.js SDK. Secrets allow you to securely store and use sensitive information like API keys, passwords, and access tokens in your Julep applications.
Installation
Ensure you have the latest version of the Node.js SDK:
npm install @julep/sdk
# or
yarn add @julep/sdk
Authentication
Initialize the client with your API key:
import { Julep } from '@julep/sdk' ;
const julep = new Julep ({ apiKey: 'your_api_key' });
Creating Secrets
Create a new secret:
const secret = await julep . secrets . create ({
name: 'stripe_api_key' ,
value: 'sk_test_...' ,
description: 'Stripe API key for payment processing' ,
metadata: { environment: 'production' , owner: 'payments-team' }
});
console . log ( `Created secret: ${ secret . name } ` );
Required Parameters
name
: The name of the secret (must be a valid identifier)
value
: The secret value to encrypt and store
Optional Parameters
description
: A description of what the secret is used for
metadata
: An object of metadata to associate with the secret
Listing Secrets
List all available secrets:
// List all secrets
const secrets = await julep . secrets . list ();
secrets . items . forEach ( secret => {
console . log ( ` ${ secret . name } : ${ secret . description } ` );
});
Use pagination to handle large numbers of secrets:
// List with pagination
const secretsPage1 = await julep . secrets . list ({ limit: 10 , offset: 0 });
const secretsPage2 = await julep . secrets . list ({ limit: 10 , offset: 10 });
Filter secrets based on metadata:
// Filter by metadata
const productionSecrets = await julep . secrets . list ({
metadata: { environment: 'production' }
});
// Filter by multiple metadata fields
const teamSecrets = await julep . secrets . list ({
metadata: {
environment: 'production' ,
owner: 'payments-team'
}
});
Retrieving Secrets
Get a specific secret by name:
const secret = await julep . secrets . get ({ name: 'stripe_api_key' });
console . log ( `Secret: ${ secret . name } , Created: ${ secret . createdAt } ` );
// Access the secret value
console . log ( `Secret value: ${ secret . value } ` );
Note : For security, when listing secrets, the .value
field will always show “ENCRYPTED”. The actual secret value is only returned when specifically requesting a single secret by name.
Updating Secrets
Update an existing secret:
const updatedSecret = await julep . secrets . update ({
name: 'stripe_api_key' ,
value: 'sk_test_new_value...' ,
description: 'Updated Stripe API key' ,
metadata: {
environment: 'production' ,
owner: 'payments-team' ,
rotated: '2025-05-10'
}
});
console . log ( `Updated secret: ${ updatedSecret . name } ` );
Partial Updates
You can update specific fields without changing others:
// Update only the description
const updatedSecret = await julep . secrets . update ({
name: 'stripe_api_key' ,
description: 'New description for Stripe API key'
});
// Update only the metadata
const updatedMetadata = await julep . secrets . update ({
name: 'stripe_api_key' ,
metadata: { lastRotated: '2025-05-10' }
});
Deleting Secrets
Delete a secret when it’s no longer needed:
await julep . secrets . delete ({ name: 'stripe_api_key' });
console . log ( 'Secret deleted' );
Using Secrets in Tasks
Reference secrets when executing tasks:
import { Julep } from '@julep/sdk' ;
const julep = new Julep ({ apiKey: 'your_api_key' });
// Define a task that uses a secret
const taskDefinition = {
steps: [
{
kind: 'tool_call' ,
tool: 'openai' ,
operation: 'chat' ,
arguments: {
model: 'gpt-4' ,
messages: [
{ role: 'user' , content: 'What \' s the weather like?' }
]
},
secret_name: 'openai_api_key'
}
]
};
// Create and execute the task
const task = await julep . tasks . create ({ task: taskDefinition });
const execution = await julep . tasks . execute ({ taskId: task . id });
See all 25 lines
Using Secrets in Expressions
You can reference secrets in expressions:
const taskDefinition = {
steps: [
{
kind: 'transform' ,
expression: "$ f'https://api.example.com/v1?api_key={secrets.api_key}&query={input}'" ,
input: 'search query' ,
output: 'api_url'
}
]
};
Using Multiple Secrets
For tools that require multiple secrets:
const taskDefinition = {
steps: [
{
kind: 'tool_call' ,
tool: 'database' ,
operation: 'query' ,
arguments: {
query: 'SELECT * FROM users' ,
connection: {
host: '$ secrets.db_host' ,
user: '$ secrets.db_username' ,
password: '$ secrets.db_password' ,
database: '$ secrets.db_name'
}
}
}
]
};
Error Handling
Handle common errors when working with secrets:
try {
const secret = await julep . secrets . get ({ name: 'non_existent_secret' });
} catch ( error ) {
if ( error . code === 'not_found' ) {
console . log ( 'Secret not found' );
} else {
console . error ( 'Error retrieving secret:' , error );
}
}
try {
// Attempt to create a secret with an invalid name
const secret = await julep . secrets . create ({ name: 'invalid name' , value: 'test' });
} catch ( error ) {
if ( error . code === 'validation_error' ) {
console . log ( `Validation error: ${ error . message } ` );
} else {
console . error ( 'Error creating secret:' , error );
}
}
try {
// Attempt to create a duplicate secret
const secret = await julep . secrets . create ({ name: 'existing_secret' , value: 'test' });
} catch ( error ) {
if ( error . code === 'conflict' ) {
console . log ( 'Secret already exists' );
} else {
console . error ( 'Error creating secret:' , error );
}
}
See all 31 lines
Secret Rotation Example
Implement a secret rotation policy:
import { v4 as uuidv4 } from 'uuid' ;
/**
* Safely rotate a secret by creating a new one and verifying it works
* before deleting the old one.
*/
async function rotateSecret ( julep , secretName , newValue ) {
// Create a temporary secret with a random suffix
const tempName = ` ${ secretName } _rotation_ ${ uuidv4 (). substring ( 0 , 8 ) } ` ;
// Create the new secret
await julep . secrets . create ({
name: tempName ,
value: newValue ,
description: `Temporary rotation for ${ secretName } ` ,
metadata: { rotationDate: new Date (). toISOString () }
});
// Here you would test that the new secret works
// ...
// If tests pass, update metadata on the old secret
const oldSecret = await julep . secrets . get ({ name: secretName });
const oldMetadata = oldSecret . metadata || {};
await julep . secrets . update ({
name: secretName ,
metadata: {
... oldMetadata ,
archived: 'true' ,
replacedBy: tempName ,
archivedDate: new Date (). toISOString ()
}
});
// Rename the temporary secret to the standard name
await julep . secrets . delete ({ name: secretName });
await julep . secrets . update ({
name: tempName ,
newName: secretName ,
description: oldSecret . description ,
metadata: { lastRotated: new Date (). toISOString () }
});
return await julep . secrets . get ({ name: secretName });
}
// Example usage
async function rotateStripeKey () {
const newKey = 'sk_test_new_value_after_rotation' ;
try {
const rotatedSecret = await rotateSecret ( julep , 'stripe_api_key' , newKey );
console . log ( `Rotated secret: ${ rotatedSecret . name } ` );
} catch ( error ) {
console . error ( 'Error rotating secret:' , error );
}
}
See all 57 lines
Working with Async/Await
All methods in the Node.js SDK return Promises, making them compatible with async/await:
async function manageSecrets () {
try {
// Create a secret
const secret = await julep . secrets . create ({
name: 'database_password' ,
value: 'complex-password-123' ,
description: 'Production database password'
});
// List all secrets
const secrets = await julep . secrets . list ();
// Create task using the secret
const task = await julep . tasks . create ({
steps: [
{
kind: 'tool_call' ,
tool: 'database' ,
operation: 'query' ,
secret_name: 'database_password'
}
]
});
return { secret , secrets , task };
} catch ( error ) {
console . error ( 'Error managing secrets:' , error );
throw error ;
}
}
See all 30 lines
Best Practices
Use a consistent naming convention for all secrets
Add detailed descriptions and metadata to make secrets discoverable
Implement a rotation policy for sensitive secrets
Log secret operations (creation, updates, deletions) but never log values
Use try/catch blocks to handle potential errors gracefully
Consider automating secret rotation for important credentials
Use metadata to track important dates and ownership information
Next Steps