Filter a Report by Project Name Using a Chained Query

Use two chained QUERY() calls in a Report Builder template — the first resolves a project name to its internal ID from the projects system table, the second uses that ID to filter records — letting report consumers reference projects by human-readable name instead of UUID.

Fulcrum records can be assigned to a project, but the _project_id column stored on each record is a UUID, not a name. When you want to filter a report to a specific project, you either hard-code the UUID (fragile) or perform a lookup first. This pattern uses two QUERY() calls to resolve a project name to its ID, then applies it as a filter in the main data query.

Report Template Code

// 1. Set the project name to filter on.
//    This could also be driven by a $params value for a dynamic report.
const projectName = 'Project Alpha';  // Replace with your project name

// Escape single quotes so the name can't break out of the SQL string
const safeProjectName = projectName.replace(/'/g, "''");

// 2. Look up the project_id from the Fulcrum system projects table.
const projectInfo = QUERY(`
  SELECT project_id
  FROM projects
  WHERE name = '${safeProjectName}'
`);

const projectId = projectInfo?.rows?.[0]?.project_id;
if (!projectId) {
  throw new Error('Project not found');
}

// 3. Use the resolved project_id to filter the main data query.
//    Replace "Your App Name" with your app's exact name.
const records = QUERY(`
  SELECT *
  FROM "Your App Name"
  WHERE _project_id = '${projectId}'
  ORDER BY _updated_at DESC
`);

How It Works

The projects system table contains one row per project in your organization with project_id (UUID) and name (display name). The first QUERY() fetches the matching project_id for the project name. The second QUERY() then filters "Your App Name" using that ID via the _project_id column that exists on every Fulcrum record.

Making It Dynamic with $params

Rather than hard-coding the project name, you can pass it as a URL parameter and reference it with $params:

// Caller passes ?project_name=Project+Alpha in the report URL
const projectName = $params.query?.project_name || $params.post?.project_name || '';

// $params values are external input. Validate against an allow-list when you
// can; otherwise escape single quotes before using the value in SQL.
const allowedProjects = ['Project Alpha', 'Project Beta'];  // Replace with your project names
if (!allowedProjects.includes(projectName)) {
  throw new Error('Unknown project');
}
const safeProjectName = projectName.replace(/'/g, "''");

const projectInfo = QUERY(`
  SELECT project_id FROM projects WHERE name = '${safeProjectName}'
`);

const projectId = projectInfo?.rows?.[0]?.project_id;

// Stop if no project matched, rather than querying with an undefined ID
if (!projectId) {
  throw new Error('Project not found');
}

const records = QUERY(`
  SELECT * FROM "Your App Name"
  WHERE _project_id = '${projectId}'
  ORDER BY _updated_at DESC
`);

Combining with a Date Range Filter

Add a date range condition to the second query to narrow results further:

const records = QUERY(`
  SELECT *
  FROM "Your App Name"
  WHERE _project_id = '${projectId}'
    AND _updated_at >= NOW() - INTERVAL '7 days'
  ORDER BY _updated_at DESC
`);

Notes

Project names are case-sensitive in the projects table. If the lookup returns undefined, double-check the exact name in your Fulcrum project settings.

_project_id is null for records not assigned to a project. Those records won't appear in filtered results, which is the intended behavior for project-scoped reports.

The projects system table also contains description, color, and created_at columns if you want to include project metadata in the report header.


Did this page help you?