Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support for `trackingOptions` property in Message responses when using `fields=include_tracking_options`
- Support for `rawMime` property in Message responses when using `fields=raw_mime`
- `MessageTrackingOptions` interface for tracking message opens, thread replies, link clicks, and custom labels
- Support for `singleLevel` query parameter in `ListFolderQueryParams` for Microsoft accounts to control folder hierarchy traversal

### Fixed
- Fixed 3MB payload size limit to consider total request size (message body + attachments) instead of just attachment size when determining whether to use multipart/form-data encoding
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This directory contains examples of how to use the Nylas Node.js SDK to interact

- [Notetakers](./notetakers/README.md) - Examples of how to use the Nylas Notetakers API to invite a Notetaker bot to meetings, get recordings and transcripts, and more.
- [Messages](./messages/README.md) - Examples of how to use the Nylas Messages API to list, find, send, update messages, and work with new features like tracking options and raw MIME data.
- [Folders](./folders/README.md) - Examples of how to use the Nylas Folders API, including the new `singleLevel` parameter for Microsoft accounts.

## Running the Examples

Expand Down
114 changes: 114 additions & 0 deletions examples/folders/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Nylas Folders API Examples

This directory contains examples of how to use the Nylas Folders API with the Nylas Node.js SDK, including the new `singleLevel` query parameter.

## What is the singleLevel Parameter?

The `singleLevel` parameter is a new query parameter for the "list all folders" endpoint that controls folder hierarchy traversal:

- **`singleLevel: true`** - Retrieves folders from a single-level hierarchy only (direct children)
- **`singleLevel: false`** - Retrieves folders across a multi-level hierarchy (all descendants, default behavior)
- **Microsoft accounts only** - This parameter is ignored for other providers

## Examples

- [folders.ts](./folders.ts) - A comprehensive example showing how to use the `singleLevel` parameter in various scenarios

## Running the Examples

To run these examples, you'll need to:

1. Install dependencies from the examples directory:
```bash
cd examples
npm install
```

2. Copy the `.env.example` file to `.env` if you haven't already and add your credentials:
```bash
cp .env.example .env
# Edit .env with your editor
```

3. Edit the `.env` file to include:
- `NYLAS_API_KEY` - Your Nylas API key
- `NYLAS_API_URI` (optional) - The Nylas API server URI (defaults to "https://api.us.nylas.com")
- `NYLAS_GRANT_ID` - The Grant ID for a Microsoft account to see the `singleLevel` parameter in action

4. Run the example:
```bash
# From the examples directory
npx ts-node folders/folders.ts

# Or if you add it to package.json scripts
npm run folders
```

## Understanding the Example

This example demonstrates:

1. **Default Behavior**: Listing all folders with multi-level hierarchy (default)
2. **Single-Level Listing**: Using `singleLevel: true` to get only direct children
3. **Combined Parameters**: Using `singleLevel` with `parentId` to control the starting point
4. **Comparison**: Side-by-side comparison of single-level vs multi-level results
5. **Parameter Variations**: All possible combinations of the `singleLevel` parameter

## Use Cases

The `singleLevel` parameter is particularly useful when:

- **Building UI Navigation**: You want to show only immediate child folders in a tree view
- **Performance Optimization**: Reducing the amount of data returned when you only need direct children
- **Hierarchical Processing**: Processing folder structures level by level rather than all at once
- **Microsoft-Specific Features**: Taking advantage of Microsoft's folder hierarchy capabilities

## API Reference

### ListFolderQueryParams Interface

```typescript
interface ListFolderQueryParams extends ListQueryParams {
/**
* (Microsoft and EWS only.) Use the ID of a folder to find all child folders it contains.
*/
parentId?: string;

/**
* (Microsoft only) If true, retrieves folders from a single-level hierarchy only.
* If false, retrieves folders across a multi-level hierarchy.
* @default false
*/
singleLevel?: boolean;
}
```

### Example Usage

