Documentation Index Fetch the complete documentation index at: https://docs.narrative.io/llms.txt
Use this file to discover all available pages before exploring further.
The Narrative SDK provides methods to execute, validate, and compile NQL queries programmatically. This guide covers common query patterns and best practices.
Prerequisites
Running a query
Basic execution
Use executeNql() to run a query and retrieve results:
import { NarrativeApi } from '@narrative.io/data-collaboration-sdk-ts' ;
const api = new NarrativeApi ({
apiKey: process . env . NARRATIVE_API_KEY ,
});
const result = await api . executeNql ({
nql: `
SELECT _nio_id, _nio_updated_at
FROM company_data."my_dataset"
LIMIT 100
` ,
data_plane_id: null ,
});
console . log ( 'Query state:' , result . state );
console . log ( 'Rows returned:' , result . result . rows );
console . log ( 'Cost:' , result . result . cost );
The executeNql() method accepts the following options:
Option Type Required Description nqlstringYes The NQL query to execute data_plane_idstring | nullYes Target data plane ID, or null for default execution_clusterobjectNo Cluster configuration (shared or dedicated). See Compute Pools create_as_viewbooleanNo Create the result as a view dataset instead of a materialized view
const result = await api . executeNql ({
nql: 'SELECT user_id, email, created_at FROM company_data."my_dataset" LIMIT 10' ,
data_plane_id: null ,
execution_cluster: { type: 'dedicated' },
});
Creating a view dataset
Set create_as_view to true to create a view dataset instead of a materialized view. A view dataset stores only the NQL query definition—not the query results. The NQL is inlined and re-evaluated at query time whenever other queries reference the view dataset.
const result = await api . executeNql ({
nql: `
SELECT user_id, email, event_type
FROM company_data."customer_events"
WHERE event_type = 'purchase'
` ,
data_plane_id: null ,
create_as_view: true ,
});
View datasets have several restrictions compared to materialized views. You cannot create access rules, connections, or use features like MERGE ON, PARTITIONED_BY, or chunking strategies with view datasets. See view dataset limitations for the full list.
Understanding the result
The NqlResult object contains execution metadata:
interface NqlResult {
id : string ; // Job ID
state : string ; // 'completed', 'failed', etc.
created_at : string ; // When the query was submitted
completed_at : string ; // When execution finished
result : {
type : string ;
rows : number ; // Number of rows returned
cost : number ; // Query cost
nql_type : string ;
};
input : {
nql : string ; // The original query
compiled_select ?: string ;
};
failures : unknown []; // Any execution errors
}
Validating a query
Use validateNql() to check query syntax without execution:
const validation = await api . validateNql ({
nql: `
SELECT _nio_id
FROM company_data."my_dataset"
LIMIT 10
` ,
data_plane_id: null ,
});
console . log ( 'Query is valid' );
If the query has syntax errors, the method throws an error with details about the issue.
Compiling a query
Use compileNql() to see the transpiled SQL without executing:
const compiled = await api . compileNql ({
nql: `
SELECT _nio_id, _nio_updated_at
FROM company_data."my_dataset"
WHERE _nio_updated_at > CURRENT_DATE - INTERVAL '7' DAY
LIMIT 100
` ,
data_plane_id: null ,
});
console . log ( 'Transpiled SQL:' , compiled . sql );
if ( compiled . mappingErrors ) {
console . log ( 'Mapping errors:' , compiled . mappingErrors );
}
This is useful for:
Debugging query issues
Understanding how NQL maps to the underlying SQL dialect
Verifying Rosetta Stone mappings
Parsing a query
Use parseNql() to get the abstract syntax tree (AST):
const ast = await api . parseNql ({
nql: 'SELECT _nio_id FROM company_data."my_dataset" LIMIT 10' ,
data_plane_id: null ,
});
console . log ( 'AST:' , JSON . stringify ( ast , null , 2 ));
Retrieving a previous query
If you have a job ID from a previous query, retrieve its results:
const result = await api . getNqlByJobId ( 'job-id-here' );
console . log ( 'Query state:' , result . state );
console . log ( 'Rows returned:' , result . result . rows );
Query patterns
Filtering data
const result = await api . executeNql ({
nql: `
SELECT sha256_hashed_email, event_type, event_timestamp
FROM company_data."customer_events"
WHERE event_timestamp >= CURRENT_DATE - INTERVAL '30' DAY
AND event_type = 'purchase'
LIMIT 1000
` ,
data_plane_id: null ,
});
Aggregations
const result = await api . executeNql ({
nql: `
SELECT
event_type,
COUNT(1) as event_count
FROM company_data."customer_events"
GROUP BY event_type
LIMIT 100
` ,
data_plane_id: null ,
});
Using Rosetta Stone attributes
Query normalized data across datasets:
const result = await api . executeNql ({
nql: `
SELECT
hl7_gender,
unique_id,
timestamp_utc
FROM narrative.rosetta_stone
LIMIT 100
` ,
data_plane_id: null ,
});
Error handling
Wrap query execution in try-catch to handle errors:
try {
const result = await api . executeNql ({
nql: 'SELECT user_id, email, created_at FROM company_data."my_dataset" LIMIT 10' ,
data_plane_id: null ,
});
if ( result . state === 'completed' ) {
console . log ( 'Success:' , result . result . rows , 'rows' );
} else {
console . log ( 'Query state:' , result . state );
}
} catch ( error ) {
if ( error . status === 400 ) {
console . error ( 'Invalid query:' , error . message );
} else if ( error . status === 403 ) {
console . error ( 'Permission denied' );
} else {
console . error ( 'Query failed:' , error );
}
}
Best practices
Practice Description Always use LIMIT Prevent unbounded result sets Validate before executing Use compileNql() to catch errors early Handle errors gracefully Wrap calls in try-catch Use appropriate timeouts Long-running queries may need monitoring Filter at the source Apply WHERE clauses to reduce data scanned
Related content
NQL Syntax Reference Complete NQL language reference
NQL Design Philosophy Understanding NQL’s approach
Query Optimization Tips for efficient queries
Error Handling Handle SDK errors gracefully