Repository: dagster-io/dagster
Pull request #29626: [1/2] Add a resolver to get all materialized asset keys
Author: login: salazarm
Created: 2025-04-28T13:23:11Z | Merged: 2025-04-29T08:58:07Z

## Summary & Motivation

Adds a resolver to fetch all materialized asset keys without also fetching their definitions. This uses a new resolver instead of `assetsOrError` in order to avoid fetching the entire asset graph. I took a bit of a shortcut (sorry @alangenfeld) in that I'm just returning the asset keys for now but in the future we might want to add more fields to `GrapheneAssetState` that can be supported solely by the DB row in which case we'd need to add new storage methods (using existing logic) then. 

In the next PR in this sequence we're updating our asset fetching to go through our WorkspaceContext on the frontend meaning we end up going through `workspaceLocationEntryOrError`. The only issue is that assets without a definition aren't returned by that resolver since they're not defined in the workspace so we still need to fetch all materialized assets to know about external assets hence this PR.

## How I Tested These Changes
Snapshot test + Manual testing with frontend PR https://github.com/dagster-io/dagster/pull/29627.



Diff:
diff --git a/js_modules/dagster-ui/packages/ui-core/src/graphql/possibleTypes.generated.json b/js_modules/dagster-ui/packages/ui-core/src/graphql/possibleTypes.generated.json
index 645a74d454dcd..18e0f4e4b3fd2 100644
--- a/js_modules/dagster-ui/packages/ui-core/src/graphql/possibleTypes.generated.json
+++ b/js_modules/dagster-ui/packages/ui-core/src/graphql/possibleTypes.generated.json
@@ -1 +1 @@
-{"DisplayableEvent":["EngineEvent","ExecutionStepOutputEvent","ExpectationResult","FailureMetadata","HandledOutputEvent","LoadedInputEvent","ObjectStoreOperationResult","ResourceInitFailureEvent","ResourceInitStartedEvent","ResourceInitSuccessEvent","StepWorkerStartedEvent","StepWorkerStartingEvent","MaterializationEvent","ObservationEvent","TypeCheck","FailedToMaterializeEvent"],"MarkerEvent":["EngineEvent","ResourceInitFailureEvent","ResourceInitStartedEvent","ResourceInitSuccessEvent","StepWorkerStartedEvent","StepWorkerStartingEvent"],"ErrorEvent":["EngineEvent","ExecutionStepFailureEvent","ExecutionStepUpForRetryEvent","HookErroredEvent","RunCanceledEvent","RunFailureEvent","ResourceInitFailureEvent"],"MessageEvent":["EngineEvent","ExecutionStepFailureEvent","ExecutionStepInputEvent","ExecutionStepOutputEvent","ExecutionStepRestartEvent","ExecutionStepSkippedEvent","ExecutionStepStartEvent","ExecutionStepSuccessEvent","ExecutionStepUpForRetryEvent","HandledOutputEvent","HookCompletedEvent","HookErroredEvent","HookSkippedEvent","LoadedInputEvent","LogMessageEvent","ObjectStoreOperationEvent","RunCanceledEvent","RunCancelingEvent","RunDequeuedEvent","RunEnqueuedEvent","RunFailureEvent","ResourceInitFailureEvent","ResourceInitStartedEvent","ResourceInitSuccessEvent","RunStartEvent","RunStartingEvent","RunSuccessEvent","StepExpectationResultEvent","StepWorkerStartedEvent","StepWorkerStartingEvent","MaterializationEvent","ObservationEvent","AssetMaterializationPlannedEvent","FailedToMaterializeEvent","LogsCapturedEvent","AlertStartEvent","AlertSuccessEvent","AlertFailureEvent","AssetCheckEvaluationPlannedEvent","AssetCheckEvaluationEvent"],"RunEvent":["RunCanceledEvent","RunCancelingEvent","RunDequeuedEvent","RunEnqueuedEvent","RunFailureEvent","RunStartEvent","RunStartingEvent","RunSuccessEvent","AssetMaterializationPlannedEvent","AlertStartEvent","AlertSuccessEvent","AlertFailureEvent"],"PipelineRunStepStats":["RunStepStats"],"StepEvent":["EngineEvent","ExecutionStepFailureEvent","ExecutionStepInputEvent","ExecutionStepOutputEvent","ExecutionStepRestartEvent","ExecutionStepSkippedEvent","ExecutionStepStartEvent","ExecutionStepSuccessEvent","ExecutionStepUpForRetryEvent","HandledOutputEvent","HookCompletedEvent","HookErroredEvent","HookSkippedEvent","LoadedInputEvent","ObjectStoreOperationEvent","ResourceInitFailureEvent","ResourceInitStartedEvent","ResourceInitSuccessEvent","StepExpectationResultEvent","StepWorkerStartedEvent","StepWorkerStartingEvent","MaterializationEvent","ObservationEvent","FailedToMaterializeEvent","AssetCheckEvaluationPlannedEvent","AssetCheckEvaluationEvent"],"AssetMaterializationEventType":["FailedToMaterializeEvent","MaterializationEvent"],"AssetHealthMaterializationMeta":["AssetHealthMaterializationDegradedPartitionedMeta","AssetHealthMaterializationHealthyPartitionedMeta","AssetHealthMaterializationDegradedNotPartitionedMeta"],"AssetHealthCheckMeta":["AssetHealthCheckDegradedMeta","AssetHealthCheckWarningMeta","AssetHealthCheckUnknownMeta"],"InternalFreshnessPolicy":["TimeWindowFreshnessPolicy"],"AssetOwner":["UserAssetOwner","TeamAssetOwner"],"AssetPartitionStatuses":["DefaultPartitionStatuses","MultiPartitionStatuses","TimePartitionStatuses"],"PartitionStatus1D":["TimePartitionStatuses","DefaultPartitionStatuses"],"AssetChecksOrError":["AssetChecks","AssetCheckNeedsMigrationError","AssetCheckNeedsUserCodeUpgrade","AssetCheckNeedsAgentUpgradeError"],"Instigator":["Schedule","Sensor"],"EvaluationStackEntry":["EvaluationStackListItemEntry","EvaluationStackPathEntry","EvaluationStackMapKeyEntry","EvaluationStackMapValueEntry"],"IPipelineSnapshot":["Pipeline","PipelineSnapshot","Job"],"PipelineConfigValidationError":["FieldNotDefinedConfigError","FieldsNotDefinedConfigError","MissingFieldConfigError","MissingFieldsConfigError","RuntimeMismatchConfigError","SelectorTypeConfigError"],"PipelineConfigValidationInvalid":["RunConfigValidationInvalid"],"PipelineConfigValidationResult":["InvalidSubsetError","PipelineConfigValidationValid","RunConfigValidationInvalid","PipelineNotFoundError","PythonError"],"PipelineReference":["PipelineSnapshot","UnknownPipeline"],"PipelineRun":["Run"],"DagsterRunEvent":["ExecutionStepFailureEvent","ExecutionStepInputEvent","ExecutionStepOutputEvent","ExecutionStepSkippedEvent","ExecutionStepStartEvent","ExecutionStepSuccessEvent","ExecutionStepUpForRetryEvent","ExecutionStepRestartEvent","LogMessageEvent","ResourceInitFailureEvent","ResourceInitStartedEvent","ResourceInitSuccessEvent","RunFailureEvent","RunStartEvent","RunEnqueuedEvent","RunDequeuedEvent","RunStartingEvent","RunCancelingEvent","RunCanceledEvent","RunSuccessEvent","StepWorkerStartedEvent","StepWorkerStartingEvent","HandledOutputEvent","LoadedInputEvent","LogsCapturedEvent","ObjectStoreOperationEvent","StepExpectationResultEvent","MaterializationEvent","ObservationEvent","FailedToMaterializeEvent","EngineEvent","HookCompletedEvent","HookSkippedEvent","HookErroredEvent","AlertStartEvent","AlertSuccessEvent","AlertFailureEvent","AssetMaterializationPlannedEvent","AssetCheckEvaluationPlannedEvent","AssetCheckEvaluationEvent"],"PipelineRunLogsSubscriptionPayload":["PipelineRunLogsSubscriptionSuccess","PipelineRunLogsSubscriptionFailure"],"RunOrError":["Run","RunNotFoundError","PythonError"],"PipelineRunStatsSnapshot":["RunStatsSnapshot"],"RunStatsSnapshotOrError":["RunStatsSnapshot","PythonError"],"PipelineSnapshotOrError":["PipelineNotFoundError","PipelineSnapshot","PipelineSnapshotNotFoundError","PythonError"],"RunsFeedEntry":["Run","PartitionBackfill"],"AssetOrError":["Asset","AssetNotFoundError"],"AssetsOrError":["AssetConnection","PythonError"],"DeletePipelineRunResult":["DeletePipelineRunSuccess","UnauthorizedError","PythonError","RunNotFoundError"],"ExecutionPlanOrError":["ExecutionPlan","RunConfigValidationInvalid","PipelineNotFoundError","InvalidSubsetError","PythonError"],"LaunchMultipleRunsResultOrError":["LaunchMultipleRunsResult","PythonError"],"PipelineOrError":["Pipeline","PipelineNotFoundError","InvalidSubsetError","PythonError"],"ReloadRepositoryLocationMutationResult":["WorkspaceLocationEntry","ReloadNotSupported","RepositoryLocationNotFound","UnauthorizedError","PythonError"],"RepositoryLocationOrLoadError":["RepositoryLocation","PythonError"],"ReloadWorkspaceMutationResult":["Workspace","UnauthorizedError","PythonError"],"ShutdownRepositoryLocationMutationResult":["ShutdownRepositoryLocationSuccess","RepositoryLocationNotFound","UnauthorizedError","PythonError"],"TerminatePipelineExecutionFailure":["TerminateRunFailure"],"TerminatePipelineExecutionSuccess":["TerminateRunSuccess"],"TerminateRunResult":["TerminateRunSuccess","TerminateRunFailure","RunNotFoundError","UnauthorizedError","PythonError"],"ScheduleMutationResult":["PythonError","UnauthorizedError","ScheduleStateResult","ScheduleNotFoundError"],"ScheduleOrError":["Schedule","ScheduleNotFoundError","PythonError"],"SchedulerOrError":["Scheduler","SchedulerNotDefinedError","PythonError"],"SchedulesOrError":["Schedules","RepositoryNotFoundError","PythonError"],"ScheduleTickSpecificData":["ScheduleTickSuccessData","ScheduleTickFailureData"],"LaunchBackfillResult":["LaunchBackfillSuccess","PartitionSetNotFoundError","PartitionKeysNotFoundError","InvalidStepError","InvalidOutputError","RunConfigValidationInvalid","PipelineNotFoundError","RunConflict","UnauthorizedError","PythonError","InvalidSubsetError","PresetNotFoundError","ConflictingExecutionParamsError","NoModeProvidedError"],"ConfigTypeOrError":["EnumConfigType","CompositeConfigType","RegularConfigType","PipelineNotFoundError","ConfigTypeNotFoundError","PythonError"],"ConfigType":["ArrayConfigType","CompositeConfigType","EnumConfigType","NullableConfigType","RegularConfigType","ScalarUnionConfigType","MapConfigType"],"WrappingConfigType":["ArrayConfigType","NullableConfigType"],"DagsterType":["ListDagsterType","NullableDagsterType","RegularDagsterType"],"DagsterTypeOrError":["RegularDagsterType","PipelineNotFoundError","DagsterTypeNotFoundError","PythonError"],"WrappingDagsterType":["ListDagsterType","NullableDagsterType"],"Error":["AssetCheckNeedsMigrationError","AssetCheckNeedsUserCodeUpgrade","AssetCheckNeedsAgentUpgradeError","PartitionKeysNotFoundError","AssetNotFoundError","ConflictingExecutionParamsError","ConfigTypeNotFoundError","DagsterTypeNotFoundError","InvalidPipelineRunsFilterError","InvalidSubsetError","ModeNotFoundError","NoModeProvidedError","PartitionSetNotFoundError","PipelineNotFoundError","RunConflict","PipelineSnapshotNotFoundError","PresetNotFoundError","PythonError","ErrorChainLink","UnauthorizedError","ReloadNotSupported","RepositoryLocationNotFound","RepositoryNotFoundError","ResourceNotFoundError","RunGroupNotFoundError","RunNotFoundError","ScheduleNotFoundError","SchedulerNotDefinedError","SensorNotFoundError","UnsupportedOperationError","DuplicateDynamicPartitionError","InstigationStateNotFoundError","SolidStepStatusUnavailableError","GraphNotFoundError","BackfillNotFoundError","PartitionSubsetDeserializationError","AutoMaterializeAssetEvaluationNeedsMigrationError"],"PipelineRunConflict":["RunConflict"],"PipelineRunNotFoundError":["RunNotFoundError"],"RepositoriesOrError":["RepositoryConnection","RepositoryNotFoundError","PythonError"],"LocationDocsJsonOrError":["LocationDocsJson","PythonError"],"RepositoryOrError":["PythonError","Repository","RepositoryNotFoundError"],"WorkspaceLocationEntryOrError":["WorkspaceLocationEntry","PythonError"],"InstigationTypeSpecificData":["SensorData","ScheduleData"],"InstigationStateOrError":["InstigationState","InstigationStateNotFoundError","PythonError"],"InstigationStatesOrError":["InstigationStates","PythonError"],"MetadataEntry":["TableColumnLineageMetadataEntry","TableSchemaMetadataEntry","TableMetadataEntry","FloatMetadataEntry","IntMetadataEntry","JsonMetadataEntry","BoolMetadataEntry","MarkdownMetadataEntry","PathMetadataEntry","NotebookMetadataEntry","PythonArtifactMetadataEntry","TextMetadataEntry","UrlMetadataEntry","PipelineRunMetadataEntry","AssetMetadataEntry","JobMetadataEntry","CodeReferencesMetadataEntry","NullMetadataEntry","TimestampMetadataEntry","PoolMetadataEntry"],"SourceLocation":["LocalFileCodeReference","UrlCodeReference"],"PartitionRunConfigOrError":["PartitionRunConfig","PythonError"],"AssetBackfillStatus":["AssetPartitionsStatusCounts","UnpartitionedAssetStatus"],"PartitionSetOrError":["PartitionSet","PartitionSetNotFoundError","PythonError"],"PartitionSetsOrError":["PartitionSets","PipelineNotFoundError","PythonError"],"PartitionsOrError":["Partitions","PythonError"],"PartitionStatusesOrError":["PartitionStatuses","PythonError"],"PartitionTagsOrError":["PartitionTags","PythonError"],"RunConfigSchemaOrError":["RunConfigSchema","PipelineNotFoundError","InvalidSubsetError","ModeNotFoundError","PythonError"],"LaunchRunResult":["LaunchRunSuccess","InvalidStepError","InvalidOutputError","RunConfigValidationInvalid","PipelineNotFoundError","RunConflict","UnauthorizedError","PythonError","InvalidSubsetError","PresetNotFoundError","ConflictingExecutionParamsError","NoModeProvidedError"],"LaunchRunReexecutionResult":["LaunchRunSuccess","InvalidStepError","InvalidOutputError","RunConfigValidationInvalid","PipelineNotFoundError","RunConflict","UnauthorizedError","PythonError","InvalidSubsetError","PresetNotFoundError","ConflictingExecutionParamsError","NoModeProvidedError"],"LaunchPipelineRunSuccess":["LaunchRunSuccess"],"RunsOrError":["Runs","InvalidPipelineRunsFilterError","PythonError"],"PipelineRuns":["Runs"],"RunGroupOrError":["RunGroup","RunGroupNotFoundError","PythonError"],"SensorOrError":["Sensor","SensorNotFoundError","UnauthorizedError","PythonError"],"SensorsOrError":["Sensors","RepositoryNotFoundError","PythonError"],"StopSensorMutationResultOrError":["StopSensorMutationResult","UnauthorizedError","PythonError"],"ISolidDefinition":["CompositeSolidDefinition","SolidDefinition"],"SolidContainer":["Pipeline","PipelineSnapshot","Job","CompositeSolidDefinition","Graph"],"SolidStepStatsOrError":["SolidStepStatsConnection","SolidStepStatusUnavailableError"],"WorkspaceOrError":["Workspace","PythonError"],"WorkspaceLocationStatusEntriesOrError":["WorkspaceLocationStatusEntries","PythonError"],"ResourcesOrError":["ResourceConnection","PipelineNotFoundError","InvalidSubsetError","PythonError"],"GraphOrError":["Graph","GraphNotFoundError","PythonError"],"ResourceDetailsOrError":["ResourceDetails","ResourceNotFoundError","PythonError"],"ResourceDetailsListOrError":["ResourceDetailsList","RepositoryNotFoundError","PythonError"],"EnvVarWithConsumersOrError":["EnvVarWithConsumersList","PythonError"],"RunsFeedConnectionOrError":["RunsFeedConnection","PythonError"],"RunsFeedCountOrError":["RunsFeedCount","PythonError"],"RunTagKeysOrError":["PythonError","RunTagKeys"],"RunTagsOrError":["PythonError","RunTags"],"RunIdsOrError":["RunIds","InvalidPipelineRunsFilterError","PythonError"],"AssetNodeOrError":["AssetNode","AssetNotFoundError"],"PartitionBackfillOrError":["PartitionBackfill","BackfillNotFoundError","PythonError"],"PartitionBackfillsOrError":["PartitionBackfills","PythonError"],"EventConnectionOrError":["EventConnection","RunNotFoundError","PythonError"],"AutoMaterializeAssetEvaluationRecordsOrError":["AutoMaterializeAssetEvaluationRecords","AutoMaterializeAssetEvaluationNeedsMigrationError"],"PartitionKeysOrError":["PartitionKeys","PartitionSubsetDeserializationError"],"AutoMaterializeRuleEvaluationData":["TextRuleEvaluationData","ParentMaterializedRuleEvaluationData","WaitingOnKeysRuleEvaluationData"],"AssetConditionEvaluationNode":["UnpartitionedAssetConditionEvaluationNode","PartitionedAssetConditionEvaluationNode","SpecificPartitionAssetConditionEvaluationNode"],"AssetConditionEvaluationRecordsOrError":["AssetConditionEvaluationRecords","AutoMaterializeAssetEvaluationNeedsMigrationError"],"EntityKey":["AssetKey","AssetCheckhandle"],"SensorDryRunResult":["PythonError","SensorNotFoundError","DryRunInstigationTick"],"ScheduleDryRunResult":["DryRunInstigationTick","PythonError","ScheduleNotFoundError"],"TerminateRunsResultOrError":["TerminateRunsResult","PythonError"],"AssetWipeMutationResult":["AssetNotFoundError","UnauthorizedError","PythonError","UnsupportedOperationError","AssetWipeSuccess"],"ReportRunlessAssetEventsResult":["UnauthorizedError","PythonError","ReportRunlessAssetEventsSuccess"],"ResumeBackfillResult":["ResumeBackfillSuccess","UnauthorizedError","PythonError"],"CancelBackfillResult":["CancelBackfillSuccess","UnauthorizedError","PythonError"],"LogTelemetryMutationResult":["LogTelemetrySuccess","PythonError"],"AddDynamicPartitionResult":["AddDynamicPartitionSuccess","UnauthorizedError","PythonError","DuplicateDynamicPartitionError"],"DeleteDynamicPartitionsResult":["DeleteDynamicPartitionsSuccess","UnauthorizedError","PythonError"]}
\ No newline at end of file
+{"DisplayableEvent":["EngineEvent","ExecutionStepOutputEvent","ExpectationResult","FailureMetadata","HandledOutputEvent","LoadedInputEvent","ObjectStoreOperationResult","ResourceInitFailureEvent","ResourceInitStartedEvent","ResourceInitSuccessEvent","StepWorkerStartedEvent","StepWorkerStartingEvent","MaterializationEvent","ObservationEvent","TypeCheck","FailedToMaterializeEvent"],"MarkerEvent":["EngineEvent","ResourceInitFailureEvent","ResourceInitStartedEvent","ResourceInitSuccessEvent","StepWorkerStartedEvent","StepWorkerStartingEvent"],"ErrorEvent":["EngineEvent","ExecutionStepFailureEvent","ExecutionStepUpForRetryEvent","HookErroredEvent","RunCanceledEvent","RunFailureEvent","ResourceInitFailureEvent"],"MessageEvent":["EngineEvent","ExecutionStepFailureEvent","ExecutionStepInputEvent","ExecutionStepOutputEvent","ExecutionStepRestartEvent","ExecutionStepSkippedEvent","ExecutionStepStartEvent","ExecutionStepSuccessEvent","ExecutionStepUpForRetryEvent","HandledOutputEvent","HookCompletedEvent","HookErroredEvent","HookSkippedEvent","LoadedInputEvent","LogMessageEvent","ObjectStoreOperationEvent","RunCanceledEvent","RunCancelingEvent","RunDequeuedEvent","RunEnqueuedEvent","RunFailureEvent","ResourceInitFailureEvent","ResourceInitStartedEvent","ResourceInitSuccessEvent","RunStartEvent","RunStartingEvent","RunSuccessEvent","StepExpectationResultEvent","StepWorkerStartedEvent","StepWorkerStartingEvent","MaterializationEvent","ObservationEvent","AssetMaterializationPlannedEvent","FailedToMaterializeEvent","LogsCapturedEvent","AlertStartEvent","AlertSuccessEvent","AlertFailureEvent","AssetCheckEvaluationPlannedEvent","AssetCheckEvaluationEvent"],"RunEvent":["RunCanceledEvent","RunCancelingEvent","RunDequeuedEvent","RunEnqueuedEvent","RunFailureEvent","RunStartEvent","RunStartingEvent","RunSuccessEvent","AssetMaterializationPlannedEvent","AlertStartEvent","AlertSuccessEvent","AlertFailureEvent"],"PipelineRunStepStats":["RunStepStats"],"StepEvent":["EngineEvent","ExecutionStepFailureEvent","ExecutionStepInputEvent","ExecutionStepOutputEvent","ExecutionStepRestartEvent","ExecutionStepSkippedEvent","ExecutionStepStartEvent","ExecutionStepSuccessEvent","ExecutionStepUpForRetryEvent","HandledOutputEvent","HookCompletedEvent","HookErroredEvent","HookSkippedEvent","LoadedInputEvent","ObjectStoreOperationEvent","ResourceInitFailureEvent","ResourceInitStartedEvent","ResourceInitSuccessEvent","StepExpectationResultEvent","StepWorkerStartedEvent","StepWorkerStartingEvent","MaterializationEvent","ObservationEvent","FailedToMaterializeEvent","AssetCheckEvaluationPlannedEvent","AssetCheckEvaluationEvent"],"AssetMaterializationEventType":["FailedToMaterializeEvent","MaterializationEvent"],"AssetHealthMaterializationMeta":["AssetHealthMaterializationDegradedPartitionedMeta","AssetHealthMaterializationHealthyPartitionedMeta","AssetHealthMaterializationDegradedNotPartitionedMeta"],"AssetHealthCheckMeta":["AssetHealthCheckDegradedMeta","AssetHealthCheckWarningMeta","AssetHealthCheckUnknownMeta"],"InternalFreshnessPolicy":["TimeWindowFreshnessPolicy"],"AssetOwner":["UserAssetOwner","TeamAssetOwner"],"AssetPartitionStatuses":["DefaultPartitionStatuses","MultiPartitionStatuses","TimePartitionStatuses"],"PartitionStatus1D":["TimePartitionStatuses","DefaultPartitionStatuses"],"AssetChecksOrError":["AssetChecks","AssetCheckNeedsMigrationError","AssetCheckNeedsUserCodeUpgrade","AssetCheckNeedsAgentUpgradeError"],"Instigator":["Schedule","Sensor"],"EvaluationStackEntry":["EvaluationStackListItemEntry","EvaluationStackPathEntry","EvaluationStackMapKeyEntry","EvaluationStackMapValueEntry"],"IPipelineSnapshot":["Pipeline","PipelineSnapshot","Job"],"PipelineConfigValidationError":["FieldNotDefinedConfigError","FieldsNotDefinedConfigError","MissingFieldConfigError","MissingFieldsConfigError","RuntimeMismatchConfigError","SelectorTypeConfigError"],"PipelineConfigValidationInvalid":["RunConfigValidationInvalid"],"PipelineConfigValidationResult":["InvalidSubsetError","PipelineConfigValidationValid","RunConfigValidationInvalid","PipelineNotFoundError","PythonError"],"PipelineReference":["PipelineSnapshot","UnknownPipeline"],"PipelineRun":["Run"],"DagsterRunEvent":["ExecutionStepFailureEvent","ExecutionStepInputEvent","ExecutionStepOutputEvent","ExecutionStepSkippedEvent","ExecutionStepStartEvent","ExecutionStepSuccessEvent","ExecutionStepUpForRetryEvent","ExecutionStepRestartEvent","LogMessageEvent","ResourceInitFailureEvent","ResourceInitStartedEvent","ResourceInitSuccessEvent","RunFailureEvent","RunStartEvent","RunEnqueuedEvent","RunDequeuedEvent","RunStartingEvent","RunCancelingEvent","RunCanceledEvent","RunSuccessEvent","StepWorkerStartedEvent","StepWorkerStartingEvent","HandledOutputEvent","LoadedInputEvent","LogsCapturedEvent","ObjectStoreOperationEvent","StepExpectationResultEvent","MaterializationEvent","ObservationEvent","FailedToMaterializeEvent","EngineEvent","HookCompletedEvent","HookSkippedEvent","HookErroredEvent","AlertStartEvent","AlertSuccessEvent","AlertFailureEvent","AssetMaterializationPlannedEvent","AssetCheckEvaluationPlannedEvent","AssetCheckEvaluationEvent"],"PipelineRunLogsSubscriptionPayload":["PipelineRunLogsSubscriptionSuccess","PipelineRunLogsSubscriptionFailure"],"RunOrError":["Run","RunNotFoundError","PythonError"],"PipelineRunStatsSnapshot":["RunStatsSnapshot"],"RunStatsSnapshotOrError":["RunStatsSnapshot","PythonError"],"PipelineSnapshotOrError":["PipelineNotFoundError","PipelineSnapshot","PipelineSnapshotNotFoundError","PythonError"],"RunsFeedEntry":["Run","PartitionBackfill"],"AssetOrError":["Asset","AssetNotFoundError"],"AssetsOrError":["AssetConnection","PythonError"],"DeletePipelineRunResult":["DeletePipelineRunSuccess","UnauthorizedError","PythonError","RunNotFoundError"],"ExecutionPlanOrError":["ExecutionPlan","RunConfigValidationInvalid","PipelineNotFoundError","InvalidSubsetError","PythonError"],"LaunchMultipleRunsResultOrError":["LaunchMultipleRunsResult","PythonError"],"PipelineOrError":["Pipeline","PipelineNotFoundError","InvalidSubsetError","PythonError"],"ReloadRepositoryLocationMutationResult":["WorkspaceLocationEntry","ReloadNotSupported","RepositoryLocationNotFound","UnauthorizedError","PythonError"],"RepositoryLocationOrLoadError":["RepositoryLocation","PythonError"],"ReloadWorkspaceMutationResult":["Workspace","UnauthorizedError","PythonError"],"ShutdownRepositoryLocationMutationResult":["ShutdownRepositoryLocationSuccess","RepositoryLocationNotFound","UnauthorizedError","PythonError"],"TerminatePipelineExecutionFailure":["TerminateRunFailure"],"TerminatePipelineExecutionSuccess":["TerminateRunSuccess"],"TerminateRunResult":["TerminateRunSuccess","TerminateRunFailure","RunNotFoundError","UnauthorizedError","PythonError"],"ScheduleMutationResult":["PythonError","UnauthorizedError","ScheduleStateResult","ScheduleNotFoundError"],"ScheduleOrError":["Schedule","ScheduleNotFoundError","PythonError"],"SchedulerOrError":["Scheduler","SchedulerNotDefinedError","PythonError"],"SchedulesOrError":["Schedules","RepositoryNotFoundError","PythonError"],"ScheduleTickSpecificData":["ScheduleTickSuccessData","ScheduleTickFailureData"],"LaunchBackfillResult":["LaunchBackfillSuccess","PartitionSetNotFoundError","PartitionKeysNotFoundError","InvalidStepError","InvalidOutputError","RunConfigValidationInvalid","PipelineNotFoundError","RunConflict","UnauthorizedError","PythonError","InvalidSubsetError","PresetNotFoundError","ConflictingExecutionParamsError","NoModeProvidedError"],"ConfigTypeOrError":["EnumConfigType","CompositeConfigType","RegularConfigType","PipelineNotFoundError","ConfigTypeNotFoundError","PythonError"],"ConfigType":["ArrayConfigType","CompositeConfigType","EnumConfigType","NullableConfigType","RegularConfigType","ScalarUnionConfigType","MapConfigType"],"WrappingConfigType":["ArrayConfigType","NullableConfigType"],"DagsterType":["ListDagsterType","NullableDagsterType","RegularDagsterType"],"DagsterTypeOrError":["RegularDagsterType","PipelineNotFoundError","DagsterTypeNotFoundError","PythonError"],"WrappingDagsterType":["ListDagsterType","NullableDagsterType"],"Error":["AssetCheckNeedsMigrationError","AssetCheckNeedsUserCodeUpgrade","AssetCheckNeedsAgentUpgradeError","PartitionKeysNotFoundError","AssetNotFoundError","ConflictingExecutionParamsError","ConfigTypeNotFoundError","DagsterTypeNotFoundError","InvalidPipelineRunsFilterError","InvalidSubsetError","ModeNotFoundError","NoModeProvidedError","PartitionSetNotFoundError","PipelineNotFoundError","RunConflict","PipelineSnapshotNotFoundError","PresetNotFoundError","PythonError","ErrorChainLink","UnauthorizedError","ReloadNotSupported","RepositoryLocationNotFound","RepositoryNotFoundError","ResourceNotFoundError","RunGroupNotFoundError","RunNotFoundError","ScheduleNotFoundError","SchedulerNotDefinedError","SensorNotFoundError","UnsupportedOperationError","DuplicateDynamicPartitionError","InstigationStateNotFoundError","SolidStepStatusUnavailableError","GraphNotFoundError","BackfillNotFoundError","PartitionSubsetDeserializationError","AutoMaterializeAssetEvaluationNeedsMigrationError"],"PipelineRunConflict":["RunConflict"],"PipelineRunNotFoundError":["RunNotFoundError"],"RepositoriesOrError":["RepositoryConnection","RepositoryNotFoundError","PythonError"],"LocationDocsJsonOrError":["LocationDocsJson","PythonError"],"RepositoryOrError":["PythonError","Repository","RepositoryNotFoundError"],"WorkspaceLocationEntryOrError":["WorkspaceLocationEntry","PythonError"],"InstigationTypeSpecificData":["SensorData","ScheduleData"],"InstigationStateOrError":["InstigationState","InstigationStateNotFoundError","PythonError"],"InstigationStatesOrError":["InstigationStates","PythonError"],"MetadataEntry":["TableColumnLineageMetadataEntry","TableSchemaMetadataEntry","TableMetadataEntry","FloatMetadataEntry","IntMetadataEntry","JsonMetadataEntry","BoolMetadataEntry","MarkdownMetadataEntry","PathMetadataEntry","NotebookMetadataEntry","PythonArtifactMetadataEntry","TextMetadataEntry","UrlMetadataEntry","PipelineRunMetadataEntry","AssetMetadataEntry","JobMetadataEntry","CodeReferencesMetadataEntry","NullMetadataEntry","TimestampMetadataEntry","PoolMetadataEntry"],"SourceLocation":["LocalFileCodeReference","UrlCodeReference"],"PartitionRunConfigOrError":["PartitionRunConfig","PythonError"],"AssetBackfillStatus":["AssetPartitionsStatusCounts","UnpartitionedAssetStatus"],"PartitionSetOrError":["PartitionSet","PartitionSetNotFoundError","PythonError"],"PartitionSetsOrError":["PartitionSets","PipelineNotFoundError","PythonError"],"PartitionsOrError":["Partitions","PythonError"],"PartitionStatusesOrError":["PartitionStatuses","PythonError"],"PartitionTagsOrError":["PartitionTags","PythonError"],"RunConfigSchemaOrError":["RunConfigSchema","PipelineNotFoundError","InvalidSubsetError","ModeNotFoundError","PythonError"],"LaunchRunResult":["LaunchRunSuccess","InvalidStepError","InvalidOutputError","RunConfigValidationInvalid","PipelineNotFoundError","RunConflict","UnauthorizedError","PythonError","InvalidSubsetError","PresetNotFoundError","ConflictingExecutionParamsError","NoModeProvidedError"],"LaunchRunReexecutionResult":["LaunchRunSuccess","InvalidStepError","InvalidOutputError","RunConfigValidationInvalid","PipelineNotFoundError","RunConflict","UnauthorizedError","PythonError","InvalidSubsetError","PresetNotFoundError","ConflictingExecutionParamsError","NoModeProvidedError"],"LaunchPipelineRunSuccess":["LaunchRunSuccess"],"RunsOrError":["Runs","InvalidPipelineRunsFilterError","PythonError"],"PipelineRuns":["Runs"],"RunGroupOrError":["RunGroup","RunGroupNotFoundError","PythonError"],"SensorOrError":["Sensor","SensorNotFoundError","UnauthorizedError","PythonError"],"SensorsOrError":["Sensors","RepositoryNotFoundError","PythonError"],"StopSensorMutationResultOrError":["StopSensorMutationResult","UnauthorizedError","PythonError"],"ISolidDefinition":["CompositeSolidDefinition","SolidDefinition"],"SolidContainer":["Pipeline","PipelineSnapshot","Job","CompositeSolidDefinition","Graph"],"SolidStepStatsOrError":["SolidStepStatsConnection","SolidStepStatusUnavailableError"],"WorkspaceOrError":["Workspace","PythonError"],"WorkspaceLocationStatusEntriesOrError":["WorkspaceLocationStatusEntries","PythonError"],"ResourcesOrError":["ResourceConnection","PipelineNotFoundError","InvalidSubsetError","PythonError"],"GraphOrError":["Graph","GraphNotFoundError","PythonError"],"ResourceDetailsOrError":["ResourceDetails","ResourceNotFoundError","PythonError"],"ResourceDetailsListOrError":["ResourceDetailsList","RepositoryNotFoundError","PythonError"],"EnvVarWithConsumersOrError":["EnvVarWithConsumersList","PythonError"],"RunsFeedConnectionOrError":["RunsFeedConnection","PythonError"],"RunsFeedCountOrError":["RunsFeedCount","PythonError"],"RunTagKeysOrError":["PythonError","RunTagKeys"],"RunTagsOrError":["PythonError","RunTags"],"RunIdsOrError":["RunIds","InvalidPipelineRunsFilterError","PythonError"],"AssetRecordsOrError":["AssetRecordConnection","PythonError"],"AssetNodeOrError":["AssetNode","AssetNotFoundError"],"PartitionBackfillOrError":["PartitionBackfill","BackfillNotFoundError","PythonError"],"PartitionBackfillsOrError":["PartitionBackfills","PythonError"],"EventConnectionOrError":["EventConnection","RunNotFoundError","PythonError"],"AutoMaterializeAssetEvaluationRecordsOrError":["AutoMaterializeAssetEvaluationRecords","AutoMaterializeAssetEvaluationNeedsMigrationError"],"PartitionKeysOrError":["PartitionKeys","PartitionSubsetDeserializationError"],"AutoMaterializeRuleEvaluationData":["TextRuleEvaluationData","ParentMaterializedRuleEvaluationData","WaitingOnKeysRuleEvaluationData"],"AssetConditionEvaluationNode":["UnpartitionedAssetConditionEvaluationNode","PartitionedAssetConditionEvaluationNode","SpecificPartitionAssetConditionEvaluationNode"],"AssetConditionEvaluationRecordsOrError":["AssetConditionEvaluationRecords","AutoMaterializeAssetEvaluationNeedsMigrationError"],"EntityKey":["AssetKey","AssetCheckhandle"],"SensorDryRunResult":["PythonError","SensorNotFoundError","DryRunInstigationTick"],"ScheduleDryRunResult":["DryRunInstigationTick","PythonError","ScheduleNotFoundError"],"TerminateRunsResultOrError":["TerminateRunsResult","PythonError"],"AssetWipeMutationResult":["AssetNotFoundError","UnauthorizedError","PythonError","UnsupportedOperationError","AssetWipeSuccess"],"ReportRunlessAssetEventsResult":["UnauthorizedError","PythonError","ReportRunlessAssetEventsSuccess"],"ResumeBackfillResult":["ResumeBackfillSuccess","UnauthorizedError","PythonError"],"CancelBackfillResult":["CancelBackfillSuccess","UnauthorizedError","PythonError"],"LogTelemetryMutationResult":["LogTelemetrySuccess","PythonError"],"AddDynamicPartitionResult":["AddDynamicPartitionSuccess","UnauthorizedError","PythonError","DuplicateDynamicPartitionError"],"DeleteDynamicPartitionsResult":["DeleteDynamicPartitionsSuccess","UnauthorizedError","PythonError"]}
\ No newline at end of file
diff --git a/js_modules/dagster-ui/packages/ui-core/src/graphql/schema.graphql b/js_modules/dagster-ui/packages/ui-core/src/graphql/schema.graphql
index c60f838e67f0c..f6ffd0b459aa3 100644
--- a/js_modules/dagster-ui/packages/ui-core/src/graphql/schema.graphql
+++ b/js_modules/dagster-ui/packages/ui-core/src/graphql/schema.graphql
@@ -3507,6 +3507,7 @@ type Query {
   runConfigSchemaOrError(selector: PipelineSelector!, mode: String): RunConfigSchemaOrError!
   instance: Instance!
   assetsOrError(prefix: [String!], cursor: String, limit: Int): AssetsOrError!
+  assetRecordsOrError(prefix: [String!], cursor: String, limit: Int): AssetRecordsOrError!
   assetOrError(assetKey: AssetKeyInput!): AssetOrError!
   assetNodes(
     group: AssetGroupSelector
@@ -3695,6 +3696,18 @@ type RunIds {
   results: [String!]!
 }
 
+union AssetRecordsOrError = AssetRecordConnection | PythonError
+
+type AssetRecordConnection {
+  assets: [AssetRecord!]!
+  cursor: String
+}
+
+type AssetRecord {
+  id: String!
+  key: AssetKey!
+}
+
 input AssetGroupSelector {
   groupName: String!
   repositoryName: String!
diff --git a/js_modules/dagster-ui/packages/ui-core/src/graphql/types.ts b/js_modules/dagster-ui/packages/ui-core/src/graphql/types.ts
index 907d831ada096..2ddd0e5e4e7e0 100644
--- a/js_modules/dagster-ui/packages/ui-core/src/graphql/types.ts
+++ b/js_modules/dagster-ui/packages/ui-core/src/graphql/types.ts
@@ -704,6 +704,20 @@ export type AssetPartitionsStatusCounts = {
   numPartitionsTargeted: Scalars['Int']['output'];
 };
 
+export type AssetRecord = {
+  __typename: 'AssetRecord';
+  id: Scalars['String']['output'];
+  key: AssetKey;
+};
+
+export type AssetRecordConnection = {
+  __typename: 'AssetRecordConnection';
+  assets: Array<AssetRecord>;
+  cursor: Maybe<Scalars['String']['output']>;
+};
+
+export type AssetRecordsOrError = AssetRecordConnection | PythonError;
+
 export type AssetSelection = {
   __typename: 'AssetSelection';
   assetChecks: Array<AssetCheckhandle>;
@@ -3979,6 +3993,7 @@ export type Query = {
   assetNodeOrError: AssetNodeOrError;
   assetNodes: Array<AssetNode>;
   assetOrError: AssetOrError;
+  assetRecordsOrError: AssetRecordsOrError;
   assetsLatestInfo: Array<AssetLatestInfo>;
   assetsOrError: AssetsOrError;
   autoMaterializeAssetEvaluationsOrError: Maybe<AutoMaterializeAssetEvaluationRecordsOrError>;
@@ -4086,6 +4101,12 @@ export type QueryAssetOrErrorArgs = {
   assetKey: AssetKeyInput;
 };
 
+export type QueryAssetRecordsOrErrorArgs = {
+  cursor?: InputMaybe<Scalars['String']['input']>;
+  limit?: InputMaybe<Scalars['Int']['input']>;
+  prefix?: InputMaybe<Array<Scalars['String']['input']>>;
+};
+
 export type QueryAssetsLatestInfoArgs = {
   assetKeys: Array<AssetKeyInput>;
 };
@@ -7337,6 +7358,37 @@ export const buildAssetPartitionsStatusCounts = (
   };
 };
 
+export const buildAssetRecord = (
+  overrides?: Partial<AssetRecord>,
+  _relationshipsToOmit: Set<string> = new Set(),
+): {__typename: 'AssetRecord'} & AssetRecord => {
+  const relationshipsToOmit: Set<string> = new Set(_relationshipsToOmit);
+  relationshipsToOmit.add('AssetRecord');
+  return {
+    __typename: 'AssetRecord',
+    id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 'nemo',
+    key:
+      overrides && overrides.hasOwnProperty('key')
+        ? overrides.key!
+        : relationshipsToOmit.has('AssetKey')
+          ? ({} as AssetKey)
+          : buildAssetKey({}, relationshipsToOmit),
+  };
+};
+
+export const buildAssetRecordConnection = (
+  overrides?: Partial<AssetRecordConnection>,
+  _relationshipsToOmit: Set<string> = new Set(),
+): {__typename: 'AssetRecordConnection'} & AssetRecordConnection => {
+  const relationshipsToOmit: Set<string> = new Set(_relationshipsToOmit);
+  relationshipsToOmit.add('AssetRecordConnection');
+  return {
+    __typename: 'AssetRecordConnection',
+    assets: overrides && overrides.hasOwnProperty('assets') ? overrides.assets! : [],
+    cursor: overrides && overrides.hasOwnProperty('cursor') ? overrides.cursor! : 'voluptatem',
+  };
+};
+
 export const buildAssetSelection = (
   overrides?: Partial<AssetSelection>,
   _relationshipsToOmit: Set<string> = new Set(),
@@ -12780,6 +12832,12 @@ export const buildQuery = (
         : relationshipsToOmit.has('Asset')
           ? ({} as Asset)
           : buildAsset({}, relationshipsToOmit),
+    assetRecordsOrError:
+      overrides && overrides.hasOwnProperty('assetRecordsOrError')
+        ? overrides.assetRecordsOrError!
+        : relationshipsToOmit.has('AssetRecordConnection')
+          ? ({} as AssetRecordConnection)
+          : buildAssetRecordConnection({}, relationshipsToOmit),
     assetsLatestInfo:
       overrides && overrides.hasOwnProperty('assetsLatestInfo') ? overrides.assetsLatestInfo! : [],
     assetsOrError:
diff --git a/python_modules/dagster-graphql/dagster_graphql/implementation/fetch_assets.py b/python_modules/dagster-graphql/dagster_graphql/implementation/fetch_assets.py
index c4f3b349bfc1a..58cdfdcb0d4de 100644
--- a/python_modules/dagster-graphql/dagster_graphql/implementation/fetch_assets.py
+++ b/python_modules/dagster-graphql/dagster_graphql/implementation/fetch_assets.py
@@ -57,7 +57,10 @@
         GrapheneMultiPartitionStatuses,
         GrapheneTimePartitionStatuses,
     )
-    from dagster_graphql.schema.roots.assets import GrapheneAssetConnection
+    from dagster_graphql.schema.roots.assets import (
+        GrapheneAssetConnection,
+        GrapheneAssetRecordConnection,
+    )
     from dagster_graphql.schema.util import ResolveInfo
 
 
@@ -75,6 +78,36 @@ def _normalize_asset_cursor_str(cursor_string: Optional[str]) -> Optional[str]:
         return cursor_string
 
 
+def get_asset_records(
+    graphene_info: "ResolveInfo",
+    prefix: Optional[Sequence[str]] = None,
+    cursor: Optional[str] = None,
+    limit: Optional[int] = None,
+) -> "GrapheneAssetRecordConnection":
+    from dagster_graphql.schema.pipelines.pipeline import GrapheneAssetRecord
+    from dagster_graphql.schema.roots.assets import GrapheneAssetRecordConnection
+
+    instance = graphene_info.context.instance
+
+    normalized_cursor_str = _normalize_asset_cursor_str(cursor)
+    materialized_assets = sorted(
+        # TODO(salazarm): Replace this with `get_asset_records` once that supports pagination arguments.
+        instance.get_asset_keys(prefix=prefix, limit=limit, cursor=normalized_cursor_str),
+        key=str,
+    )
+
+    return GrapheneAssetRecordConnection(
+        assets=[
+            GrapheneAssetRecord(
+                id=asset_key.to_string(),
+                key=asset_key,
+            )
+            for asset_key in materialized_assets
+        ],
+        cursor=materialized_assets[-1].to_string() if materialized_assets else None,
+    )
+
+
 def get_assets(
     graphene_info: "ResolveInfo",
     prefix: Optional[Sequence[str]] = None,
diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/pipelines/pipeline.py b/python_modules/dagster-graphql/dagster_graphql/schema/pipelines/pipeline.py
index a2ccfd3681e3d..073b035a4a896 100644
--- a/python_modules/dagster-graphql/dagster_graphql/schema/pipelines/pipeline.py
+++ b/python_modules/dagster-graphql/dagster_graphql/schema/pipelines/pipeline.py
@@ -1215,3 +1215,11 @@ def _asset_key_input_list_to_asset_key_set(
     return (
         {key_input.to_asset_key() for key_input in asset_keys} if asset_keys is not None else None
     )
+
+
+class GrapheneAssetRecord(graphene.ObjectType):
+    id = graphene.NonNull(graphene.String)
+    key = graphene.NonNull(GrapheneAssetKey)
+
+    class Meta:
+        name = "AssetRecord"
diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/roots/assets.py b/python_modules/dagster-graphql/dagster_graphql/schema/roots/assets.py
index 726c25ab89155..03ee0b79aa782 100644
--- a/python_modules/dagster-graphql/dagster_graphql/schema/roots/assets.py
+++ b/python_modules/dagster-graphql/dagster_graphql/schema/roots/assets.py
@@ -1,7 +1,7 @@
 import graphene
 
 from dagster_graphql.schema.errors import GrapheneAssetNotFoundError, GraphenePythonError
-from dagster_graphql.schema.pipelines.pipeline import GrapheneAsset
+from dagster_graphql.schema.pipelines.pipeline import GrapheneAsset, GrapheneAssetRecord
 from dagster_graphql.schema.util import non_null_list
 
 
@@ -13,12 +13,26 @@ class Meta:
         name = "AssetConnection"
 
 
+class GrapheneAssetRecordConnection(graphene.ObjectType):
+    assets = non_null_list(GrapheneAssetRecord)
+    cursor = graphene.Field(graphene.String)
+
+    class Meta:
+        name = "AssetRecordConnection"
+
+
 class GrapheneAssetsOrError(graphene.Union):
     class Meta:
         types = (GrapheneAssetConnection, GraphenePythonError)
         name = "AssetsOrError"
 
 
+class GrapheneAssetRecordsOrError(graphene.Union):
+    class Meta:
+        types = (GrapheneAssetRecordConnection, GraphenePythonError)
+        name = "AssetRecordsOrError"
+
+
 class GrapheneAssetOrError(graphene.Union):
     class Meta:
         types = (GrapheneAsset, GrapheneAssetNotFoundError)
diff --git a/python_modules/dagster-graphql/dagster_graphql/schema/roots/query.py b/python_modules/dagster-graphql/dagster_graphql/schema/roots/query.py
index b77b1c97285c7..638ecc2fdeea3 100644
--- a/python_modules/dagster-graphql/dagster_graphql/schema/roots/query.py
+++ b/python_modules/dagster-graphql/dagster_graphql/schema/roots/query.py
@@ -43,6 +43,7 @@
     get_asset,
     get_asset_node,
     get_asset_node_definition_collisions,
+    get_asset_records,
     get_assets,
 )
 from dagster_graphql.implementation.fetch_auto_materialize_asset_evaluations import (
@@ -176,7 +177,11 @@
     GrapheneResourceDetailsListOrError,
     GrapheneResourceDetailsOrError,
 )
-from dagster_graphql.schema.roots.assets import GrapheneAssetOrError, GrapheneAssetsOrError
+from dagster_graphql.schema.roots.assets import (
+    GrapheneAssetOrError,
+    GrapheneAssetRecordsOrError,
+    GrapheneAssetsOrError,
+)
 from dagster_graphql.schema.roots.execution_plan import GrapheneExecutionPlanOrError
 from dagster_graphql.schema.roots.pipeline import GrapheneGraphOrError, GraphenePipelineOrError
 from dagster_graphql.schema.run_config import GrapheneRunConfigSchemaOrError
@@ -459,9 +464,16 @@ class Meta:
         prefix=graphene.List(graphene.NonNull(graphene.String)),
         cursor=graphene.String(),
         limit=graphene.Int(),
-        description="Retrieve assets after applying a prefix filter, cursor, and limit.",
+        description="Retrieve all assets (both with or without a definition) after applying a prefix filter, cursor, and limit.",
     )
 
+    assetRecordsOrError = graphene.Field(
+        graphene.NonNull(GrapheneAssetRecordsOrError),
+        prefix=graphene.List(graphene.NonNull(graphene.String)),
+        cursor=graphene.String(),
+        limit=graphene.Int(),
+        description="Retrieve materialized asset records after applying a prefix filter, cursor, and limit.",
+    )
     assetOrError = graphene.Field(
         graphene.NonNull(GrapheneAssetOrError),
         assetKey=graphene.Argument(graphene.NonNull(GrapheneAssetKeyInput)),
@@ -475,7 +487,7 @@ class Meta:
         assetKeys=graphene.Argument(graphene.List(graphene.NonNull(GrapheneAssetKeyInput))),
         loadMaterializations=graphene.Boolean(default_value=False),
         description=(
-            "Retrieve asset nodes after applying a filter on asset group, job, and asset keys."
+            "Retrieve asset nodes (assets with a definition) after applying a filter on asset group, job, and asset keys."
         ),
     )
 
@@ -1123,6 +1135,21 @@ def resolve_assetsOrError(
             limit=limit,
         )
 
+    @capture_error
+    def resolve_assetRecordsOrError(
+        self,
+        graphene_info: ResolveInfo,
+        prefix: Optional[Sequence[str]] = None,
+        cursor: Optional[str] = None,
+        limit: Optional[int] = None,
+    ):
+        return get_asset_records(
+            graphene_info,
+            prefix=prefix,
+            cursor=cursor,
+            limit=limit,
+        )
+
     def resolve_assetOrError(self, graphene_info: ResolveInfo, assetKey: GrapheneAssetKeyInput):
         return get_asset(graphene_info, AssetKey.from_graphql_input(assetKey))
 
diff --git a/python_modules/dagster-test/dagster_test/toys/big_honkin_asset_graph.py b/python_modules/dagster-test/dagster_test/toys/big_honkin_asset_graph.py
index 3060897723992..aff2c0af9bf0c 100644
--- a/python_modules/dagster-test/dagster_test/toys/big_honkin_asset_graph.py
+++ b/python_modules/dagster-test/dagster_test/toys/big_honkin_asset_graph.py
@@ -1,6 +1,14 @@
 import random
 
-from dagster import AssetKey, AssetsDefinition, asset
+from dagster import (
+    AssetKey,
+    AssetMaterialization,
+    AssetsDefinition,
+    OpExecutionContext,
+    asset,
+    job,
+    op,
+)
 
 N_ASSETS = 1000
 
@@ -31,3 +39,19 @@ def some_asset():
 
 
 assets = generate_big_honkin_assets()
+
+
+letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+
+@op
+def create_non_sda_asset(context: OpExecutionContext):
+    for i in range(100000):
+        context.log_event(
+            AssetMaterialization(asset_key=f"{letters[i % len(letters)]}_external_asset_{i}")
+        )
+
+
+@job
+def big_honkin_assets_job():
+    create_non_sda_asset()
diff --git a/python_modules/dagster-test/dagster_test/toys/repo.py b/python_modules/dagster-test/dagster_test/toys/repo.py
index 1ad57b0a88bc7..fbd9d64cd00e4 100644
--- a/python_modules/dagster-test/dagster_test/toys/repo.py
+++ b/python_modules/dagster-test/dagster_test/toys/repo.py
@@ -23,6 +23,7 @@
 from dagster_test.toys.auto_materializing.repo_2 import (
     auto_materialize_repo_2 as auto_materialize_repo_2,
 )
+from dagster_test.toys.big_honkin_asset_graph import big_honkin_assets_job
 from dagster_test.toys.branches import branch_failed_job, branch_job
 from dagster_test.toys.composition import composition_job
 from dagster_test.toys.cross_repo_assets import (
@@ -185,9 +186,7 @@ def long_asset_keys_repository():
 
 @repository
 def big_honkin_assets_repository():
-    return cast(
-        "Sequence[AssetsDefinition]", [load_assets_from_modules([big_honkin_asset_graph_module])]
-    )
+    return [load_assets_from_modules([big_honkin_asset_graph_module]), big_honkin_assets_job]
 
 
 @repository


Reviews:
user: login: gibsondan
body: Maybe GrapheneAssetRecord?
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-28T13:25:35Z
user: login: graphite-app[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T13:49:37Z
user: login: alangenfeld
body: i like having an api that is aspirationally 1:1 with the public `get_asset_records` api. 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T21:39:03Z

Review comments:
diff_hunk: @@ -75,6 +78,34 @@ def _normalize_asset_cursor_str(cursor_string: Optional[str]) -> Optional[str]:
         return cursor_string
 
 
+def get_asset_records(
+    graphene_info: "ResolveInfo",
+    prefix: Optional[Sequence[str]] = None,
+    cursor: Optional[str] = None,
+    limit: Optional[int] = None,
+) -> "GrapheneAssetRecordConnection":
+    from dagster_graphql.schema.pipelines.pipeline import GrapheneAssetRecord
+    from dagster_graphql.schema.roots.assets import GrapheneAssetRecordConnection
+
+    instance = graphene_info.context.instance
+
+    normalized_cursor_str = _normalize_asset_cursor_str(cursor)
+    materialized_assets = sorted(
+        instance.get_asset_keys(prefix=prefix, limit=limit, cursor=normalized_cursor_str), key=str
+    )
+
+    return GrapheneAssetRecordConnection(
+        assets=[
+            GrapheneAssetRecord(
+                id=asset_key.to_string(),
+                key=asset_key,
+            )
+            for asset_key in materialized_assets
+        ],
+        cursor=materialized_assets[-1].to_string() if materialized_assets else None,
path: python_modules/dagster-graphql/dagster_graphql/implementation/fetch_assets.py
user: login: graphite-app[bot]
body: There's a potential issue with cursor pagination in this code. When setting the cursor to `materialized_assets[-1].to_string() if materialized_assets else None`, the code correctly handles empty results but doesn't account for whether there are more results available.

If `get_asset_keys()` returns exactly the number of items requested by the `limit` parameter, the cursor will be set to the last item, but there's no way to know if there are more items beyond this point. This could lead to pagination stopping prematurely.

Consider modifying the cursor logic to either:
1. Request `limit + 1` items and return only `limit` items in the response, using the extra item to determine if more results exist
2. Add a separate method to check if more results exist beyond the current page
3. Have `get_asset_keys()` return both the results and a boolean indicating if more results are available

This would ensure that pagination works correctly even when the exact number of requested items is returned.

*Spotted by [Diamond](https://app.graphite.dev/diamond/?org=dagster-io&ref=ai-review-comment)*<i class='graphite__hidden'><br /><br />Is this helpful? React 👍 or 👎 to let us know.</i>
created_at: 2025-04-28T13:49:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -75,6 +78,34 @@ def _normalize_asset_cursor_str(cursor_string: Optional[str]) -> Optional[str]:
         return cursor_string
 
 
+def get_asset_records(
+    graphene_info: "ResolveInfo",
+    prefix: Optional[Sequence[str]] = None,
+    cursor: Optional[str] = None,
+    limit: Optional[int] = None,
+) -> "GrapheneAssetRecordConnection":
+    from dagster_graphql.schema.pipelines.pipeline import GrapheneAssetRecord
+    from dagster_graphql.schema.roots.assets import GrapheneAssetRecordConnection
+
+    instance = graphene_info.context.instance
+
+    normalized_cursor_str = _normalize_asset_cursor_str(cursor)
+    materialized_assets = sorted(
+        instance.get_asset_keys(prefix=prefix, limit=limit, cursor=normalized_cursor_str), key=str
+    )
path: python_modules/dagster-graphql/dagster_graphql/implementation/fetch_assets.py
user: login: alangenfeld
body: if were punting on it leave a comment that the expectation is that this will get replaced with `get_asset_records` once the pagination arguments are exposed 
created_at: 2025-04-28T21:36:25Z
author_association: CONTRIBUTOR

PR comments:
user: login: jamiedemaria
created_at: 2025-04-28T13:33:57Z
body: haven't looked at the implementation yet, but adding a comment to the new class (and maybe also GrapheneAssetNode and GrapheneAsset) describing the use cases each class is for would be helpful. The difference between GrapheneAssetNode and GrapheneAsset isn't immediately clear, and adding a third class to that will just add to the confusion
author_association: CONTRIBUTOR
user: login: salazarm
created_at: 2025-04-28T13:46:59Z
body: @gibsondan Ah looks like I can just use `get_asset_records` then that seems to do what we want
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-28T13:47:27Z
body: Deploy preview for _dagit-core-storybook_ ready!

✅ Preview
https://dagit-core-storybook-op0jltw7a-elementl.vercel.app
https://salazarm-asset-state-resolver.core-storybook.dagster-docs.io

Built with commit caa3ae7bd99dbe59e2760dd9b699525d9c180bab.
This pull request is being automatically deployed with [vercel-action](https://github.com/marketplace/actions/vercel-action)
author_association: CONTRIBUTOR
user: login: salazarm
created_at: 2025-04-28T13:52:43Z
body: actually nvm. the `get_asset_records` function does [extra fetching](https://github.com/dagster-io/dagster/blob/5bbc6f666ee70b34cb3801ec03963a2b7ca6d3da/python_modules/dagster/dagster/_core/storage/event_log/sql_event_log.py#L1246) that we don't need
author_association: CONTRIBUTOR
user: login: jamiedemaria
created_at: 2025-04-28T13:55:36Z
body: the internal impl can get all the info needed from one db row https://github.com/dagster-io/internal/blob/master/dagster-cloud/python_modules/dagster-cloud-backend/dagster_cloud_backend/storage/host_cloud/event_log_storage/storage.py#L2715
author_association: CONTRIBUTOR
user: login: gibsondan
created_at: 2025-04-28T13:58:22Z
body: oh, I forgot we already have GrapheneAsset - that already takes an optional definition object in its constructor, do we need a brand new type here?
author_association: MEMBER
user: login: salazarm
created_at: 2025-04-28T14:06:16Z
body: @gibsondan I think it would be confusing that depending on the resolver the same GrapheneAsset (same id) can return a definition that is null or not. We're not using apollo caching anymore for this but it would have caused bugs if we were where the apollo cache would wipe out the definition. 
author_association: CONTRIBUTOR
user: login: salazarm
created_at: 2025-04-28T14:07:16Z
body: @jamiedemaria I can look into it in a follow-up. I'd need to update the parameters to allow pagination
author_association: CONTRIBUTOR
user: login: neilfulwiler
created_at: 2025-04-28T15:03:32Z
body: is there a reason we can't do this in the existing resolver itself? ie skip the asset node load if the query isn't requesting that data (I think thats the graphql way? or is that a hack?)
author_association: CONTRIBUTOR
user: login: gibsondan
created_at: 2025-04-28T15:05:50Z
body: Neil i think there is a semantic difference here - if you want to include
assets that exist in the graph but have never been materialized, the only
way to get that information is to look at the graph

On Mon, Apr 28, 2025 at 10:03 AM Neil Fulwiler ***@***.***>
wrote:

> *neilfulwiler* left a comment (dagster-io/dagster#29626)
> <https://github.com/dagster-io/dagster/pull/29626#issuecomment-2835557952>
>
> is there a reason we can't do this in the existing resolver itself? ie
> skip the asset node load if the query isn't requesting that data (I think
> thats the graphql way? or is that a hack?)
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/dagster-io/dagster/pull/29626#issuecomment-2835557952>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/ACAPJC5VGPRGVQHTNPFLWN323Y7NTAVCNFSM6AAAAAB4ANEOUKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDQMZVGU2TOOJVGI>
> .
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
>

author_association: MEMBER
user: login: salazarm
created_at: 2025-04-28T17:22:44Z
body: @gibsondan  I can switch to using GrapheneAsset in this case but I think we wanted to have a separate object to avoid exposing fields that could accidentally do something expensive 
author_association: CONTRIBUTOR