```typescript
import Nylas from 'nylas';

const nylas = new Nylas({ apiKey: 'your-api-key' });

// Get only direct children of a specific folder
const singleLevelFolders = await nylas.folders.list({
identifier: 'grant-id',
queryParams: {
parentId: 'parent-folder-id',
singleLevel: true
}
});

// Get all descendants (default behavior)
const allFolders = await nylas.folders.list({
identifier: 'grant-id',
queryParams: {
parentId: 'parent-folder-id',
singleLevel: false // or omit this parameter
}
});
```

## Documentation

For more information, see the [Nylas API Documentation](https://developer.nylas.com/).
218 changes: 218 additions & 0 deletions examples/folders/folders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import dotenv from 'dotenv';
import path from 'path';
import * as process from 'process';
import Nylas, {
Folder,
NylasResponse,
NylasListResponse,
NylasApiError,
ListFolderQueryParams
} from 'nylas';

// Load environment variables from .env file
dotenv.config({ path: path.resolve(__dirname, '../.env') });

const NYLAS_API_KEY = process.env.NYLAS_API_KEY;
const NYLAS_GRANT_ID = process.env.NYLAS_GRANT_ID;

if (!NYLAS_API_KEY) {
console.error('NYLAS_API_KEY is required. Please add it to your .env file.');
process.exit(1);
}

if (!NYLAS_GRANT_ID) {
console.error('NYLAS_GRANT_ID is required. Please add it to your .env file.');
process.exit(1);
}

async function listFoldersExample() {
try {
// Initialize Nylas client
const nylas = new Nylas({
apiKey: NYLAS_API_KEY!,
apiUri: process.env.NYLAS_API_URI || 'https://api.us.nylas.com',
});

console.log('=== Nylas Folders API Demo ===\n');

// 1. List all folders (default behavior - multi-level hierarchy)
console.log('1. Listing all folders (multi-level hierarchy):');
const allFolders: NylasListResponse<Folder> = await nylas.folders.list({
identifier: NYLAS_GRANT_ID!,
queryParams: {}
});

console.log(`Found ${allFolders.data.length} folders total:`);
allFolders.data.forEach((folder, index) => {
console.log(` ${index + 1}. ${folder.name} (ID: ${folder.id})`);
if (folder.parentId) {
console.log(` └─ Parent ID: ${folder.parentId}`);
}
if (folder.childCount !== undefined) {
console.log(` └─ Child Count: ${folder.childCount}`);
}
});
console.log();

// 2. List folders with single-level hierarchy (Microsoft only)
console.log('2. Listing folders with single-level hierarchy (Microsoft only):');
const singleLevelFolders: NylasListResponse<Folder> = await nylas.folders.list({
identifier: NYLAS_GRANT_ID!,
queryParams: {
singleLevel: true
} as any
});

console.log(`Found ${singleLevelFolders.data.length} folders at single level:`);
singleLevelFolders.data.forEach((folder, index) => {
console.log(` ${index + 1}. ${folder.name} (ID: ${folder.id})`);
if (folder.parentId) {
console.log(` └─ Parent ID: ${folder.parentId}`);
}
});
console.log();

// 3. List folders with both singleLevel and parentId parameters
const rootFolders = allFolders.data.filter(folder => !folder.parentId);
if (rootFolders.length > 0) {
const rootFolder = rootFolders[0];
console.log(`3. Listing child folders of "${rootFolder.name}" with single-level hierarchy:`);

const childFolders: NylasListResponse<Folder> = await nylas.folders.list({
identifier: NYLAS_GRANT_ID!,
queryParams: {
parentId: rootFolder.id,
singleLevel: true
} as any
});

console.log(`Found ${childFolders.data.length} direct child folders:`);
childFolders.data.forEach((folder, index) => {
console.log(` ${index + 1}. ${folder.name} (ID: ${folder.id})`);
});
} else {
console.log('3. No root folders found to demonstrate parentId + singleLevel combination.');
}
console.log();

// 4. Compare single-level vs multi-level for the same parent
if (rootFolders.length > 0) {
const rootFolder = rootFolders[0];
console.log('4. Comparing single-level vs multi-level hierarchy:');

// Multi-level (default)
const multiLevelChildren: NylasListResponse<Folder> = await nylas.folders.list({
identifier: NYLAS_GRANT_ID!,
queryParams: {
parentId: rootFolder.id,
singleLevel: false // explicit false
} as any
});

// Single-level
const singleLevelChildren: NylasListResponse<Folder> = await nylas.folders.list({
identifier: NYLAS_GRANT_ID!,
queryParams: {
parentId: rootFolder.id,
singleLevel: true
} as any
});

console.log(`Multi-level hierarchy: ${multiLevelChildren.data.length} folders`);
console.log(`Single-level hierarchy: ${singleLevelChildren.data.length} folders`);

if (multiLevelChildren.data.length !== singleLevelChildren.data.length) {
console.log('📝 Note: Different folder counts indicate the singleLevel parameter is working correctly.');
console.log(' Multi-level includes nested folders, single-level shows only direct children.');
}
}

} catch (error) {
if (error instanceof NylasApiError) {
console.error('Nylas API Error:', error.message);
console.error('Status Code:', error.statusCode);
console.error('Error Type:', error.type);
} else {
console.error('Unexpected error:', error);
}
}
}

// Enhanced demonstration with detailed explanations
async function detailedFoldersDemo() {
try {
const nylas = new Nylas({
apiKey: NYLAS_API_KEY!,
apiUri: process.env.NYLAS_API_URI || 'https://api.us.nylas.com',
});

console.log('\n=== Detailed singleLevel Parameter Demo ===\n');

console.log('The singleLevel parameter controls folder hierarchy traversal:');
console.log('• singleLevel: true → Returns only direct children (single level)');
console.log('• singleLevel: false → Returns all descendants (multi-level, default)');
console.log('• Microsoft accounts only - ignored for other providers\n');

// Show all query parameter combinations
const queryVariations: Array<{name: string, params: any}> = [
{
name: 'Default (multi-level)',
params: {}
},
{
name: 'Explicit multi-level',
params: { singleLevel: false }
},
{
name: 'Single-level only',
params: { singleLevel: true }
}
];

for (const variation of queryVariations) {
console.log(`--- ${variation.name} ---`);
console.log(`Query params: ${JSON.stringify(variation.params)}`);

try {
const folders: NylasListResponse<Folder> = await nylas.folders.list({
identifier: NYLAS_GRANT_ID!,
queryParams: variation.params
});

console.log(`Result: ${folders.data.length} folders found`);

// Show folder hierarchy structure
const rootFolders = folders.data.filter(f => !f.parentId);
const childFolders = folders.data.filter(f => f.parentId);

console.log(`├─ Root folders: ${rootFolders.length}`);
console.log(`└─ Child folders: ${childFolders.length}`);
} catch (error) {
console.log(`Error: ${error instanceof NylasApiError ? error.message : 'Unknown error'}`);
}
console.log();
}

} catch (error) {
console.error('Demo error:', error);
}
}

// Run the examples
async function main() {
await listFoldersExample();
await detailedFoldersDemo();

console.log('=== Folders API Demo Complete ===');
console.log('\nKey takeaways:');
console.log('1. The singleLevel parameter is Microsoft-specific');
console.log('2. Use singleLevel: true to get only direct children');
console.log('3. Use singleLevel: false (or omit) for full hierarchy');
console.log('4. Combine with parentId to control which folder to start from');
}

if (require.main === module) {
main().catch(console.error);
}

export default main;
3 changes: 2 additions & 1 deletion examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"build": "tsc",
"notetakers": "ts-node notetakers/notetaker.ts",
"calendars": "ts-node calendars/event_with_notetaker.ts",
"messages": "ts-node messages/messages.ts"
"messages": "ts-node messages/messages.ts",
"folders": "ts-node folders/folders.ts"
},
"dependencies": {
"dotenv": "^16.0.0",
Expand Down
7 changes: 7 additions & 0 deletions src/models/folders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ export interface ListFolderQueryParams extends ListQueryParams {
* (Microsoft and EWS only.) Use the ID of a folder to find all child folders it contains.
*/
parentId?: string;

/**
* (Microsoft only) If true, retrieves folders from a single-level hierarchy only.
* If false, retrieves folders across a multi-level hierarchy.
* @default false
*/
singleLevel?: boolean;
}

export type UpdateFolderRequest = Partial<CreateFolderRequest>;
Loading