From 9c2475a5fe5db884817aa156714b1598fef01bd5 Mon Sep 17 00:00:00 2001
From: Milho001 <l.milhomemfrancochristino@uu.nl>
Date: Fri, 19 Jan 2024 18:28:39 +0000
Subject: [PATCH] feat(ws): all communication logic refactor

---
 apps/web/package.json                         |   3 +-
 apps/web/src/app/app.tsx                      |  97 +----
 ...eSelector.tsx => dbConnectionSelector.tsx} | 103 +++---
 .../forms/AddDatabase/mockDatabases.ts        |  70 ----
 .../forms/AddDatabase/newdatabase.tsx         | 349 ------------------
 .../DatabaseManagement/forms/databaseForm.tsx | 180 +++++++++
 .../forms/mockSaveStates.tsx                  | 113 ++++++
 .../DatabaseManagement/forms/settings.tsx     | 322 +++++++---------
 .../src/components/navbar/databasemenu.tsx    |  10 +-
 apps/web/src/components/navbar/navbar.tsx     |  37 +-
 .../shared/lib/components/dropdowns/index.tsx |   6 +-
 libs/shared/lib/data-access/api/database.ts   | 168 ---------
 libs/shared/lib/data-access/api/eventBus.tsx  | 126 +++++++
 libs/shared/lib/data-access/api/index.ts      |   5 +-
 libs/shared/lib/data-access/api/query.ts      |  42 ---
 libs/shared/lib/data-access/api/schema.ts     |  38 --
 libs/shared/lib/data-access/api/wsQuery.ts    |  16 +
 libs/shared/lib/data-access/api/wsSchema.ts   |  17 +
 libs/shared/lib/data-access/api/wsState.tsx   | 102 +++++
 .../authorization/dashboardAlerts.tsx         |  83 +++--
 .../lib/data-access/authorization/useAuth.tsx |  52 +--
 .../WebSocketHandler.tsx                      |  62 +++-
 .../lib/data-access/socket/broker/index.tsx   |  18 +-
 libs/shared/lib/data-access/socket/types.ts   |  50 +++
 .../shared/lib/data-access/store/authSlice.ts |  35 +-
 .../lib/data-access/store/configSlice.ts      |  32 +-
 libs/shared/lib/data-access/store/hooks.ts    |   8 +-
 libs/shared/lib/data-access/store/index.ts    |   2 +-
 .../data-access/store/querybuilderSlice.ts    |  19 +-
 .../lib/data-access/store/sessionSlice.ts     |  52 ++-
 .../querybuilder/model/BackendQueryFormat.tsx |   5 +-
 .../lib/querybuilder/panel/querybuilder.tsx   |  17 +-
 .../panel/shemaquerybuilder.stories.tsx       |   4 +-
 .../stories/querybuilder-simple.stories.tsx   |   4 +-
 .../querybuilder-single-entity.stories.tsx    |   4 +-
 ...erybuilder-single-relationship.stories.tsx |   4 +-
 .../entitypill/entitypill-full.stories.tsx    |   9 +-
 .../relation-full_reactflow.stories.tsx       |   4 +-
 .../query-utils/query2backend.spec.ts         |   3 +-
 .../querybuilder/query-utils/query2backend.ts |   9 +-
 libs/shared/lib/schema/panel/schema.tsx       |   9 +-
 .../pills/nodes/relation/relation-node.tsx    |   2 -
 libs/shared/lib/vis/index.tsx                 |   2 -
 .../visualizations/nodelink/nodelinkvis.tsx   |   2 +-
 pnpm-lock.yaml                                |  37 +-
 45 files changed, 1132 insertions(+), 1200 deletions(-)
 rename apps/web/src/components/navbar/DatabaseManagement/{DatabaseSelector.tsx => dbConnectionSelector.tsx} (64%)
 delete mode 100644 apps/web/src/components/navbar/DatabaseManagement/forms/AddDatabase/mockDatabases.ts
 delete mode 100644 apps/web/src/components/navbar/DatabaseManagement/forms/AddDatabase/newdatabase.tsx
 create mode 100644 apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx
 create mode 100644 apps/web/src/components/navbar/DatabaseManagement/forms/mockSaveStates.tsx
 delete mode 100644 libs/shared/lib/data-access/api/database.ts
 create mode 100644 libs/shared/lib/data-access/api/eventBus.tsx
 delete mode 100644 libs/shared/lib/data-access/api/query.ts
 delete mode 100644 libs/shared/lib/data-access/api/schema.ts
 create mode 100644 libs/shared/lib/data-access/api/wsQuery.ts
 create mode 100644 libs/shared/lib/data-access/api/wsSchema.ts
 create mode 100644 libs/shared/lib/data-access/api/wsState.tsx
 create mode 100644 libs/shared/lib/data-access/socket/types.ts

diff --git a/apps/web/package.json b/apps/web/package.json
index 6878b5202..5ddc36735 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -26,7 +26,8 @@
     "react-redux": "^8.0.5",
     "react-router-dom": "^6.8.1",
     "reactflow": "11.4.0-next.1",
-    "styled-components": "^5.3.6"
+    "styled-components": "^5.3.6",
+    "use-immer": "^0.9.0"
   },
   "devDependencies": {
     "@import-meta-env/cli": "^0.6.5",
diff --git a/apps/web/src/app/app.tsx b/apps/web/src/app/app.tsx
index 9ddedfcbd..f8bc379dd 100644
--- a/apps/web/src/app/app.tsx
+++ b/apps/web/src/app/app.tsx
@@ -1,27 +1,15 @@
 import React, { useEffect, useRef, useState } from 'react';
 import {
-  readInSchemaFromBackend,
-  useAuth,
   useAuthorizationCache,
-  useDatabaseAPI,
-  useQueryAPI,
   useQuerybuilderGraph,
-  useQuerybuilderHash,
-  useSchemaAPI,
   useSessionCache,
 } from '@graphpolaris/shared/lib/data-access';
-import { WebSocketHandler } from '@graphpolaris/shared/lib/data-access/socket';
-import Broker from '@graphpolaris/shared/lib/data-access/socket/broker';
 import {
-  assignNewGraphQueryResult,
   useAppDispatch,
-  useConfig,
   useML,
-  useMLEnabledHash,
   useQuerybuilderSettings,
 } from '@graphpolaris/shared/lib/data-access/store';
 import {
-  GraphQueryResultFromBackendPayload,
   resetGraphQueryResults,
   queryingBackend,
 } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
@@ -29,95 +17,43 @@ import { Query2BackendQuery, QueryBuilder, QueryMultiGraph } from '@graphpolaris
 import { Schema } from '@graphpolaris/shared/lib/schema/panel';
 import { Navbar } from '../components/navbar/navbar';
 import { VisualizationPanel } from '@graphpolaris/shared/lib/vis/panel';
-import { SchemaFromBackend } from '@graphpolaris/shared/lib/schema';
-import { LinkPredictionInstance, setMLResult, allMLTypes } from '@graphpolaris/shared/lib/data-access/store/mlSlice';
 import { Resizable } from '@graphpolaris/shared/lib/components/Resizable';
 import { DashboardAlerts } from '@graphpolaris/shared/lib/data-access/authorization/dashboardAlerts';
-import { addError } from '@graphpolaris/shared/lib/data-access/store/configSlice';
+import { EventBus } from '@graphpolaris/shared/lib/data-access/api/eventBus';
 import Onboarding from '../components/onboarding/onboarding';
+import { wsQueryRequest } from '@graphpolaris/shared/lib/data-access/api/wsQuery';
 
 export interface App {}
 
 export function App(props: App) {
-  const { login } = useAuth();
   const auth = useAuthorizationCache();
-  const api = useDatabaseAPI();
-  const api_schema = useSchemaAPI();
-  const api_query = useQueryAPI();
-  const dispatch = useAppDispatch();
-  const session = useSessionCache();
   const query = useQuerybuilderGraph() as QueryMultiGraph;
-  const queryHash = useQuerybuilderHash();
-  const ws = useRef(new WebSocketHandler(import.meta.env.BACKEND_WSS_URL));
-  const [authCheck, setAuthCheck] = useState(false);
   const ml = useML();
-  const mlHash = useMLEnabledHash();
-  const config = useConfig();
+  const session = useSessionCache();
+  const dispatch = useAppDispatch();
   const queryBuilderSettings = useQuerybuilderSettings();
 
-  // for testing purposes
-  // useEffect(() => {
-  //   console.info('Authentification changed', auth)
-  // }, [auth]);
-
-  useEffect(() => {
-    // Default
-    Broker.instance().subscribe((data: SchemaFromBackend) => dispatch(readInSchemaFromBackend(data)), 'schema_result');
-    Broker.instance().subscribe((data: GraphQueryResultFromBackendPayload) => dispatch(assignNewGraphQueryResult(data)), 'query_result');
-    allMLTypes.forEach((mlType) => {
-      Broker.instance().subscribe((data: LinkPredictionInstance[]) => dispatch(setMLResult({ type: mlType, result: data })), mlType);
-    });
-
-    login();
-
-    return () => {
-      Broker.instance().unSubscribeAll('schema_result');
-      Broker.instance().unSubscribeAll('query_result');
-      allMLTypes.forEach((mlType) => {
-        Broker.instance().unSubscribeAll(mlType);
-      });
-    };
-  }, []);
-
-  useEffect(() => {
-    // New active database
-    if (session.currentDatabase) {
-      api_schema.RequestSchema(session.currentDatabase);
-    }
-  }, [session.currentDatabase]);
-
-  useEffect(() => {
-    // Newly (un)authorized
-    if (auth.authorized && auth.jwt) {
-      console.debug('App is authorized; Getting Databases', auth.authorized);
-      setAuthCheck(true);
-      ws.current.useAuth(auth).connect(() => {
-        api.GetAllDatabases({ updateSessionCache: true }).catch((e) => {
-          dispatch(addError(e.message));
-        });
-      });
-    } else {
-      // dispatch(logout());
-    }
-  }, [auth]);
-
   const runQuery = () => {
-    if (session?.currentDatabase && query) {
+    if (session?.currentSaveState && query) {
       if (query.nodes.length === 0) {
         dispatch(resetGraphQueryResults());
       } else {
         dispatch(queryingBackend());
-        api_query.execute(Query2BackendQuery(session.currentDatabase, query, queryBuilderSettings, ml));
+        wsQueryRequest(Query2BackendQuery(session.currentSaveState, query, queryBuilderSettings, ml));
       }
     }
   };
 
-  useEffect(() => {
-    runQuery();
-  }, [queryHash, mlHash, queryBuilderSettings]);
+  const [authCheck, setAuthCheck] = useState(false);
 
   return (
     <div className="h-screen w-screen overflow-clip">
+      <EventBus
+        onRunQuery={runQuery}
+        onAuthorized={() => {
+          setAuthCheck(true);
+        }}
+      />
       <Onboarding />
       <DashboardAlerts />
       <div className={'h-screen w-screen ' + (!auth.authorized ? 'blur-sm pointer-events-none ' : '')}>
@@ -136,12 +72,7 @@ export function App(props: App) {
                     <VisualizationPanel />
                   </div>
                   <div className="w-full h-full panel">
-                    <QueryBuilder
-                      onRunQuery={() => {
-                        console.log('Run Query');
-                        runQuery();
-                      }}
-                    />
+                    <QueryBuilder onRunQuery={runQuery} />
                   </div>
                 </Resizable>
               </div>
diff --git a/apps/web/src/components/navbar/DatabaseManagement/DatabaseSelector.tsx b/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx
similarity index 64%
rename from apps/web/src/components/navbar/DatabaseManagement/DatabaseSelector.tsx
rename to apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx
index 47016885b..d0309b037 100644
--- a/apps/web/src/components/navbar/DatabaseManagement/DatabaseSelector.tsx
+++ b/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx
@@ -1,27 +1,27 @@
 import React, { useEffect, useState } from 'react';
 import { Add, Delete, Settings } from '@mui/icons-material';
-import { DatabaseInfo, useAppDispatch, useDatabaseAPI, useSchemaGraph, useSessionCache } from '@graphpolaris/shared/lib/data-access';
-import { updateCurrentDatabase } from '@graphpolaris/shared/lib/data-access/store/sessionSlice';
+import { useAppDispatch, useSchemaGraph, useSessionCache, useAuthorizationCache } from '@graphpolaris/shared/lib/data-access';
+import { updateCurrentSaveState } from '@graphpolaris/shared/lib/data-access/store/sessionSlice';
 import { SettingsForm } from './forms/settings';
-import { NewDatabaseForm } from './forms/AddDatabase/newdatabase';
 import { LoadingSpinner } from '@graphpolaris/shared/lib/components/LoadingSpinner';
 import { addError } from '@graphpolaris/shared/lib/data-access/store/configSlice';
 import { DropdownButton, DropdownContainer, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns';
 import { clearQB } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
 import { clearSchema } from '@graphpolaris/shared/lib/data-access/store/schemaSlice';
+import { DatabaseStatus, SaveStateI, wsDeleteState } from '@graphpolaris/shared/lib/data-access/api/wsState';
 
 export default function DatabaseSelector({}) {
   const dispatch = useAppDispatch();
-  const api = useDatabaseAPI();
   const session = useSessionCache();
   const schemaGraph = useSchemaGraph();
+  const authCache = useAuthorizationCache();
   const dbSelectionMenuRef = React.useRef<HTMLDivElement>(null);
   const [hovered, setHovered] = useState<string | null>(null);
   const [connecting, setConnecting] = useState<boolean>(false);
   const [dbSelectionMenuOpen, setDbSelectionMenuOpen] = useState<boolean>(false);
-  const [settingsMenuOpen, setSettingsMenuOpen] = useState<boolean>(false);
-  const [selectedDatabase, setSelectedDatabase] = useState<DatabaseInfo | null>(null);
-  const [addDatabaseFormOpen, setAddDatabaseFormOpen] = useState<boolean>(false);
+  const [settingsMenuOpen, setSettingsMenuOpen] = useState<'create' | 'update' | undefined>(undefined);
+  const [selectedSaveState, setSelectedSaveState] = useState<SaveStateI | null>(null);
+  // const [addDbConnectionFormOpen, setAddDbConnectionFormOpen] = useState<boolean>(false);
 
   useEffect(() => {
     const handleClickOutside = ({ target }: MouseEvent) => {
@@ -45,7 +45,7 @@ export default function DatabaseSelector({}) {
       timeoutId = setTimeout(() => {
         dispatch(addError("Couldn't establish connection"));
         setConnecting(false);
-        dispatch(updateCurrentDatabase(undefined));
+        dispatch(updateCurrentSaveState(undefined));
         dispatch(clearQB());
         dispatch(clearSchema());
       }, 10000);
@@ -58,39 +58,46 @@ export default function DatabaseSelector({}) {
 
   return (
     <>
-      <SettingsForm
-        open={settingsMenuOpen}
-        database={selectedDatabase}
-        onClose={() => {
-          setSettingsMenuOpen(false);
-        }}
-      />
-      <NewDatabaseForm
-        open={addDatabaseFormOpen}
+      {settingsMenuOpen !== undefined && (
+        <SettingsForm
+          open={settingsMenuOpen}
+          saveState={settingsMenuOpen === 'update' ? selectedSaveState : null}
+          onClose={() => {
+            setSettingsMenuOpen(undefined);
+          }}
+        />
+      )}
+      {/* <NewDatabaseForm
+        open={addDbConnectionFormOpen}
         onClose={() => {
-          setAddDatabaseFormOpen(false);
+          setAddDbConnectionFormOpen(false);
         }}
-      />
+      /> */}
       <DropdownContainer ref={dbSelectionMenuRef} className="w-[20rem]">
         <DropdownButton
+          disabled={connecting || authCache.authorized === false || !!authCache.roomID}
           title={
             <div className="flex items-center">
-              {connecting ? (
+              {connecting && session.currentSaveState ? (
                 <>
                   <LoadingSpinner />
-                  <p className="ml-2 truncate">Connecting to {session.currentDatabase}</p>
+                  <p className="ml-2 truncate">Connecting to {session.saveStates[session.currentSaveState].name}</p>
                 </>
-              ) : session.currentDatabase ? (
+              ) : session.currentSaveState ? (
                 <>
-                  <div className="h-2 w-2 rounded-full bg-success-500" />
-                  <p className="ml-2 truncate">Connected DB: {session.currentDatabase}</p>
+                  <div
+                    className={`h-2 w-2 rounded-full ${
+                      session.saveStates[session.currentSaveState].db.status === DatabaseStatus.tested ? 'bg-success-500' : 'bg-danger-500'
+                    }`}
+                  />
+                  <p className="ml-2 truncate">Connected DB: {session.saveStates[session.currentSaveState].name}</p>
                 </>
-              ) : session.databases === undefined ? (
+              ) : session.saveStates === undefined ? (
                 <>
                   <LoadingSpinner />
                   <p className="ml-2">Retrieving databases</p>
                 </>
-              ) : session.databases?.length === 0 ? (
+              ) : Object.keys(session.saveStates).length === 0 ? (
                 <>
                   <p className="ml-2">Add your first Database</p>
                 </>
@@ -103,12 +110,12 @@ export default function DatabaseSelector({}) {
             </div>
           }
           onClick={() => {
-            if (session.databases?.length === 0) setAddDatabaseFormOpen(true);
+            if (session.saveStates && Object.keys(session.saveStates).length === 0) setSettingsMenuOpen('create');
             else setDbSelectionMenuOpen(!dbSelectionMenuOpen);
           }}
         />
 
-        {dbSelectionMenuOpen && session.databases !== undefined && (
+        {dbSelectionMenuOpen && session.saveStates !== undefined && (
           <DropdownItemContainer align="top-10 w-full">
             <li
               className="flex items-center p-2 hover:bg-secondary-50 cursor-pointer"
@@ -116,11 +123,11 @@ export default function DatabaseSelector({}) {
                 e.preventDefault();
                 setDbSelectionMenuOpen(false);
                 setConnecting(false);
-                setAddDatabaseFormOpen(true);
+                setSettingsMenuOpen('create');
               }}
               title="Add new database"
             >
-              {session.databases.length === 0 ? (
+              {session.saveStates && Object.keys(session.saveStates).length === 0 ? (
                 <>
                   <Add />
                   <p className="ml-2">Add your first database</p>
@@ -132,43 +139,47 @@ export default function DatabaseSelector({}) {
                 </>
               )}
             </li>
-            {session.databases.map((db) => (
+            {Object.values(session.saveStates).map((save) => (
               <li
-                key={db.Name}
+                key={save.id}
                 className="flex justify-between items-center px-4 py-2 hover:bg-primary-100 gap-2 cursor-pointer"
                 onClick={(e) => {
-                  if (db.Name !== session.currentDatabase) {
+                  if (save.id !== session.currentSaveState) {
                     e.preventDefault();
                     setDbSelectionMenuOpen(false);
                     setConnecting(true);
-                    dispatch(updateCurrentDatabase(db.Name));
+                    dispatch(updateCurrentSaveState(save.id));
                     dispatch(clearQB());
                     dispatch(clearSchema());
                   } else {
                     setDbSelectionMenuOpen(false);
                   }
                 }}
-                onMouseEnter={() => setHovered(db.Name)}
+                onMouseEnter={() => setHovered(save.id)}
                 onMouseLeave={() => setHovered(null)}
-                title={`Connect to ${db.Name}`}
+                title={`Connect to ${save.name}`}
               >
-                <div className={`h-[8px] w-[8px] rounded-full shrink-0 ${db.status ? 'bg-success-500' : 'bg-danger-500'}`} />
+                <div
+                  className={`h-[8px] w-[8px] rounded-full shrink-0 ${
+                    save.db.status === DatabaseStatus.tested ? 'bg-success-500' : 'bg-danger-500'
+                  }`}
+                />
                 <div className="w-full shrink min-w-0 flex flex-col">
-                  <p className="truncate w-full shrink-0 min-w-0">{db.Name}</p>
+                  <p className="truncate w-full shrink-0 min-w-0">{save.name}</p>
                   <p className="bg-light text-2xs text-secondary-500 truncate w-fit shrink-0 min-w-0 max-w-full h-full border border-secondary-200 rounded-sm p-0.5">
-                    {db.Protocol}
-                    {db.URL}
+                    {save.db.protocol}
+                    {save.db.url}
                   </p>
                 </div>
-                {hovered === db.Name && (
+                {hovered === save.id && (
                   <div className="flex items-center ml-2">
                     <div
                       className="text-secondary-700 hover:text-secondary-400 transition-colors duration-300"
                       onClick={(e) => {
                         e.preventDefault();
                         e.stopPropagation();
-                        setSettingsMenuOpen(true);
-                        setSelectedDatabase(db);
+                        setSettingsMenuOpen('update');
+                        setSelectedSaveState(save);
                       }}
                     >
                       <Settings />
@@ -177,12 +188,12 @@ export default function DatabaseSelector({}) {
                       className="text-secondary-700 hover:text-secondary-400 transition-colors duration-300"
                       onClick={(e) => {
                         e.preventDefault();
-                        dispatch(updateCurrentDatabase(undefined));
+                        dispatch(updateCurrentSaveState(undefined));
                         dispatch(clearQB());
                         dispatch(clearSchema());
-                        api.DeleteDatabase(db.Name);
+                        wsDeleteState(save.id);
                       }}
-                      title="Delete database"
+                      title="Delete database connection"
                     >
                       <Delete />
                     </div>
diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/AddDatabase/mockDatabases.ts b/apps/web/src/components/navbar/DatabaseManagement/forms/AddDatabase/mockDatabases.ts
deleted file mode 100644
index b713e5e61..000000000
--- a/apps/web/src/components/navbar/DatabaseManagement/forms/AddDatabase/mockDatabases.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-import { DatabaseType } from '@graphpolaris/shared/lib/data-access';
-
-export const mockDatabases = [
-  {
-    name: 'Recommendations',
-    subtitle: 'Hosted by Neo4j',
-    username: 'recommendations',
-    password: 'recommendations',
-    url: 'demo.neo4jlabs.com',
-    port: 7687,
-    protocol: 'neo4j+s://',
-    internal_database_name: 'recommendations',
-    type: DatabaseType.Neo4j,
-  },
-  {
-    name: 'Movies',
-    subtitle: 'Hosted by Neo4j',
-    username: 'movies',
-    password: 'movies',
-    url: 'demo.neo4jlabs.com',
-    port: 7687,
-    protocol: 'neo4j+s://',
-    internal_database_name: 'movies',
-    type: DatabaseType.Neo4j,
-  },
-  {
-    name: 'Northwind',
-    subtitle: 'Hosted by Neo4j',
-    username: 'northwind',
-    password: 'northwind',
-    url: 'demo.neo4jlabs.com',
-    port: 7687,
-    protocol: 'neo4j+s://',
-    internal_database_name: 'northwind',
-    type: DatabaseType.Neo4j,
-  },
-  {
-    name: 'Fincen',
-    subtitle: 'Hosted by Neo4j',
-    username: 'fincen',
-    password: 'fincen',
-    url: 'demo.neo4jlabs.com',
-    port: 7687,
-    protocol: 'neo4j+s://',
-    internal_database_name: 'fincen',
-    type: DatabaseType.Neo4j,
-  },
-  {
-    name: 'Slack',
-    subtitle: 'Hosted by Neo4j',
-    username: 'slack',
-    password: 'slack',
-    url: 'demo.neo4jlabs.com',
-    port: 7687,
-    protocol: 'neo4j+s://',
-    internal_database_name: 'slack',
-    type: DatabaseType.Neo4j,
-  },
-  {
-    name: 'Game of Thrones',
-    subtitle: 'Hosted by Neo4j',
-    username: 'gameofthrones',
-    password: 'gameofthrones',
-    url: 'demo.neo4jlabs.com',
-    port: 7687,
-    protocol: 'neo4j+s://',
-    internal_database_name: 'gameofthrones',
-    type: DatabaseType.Neo4j,
-  },
-];
diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/AddDatabase/newdatabase.tsx b/apps/web/src/components/navbar/DatabaseManagement/forms/AddDatabase/newdatabase.tsx
deleted file mode 100644
index d07b1c671..000000000
--- a/apps/web/src/components/navbar/DatabaseManagement/forms/AddDatabase/newdatabase.tsx
+++ /dev/null
@@ -1,349 +0,0 @@
-import React, { useEffect, useRef, useState } from 'react';
-import {
-  AddDatabaseRequest,
-  DatabaseType,
-  databaseNameMapping,
-  databaseProtocolMapping,
-  useAppDispatch,
-  useDatabaseAPI,
-  useSchemaAPI,
-  useSessionCache,
-} from '@graphpolaris/shared/lib/data-access';
-import { addError } from '@graphpolaris/shared/lib/data-access/store/configSlice';
-import { ErrorOutline } from '@mui/icons-material';
-import { mockDatabases } from './mockDatabases';
-import { Dialog } from '@graphpolaris/shared/lib/components/Dialog';
-import { Button } from '@graphpolaris/shared/lib/components/buttons';
-import Input from '@graphpolaris/shared/lib/components/inputs';
-
-const INITIAL_DB_STATE = {
-  username: 'neo4j',
-  password: 'DevOnlyPass',
-  url: 'localhost',
-  port: 7687,
-  name: 'neo4j',
-  protocol: 'neo4j://',
-  internal_database_name: 'neo4j',
-  type: DatabaseType.Neo4j,
-};
-
-interface Connection {
-  connecting: boolean;
-  status: null | string;
-  verified: boolean | null;
-}
-
-export const NewDatabaseForm = (props: { onClose(): void; open: boolean }) => {
-  const dispatch = useAppDispatch();
-  const ref = useRef<HTMLDialogElement>(null);
-  const [state, setState] = useState<AddDatabaseRequest>(INITIAL_DB_STATE);
-  const api = useDatabaseAPI();
-  const schemaApi = useSchemaAPI();
-  const session = useSessionCache();
-  const [hasError, setHasError] = useState({});
-  const [sampleData, setSampleData] = useState<boolean | null>(false);
-  const [connection, setConnection] = useState<Connection>({
-    connecting: false,
-    status: null,
-    verified: null,
-  });
-
-  useEffect(() => {
-    if (!state) return;
-    if (state.type === DatabaseType.ArangoDB && state.port === 7687) {
-      setState({ ...state, port: 8529 });
-    } else if (state.type === DatabaseType.Neo4j && state.port === 8529) {
-      setState({ ...state, port: 7687 });
-    }
-  }, [state.type]);
-
-  function handleInputChange(field: string, value: unknown) {
-    setState({
-      ...state,
-      [field]: value,
-    });
-  }
-
-  async function testDatabaseConnection() {
-    setConnection(() => ({
-      connecting: true,
-      status: 'Verifying database connection',
-      verified: null,
-    }));
-
-    if (session.databases && !session.databases.hasOwnProperty(state.name)) {
-      setConnection((prevState) => ({
-        ...prevState,
-        connecting: false,
-        status: 'Database already connected',
-        verified: false,
-      }));
-      return;
-    }
-
-    try {
-      api
-        .TestDatabaseConnection(state)
-        .then((res: any) => {
-          setConnection((prevState) => ({
-            ...prevState,
-            status: res.message,
-            verified: res.verified,
-          }));
-        })
-        .catch((error) => {
-          setConnection((prevState) => ({
-            ...prevState,
-            connecting: false,
-            status: 'Database connection failed',
-            verified: false,
-          }));
-        });
-    } catch (e) {
-      setConnection((prevState) => ({
-        ...prevState,
-        status: 'Database connection failed',
-        verified: false,
-      }));
-    }
-  }
-
-  function loadMockDatabase({ username, password, url, port, name, protocol, internal_database_name, type }: AddDatabaseRequest) {
-    setState((prevState) => ({
-      ...prevState,
-      username,
-      password,
-      url,
-      port,
-      name,
-      protocol,
-      internal_database_name,
-      type,
-    }));
-    setSampleData(false);
-  }
-
-  function handlePortChanged(port: string): void {
-    if (!isNaN(Number(port))) setState({ ...state, port: Number(port) });
-  }
-
-  useEffect(() => {
-    if (connection.verified === true) {
-      try {
-        api
-          .AddDatabase(state, { updateDatabaseCache: true, setAsCurrent: true })
-          .then(() => {
-            schemaApi.RequestSchema(state.name);
-          })
-          .catch((e) => {
-            dispatch(addError(e.message));
-          });
-      } catch (e: any) {
-        dispatch(addError(e.message));
-      } finally {
-        closeDialog();
-      }
-    }
-  }, [connection.verified]);
-
-  function closeDialog(): void {
-    setConnection({
-      connecting: false,
-      status: null,
-      verified: null,
-    });
-    setState(INITIAL_DB_STATE);
-    setSampleData(false);
-    props.onClose();
-    ref.current?.close();
-  }
-
-  return (
-    <Dialog open={props.open} onClose={props.onClose} className="lg:min-w-[50rem] ">
-      <div className="flex justify-between align-center">
-        <h1 className="text-xl font-bold">New Database</h1>
-        <div>
-          {sampleData ? (
-            <Button variant="outline" label="Go back" onClick={() => setSampleData(false)} />
-          ) : (
-            <>
-              <h1 className="font-light text-xs">No data?</h1>
-              <p className="font-light text-sm cursor-pointer underline" onClick={() => setSampleData(true)}>
-                Try sample data
-              </p>
-            </>
-          )}
-        </div>
-      </div>
-
-      {sampleData ? (
-        <div className="grid grid-cols-2 lg:grid-cols-3 gap-2">
-          {mockDatabases.map((sample) => (
-            <div
-              key={sample.name}
-              className="card hover:bg-secondary-100 cursor-pointer mb-2 border w-[15rem]"
-              onClick={() => loadMockDatabase(sample)}
-            >
-              <div className="card-body">
-                <h2 className="card-title">{sample.name}</h2>
-                <p className="font-light text-secondary-400">{sample.subtitle}</p>
-              </div>
-            </div>
-          ))}
-        </div>
-      ) : (
-        <>
-          <Input
-            type="text"
-            label="Name of the database"
-            value={state.name}
-            placeholder="neo4j"
-            required
-            errorText="This field is required"
-            validate={(v) => {
-              setHasError({ ...hasError, name: v.length === 0 });
-              return v.length > 0;
-            }}
-            onChange={(value: string) => handleInputChange('name', value)}
-          />
-
-          <Input
-            type="text"
-            label="Internal database name"
-            value={state.internal_database_name}
-            placeholder="internal_database_name"
-            required
-            errorText="This field is required"
-            validate={(v) => {
-              setHasError({ ...hasError, internal_database_name: v.length === 0 });
-              return v.length > 0;
-            }}
-            onChange={(value: string) => handleInputChange('internal_database_name', value)}
-          />
-
-          <div className="flex w-full gap-2">
-            <Input
-              type="dropdown"
-              label="Database Type"
-              required
-              value={databaseNameMapping[state.type]}
-              options={databaseNameMapping}
-              onChange={(value: string | number) => {
-                setState({
-                  ...state,
-                  type: databaseNameMapping.indexOf(String(value)),
-                });
-              }}
-            />
-
-            <Input
-              type="dropdown"
-              label="Database Protocol"
-              required
-              value={state.protocol}
-              options={databaseProtocolMapping}
-              onChange={(value: string | number) => {
-                setState({
-                  ...state,
-                  protocol: String(value),
-                });
-              }}
-            />
-          </div>
-
-          <div className="flex w-full gap-2">
-            <Input
-              type="text"
-              label="Hostname/IP"
-              value={state.url}
-              placeholder="neo4j"
-              required
-              errorText="This field is required"
-              validate={(v) => {
-                setHasError({ ...hasError, url: v.length === 0 });
-                return v.length > 0;
-              }}
-              onChange={(value: string) => handleInputChange('url', value)}
-            />
-
-            <Input
-              type="text"
-              label="Port"
-              value={state.port.toString()}
-              placeholder="neo4j"
-              required
-              errorText="Must be between 1 and 9999"
-              validate={(v) => {
-                setHasError({ ...hasError, port: !(v <= 9999 && v > 0) });
-                return v <= 9999 && v > 0;
-              }}
-              onChange={(value: string) => handlePortChanged(value)}
-            />
-          </div>
-
-          <div className="flex w-full gap-2">
-            <Input
-              type="text"
-              label="Username"
-              value={state.username}
-              placeholder="username"
-              required
-              errorText="This field is required"
-              validate={(v) => {
-                setHasError({ ...hasError, username: v.length === 0 });
-                return v.length > 0;
-              }}
-              onChange={(value: string) => handleInputChange('username', value)}
-            />
-
-            <Input
-              type="text"
-              label="Password"
-              value={state.password}
-              placeholder="password"
-              required
-              visible={false}
-              errorText="This field is required"
-              validate={(v) => {
-                setHasError({ ...hasError, password: v.length === 0 });
-                return v.length > 0;
-              }}
-              onChange={(value: string) => handleInputChange('password', value)}
-            />
-          </div>
-
-          {!(connection.status === null) && (
-            <div className={`flex flex-col justify-center items-center`}>
-              <div className="flex justify-center items-center">
-                {connection.verified === false && <ErrorOutline className="text-secondary-400" />}
-                <p className="font-light text-sm	text-secondary-400	">{connection.status}</p>
-              </div>
-              {connection.verified === null && <progress className="progress w-56"></progress>}
-            </div>
-          )}
-          <div className="grid gap-2">
-            <Button
-              type="primary"
-              block
-              label={connection.connecting ? 'Connecting...' : 'Connect'}
-              onClick={(event) => {
-                event.preventDefault();
-                testDatabaseConnection();
-              }}
-              disabled={connection.connecting || Object.values(hasError).some((e) => e === true)}
-            />
-            <Button
-              variant="outline"
-              block
-              label="Cancel"
-              onClick={(event) => {
-                event.preventDefault();
-                closeDialog();
-              }}
-            />
-          </div>
-        </>
-      )}
-    </Dialog>
-  );
-};
diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx b/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx
new file mode 100644
index 000000000..f7ac983b8
--- /dev/null
+++ b/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx
@@ -0,0 +1,180 @@
+import React, { useEffect, useState } from 'react';
+import {
+  DatabaseInfo,
+  DatabaseType,
+  SaveStateI,
+  databaseNameMapping,
+  databaseProtocolMapping,
+  nilUUID,
+} from '@graphpolaris/shared/lib/data-access';
+import { sampleSaveStates } from './mockSaveStates';
+import Input from '@graphpolaris/shared/lib/components/inputs';
+import { useImmer } from 'use-immer';
+
+export const INITIAL_SAVE_STATE: SaveStateI = {
+  id: nilUUID,
+  name: 'Untitled',
+  db: {
+    username: 'neo4j',
+    password: 'DevOnlyPass',
+    url: 'localhost',
+    port: 7687,
+    protocol: 'neo4j://',
+    internalDatabaseName: 'neo4j',
+    type: DatabaseType.Neo4j,
+  },
+};
+
+export const DatabaseForm = (props: { data: SaveStateI; onChange: (data: SaveStateI, error: boolean) => void }) => {
+  const [formData, setFormData] = useImmer(props.data);
+  const [hasError, setHasError] = useState<Record<string, boolean>>({});
+
+  function handlePortChanged(port: string): void {
+    if (!isNaN(Number(port)))
+      setFormData((draft) => {
+        draft.db.port = Number(port);
+        return draft;
+      });
+  }
+
+  useEffect(() => {
+    props.onChange(formData, Object.values(hasError).includes(true));
+  }, [formData]);
+
+  return (
+    <>
+      <Input
+        type="text"
+        label="Name of database"
+        value={formData.name}
+        onChange={(value: string) =>
+          setFormData((draft) => {
+            draft.name = value;
+          })
+        }
+      />
+
+      <Input
+        type="text"
+        label="Internal database name"
+        value={formData.db.internalDatabaseName}
+        placeholder="internalDatabaseName"
+        required
+        errorText="This field is required"
+        validate={(v) => {
+          setHasError({ ...hasError, internalDatabaseName: v.length === 0 });
+          return v.length > 0;
+        }}
+        onChange={(value: string) =>
+          setFormData((draft) => {
+            draft.db.internalDatabaseName = value;
+          })
+        }
+      />
+
+      <div className="flex w-full gap-2">
+        <Input
+          type="dropdown"
+          label="Database Type"
+          required
+          value={databaseNameMapping[formData.db.type]}
+          options={databaseNameMapping}
+          onChange={(value: string | number) => {
+            setFormData((draft) => {
+              draft.db.type = databaseNameMapping.indexOf(value.toString());
+            });
+          }}
+        />
+
+        <Input
+          type="dropdown"
+          label="Database Protocol"
+          required
+          value={formData.db.protocol}
+          options={databaseProtocolMapping}
+          onChange={(value: string | number) => {
+            setFormData((draft) => {
+              draft.db.protocol = value.toString();
+            });
+          }}
+        />
+      </div>
+
+      <div className="flex w-full gap-2">
+        <Input
+          type="text"
+          label="Hostname/IP"
+          value={formData.db.url}
+          placeholder="neo4j"
+          required
+          errorText="This field is required"
+          validate={(v) => {
+            setHasError({ ...hasError, url: v.length === 0 });
+            return v.length > 0;
+          }}
+          onChange={(value: string) => {
+            setFormData((draft) => {
+              draft.db.url = value;
+            });
+          }}
+        />
+
+        <Input
+          type="text"
+          label="Port"
+          value={formData.db.port.toString()}
+          placeholder="neo4j"
+          required
+          errorText="Must be between 1 and 9999"
+          validate={(v) => {
+            setHasError({ ...hasError, port: !(v <= 9999 && v > 0) });
+            return v <= 9999 && v > 0;
+          }}
+          onChange={(value: string) => {
+            setFormData((draft) => {
+              draft.db.port = Number(value);
+            });
+          }}
+        />
+      </div>
+
+      <div className="flex w-full gap-2">
+        <Input
+          type="text"
+          label="Username"
+          value={formData.db.username}
+          placeholder="username"
+          required
+          errorText="This field is required"
+          validate={(v) => {
+            setHasError({ ...hasError, username: v.length === 0 });
+            return v.length > 0;
+          }}
+          onChange={(value: string) => {
+            setFormData((draft) => {
+              draft.db.username = value;
+            });
+          }}
+        />
+
+        <Input
+          type="text"
+          label="Password"
+          value={formData.db.password}
+          placeholder="password"
+          required
+          errorText="This field is required"
+          validate={(v) => {
+            setHasError({ ...hasError, password: v.length === 0 });
+            return v.length > 0;
+          }}
+          onChange={(value: string) => {
+            setFormData((draft) => {
+              draft.db.password = value;
+            });
+          }}
+        />
+      </div>
+    </>
+  );
+};
diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/mockSaveStates.tsx b/apps/web/src/components/navbar/DatabaseManagement/forms/mockSaveStates.tsx
new file mode 100644
index 000000000..ffe4c41be
--- /dev/null
+++ b/apps/web/src/components/navbar/DatabaseManagement/forms/mockSaveStates.tsx
@@ -0,0 +1,113 @@
+import React from 'react';
+import { DatabaseInfo } from '@graphpolaris/shared/lib/data-access';
+import { DatabaseType, SaveStateI, nilUUID } from '@graphpolaris/shared/lib/data-access/api/wsState';
+
+export type SaveStateSampleI = SaveStateI & {
+  subtitle: string;
+};
+
+export const sampleSaveStates: Array<SaveStateSampleI> = [
+  {
+    id: nilUUID,
+    name: 'Recommendations',
+    subtitle: 'Hosted by Neo4j',
+    db: {
+      username: 'recommendations',
+      password: 'recommendations',
+      url: 'demo.neo4jlabs.com',
+      port: 7687,
+      protocol: 'neo4j+s://',
+      internalDatabaseName: 'recommendations',
+      type: DatabaseType.Neo4j,
+    },
+  },
+  {
+    id: nilUUID,
+    name: 'Movies',
+    subtitle: 'Hosted by Neo4j',
+    db: {
+      username: 'movies',
+      password: 'movies',
+      url: 'demo.neo4jlabs.com',
+      port: 7687,
+      protocol: 'neo4j+s://',
+      internalDatabaseName: 'movies',
+      type: DatabaseType.Neo4j,
+    },
+  },
+  {
+    id: nilUUID,
+    name: 'Northwind',
+    subtitle: 'Hosted by Neo4j',
+    db: {
+      username: 'northwind',
+      password: 'northwind',
+      url: 'demo.neo4jlabs.com',
+      port: 7687,
+      protocol: 'neo4j+s://',
+      internalDatabaseName: 'northwind',
+      type: DatabaseType.Neo4j,
+    },
+  },
+  {
+    id: nilUUID,
+    name: 'Fincen',
+    subtitle: 'Hosted by Neo4j',
+    db: {
+      username: 'fincen',
+      password: 'fincen',
+      url: 'demo.neo4jlabs.com',
+      port: 7687,
+      protocol: 'neo4j+s://',
+      internalDatabaseName: 'fincen',
+      type: DatabaseType.Neo4j,
+    },
+  },
+  {
+    id: nilUUID,
+    name: 'Slack',
+    subtitle: 'Hosted by Neo4j',
+    db: {
+      username: 'slack',
+      password: 'slack',
+      url: 'demo.neo4jlabs.com',
+      port: 7687,
+      protocol: 'neo4j+s://',
+      internalDatabaseName: 'slack',
+      type: DatabaseType.Neo4j,
+    },
+  },
+  {
+    id: nilUUID,
+    name: 'Game of Thrones',
+    subtitle: 'Hosted by Neo4j',
+    db: {
+      username: 'gameofthrones',
+      password: 'gameofthrones',
+      url: 'demo.neo4jlabs.com',
+      port: 7687,
+      protocol: 'neo4j+s://',
+      internalDatabaseName: 'gameofthrones',
+      type: DatabaseType.Neo4j,
+    },
+  },
+];
+
+export const SampleDatabaseSelector = (props: { onClick: (data: SaveStateI) => void }) => {
+  return (
+    <div className="grid grid-cols-2 lg:grid-cols-3 gap-2">
+      {sampleSaveStates.map((sample) => (
+        <div
+          key={sample.name}
+          className="card hover:bg-secondary-100 cursor-pointer mb-2 border w-[15rem]"
+          onClick={() => props.onClick(sample as SaveStateI)}
+        >
+          <div className="card-body">
+            <h2 className="card-title">{sample.name}</h2>
+            <p className="font-light text-secondary-400">{sample.subtitle}</p>
+          </div>
+        </div>
+      ))}
+    </div>
+  );
+};
diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx b/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx
index 9c6fc99af..859a6fd90 100644
--- a/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx
+++ b/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx
@@ -1,125 +1,138 @@
-import React, { useEffect, useRef, useState } from 'react';
+import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
 import {
-  AddDatabaseRequest,
   databaseNameMapping,
   databaseProtocolMapping,
   useAppDispatch,
-  useDatabaseAPI,
-  DatabaseInfo,
+  SaveStateI,
+  wsUpdateState,
+  DatabaseStatus,
+  wsTestDatabaseConnection,
+  TestDatabaseConnectionResponse,
+  wsCreateState,
+  nilUUID,
+  DatabaseType,
 } from '@graphpolaris/shared/lib/data-access';
 import { ErrorOutline } from '@mui/icons-material';
 import { Dialog } from '@graphpolaris/shared/lib/components/Dialog';
 import { addError } from '@graphpolaris/shared/lib/data-access/store/configSlice';
 import { Button } from '@graphpolaris/shared/lib/components/buttons';
 import Input from '@graphpolaris/shared/lib/components/inputs';
+import { useImmer } from 'use-immer';
+import Broker from '@graphpolaris/shared/lib/data-access/socket/broker';
+import { addSaveState } from '@graphpolaris/shared/lib/data-access/store/sessionSlice';
+import { DatabaseForm } from './databaseForm';
+import { SampleDatabaseSelector } from './mockSaveStates';
+
+export const INITIAL_SAVE_STATE: SaveStateI = {
+  id: nilUUID,
+  name: 'Untitled',
+  db: {
+    username: 'neo4j',
+    password: 'DevOnlyPass',
+    url: 'localhost',
+    port: 7687,
+    protocol: 'neo4j://',
+    internalDatabaseName: 'neo4j',
+    type: DatabaseType.Neo4j,
+  },
+};
 
-interface Connection {
+type Connection = {
   updating: boolean;
   status: null | string;
   verified: boolean | null;
-}
-
-const DEFAULT_DB: AddDatabaseRequest = {
-  name: '',
-  internal_database_name: '',
-  url: '',
-  port: 7687,
-  protocol: '',
-  username: '',
-  password: '',
-  type: 0,
 };
 
-export const SettingsForm = (props: { onClose(): void; open: boolean; database: DatabaseInfo | null }) => {
+export const SettingsForm = (props: { onClose(): void; open: 'create' | 'update'; saveState: SaveStateI | null }) => {
   const dispatch = useAppDispatch();
   const ref = useRef<HTMLDialogElement>(null);
-  const [state, setState] = useState<AddDatabaseRequest>(DEFAULT_DB);
-  const api = useDatabaseAPI();
-  const [hasError, setHasError] = useState({});
+  const [formData, setFormData] = useImmer(props.saveState && props.open === 'update' ? props.saveState : INITIAL_SAVE_STATE);
+  const [hasError, setHasError] = useState(false);
+  const [sampleDataPanel, setSampleDataPanel] = useState<boolean | null>(false);
   const [connection, setConnection] = useState<Connection>({
     updating: false,
     status: null,
     verified: null,
   });
+  const formDataRef = useRef<SaveStateI | null>(null);
+  const formTitle = props.open === 'create' ? 'Create' : 'Update';
+
+  const refImperativeHandles = useRef<any>(null);
+  useImperativeHandle(refImperativeHandles, () => ({
+    processDbTested: (data: TestDatabaseConnectionResponse) => {
+      if (!formDataRef.current) {
+        console.error('formDataRef.current is null');
+        return;
+      }
+      if (data.status === 'success') {
+        setConnection((prevState) => ({
+          updating: false,
+          status: 'Database connection verified',
+          verified: true,
+        }));
+        if (props.open === 'create') {
+          wsCreateState(formDataRef.current);
+        } else {
+          wsUpdateState(formDataRef.current);
+        }
+      } else {
+        setConnection((prevState) => ({
+          updating: false,
+          status: 'Database connection failed',
+          verified: false,
+        }));
+      }
+    },
+    processStateUpdated: (data: SaveStateI) => {
+      let _data = JSON.parse(JSON.stringify(data));
+      _data.db.status = DatabaseStatus.tested;
+      dispatch(addSaveState(_data));
+      formDataRef.current = null;
+      closeDialog();
+    },
+  }));
 
   useEffect(() => {
-    const db = props.database;
-    if (db) {
-      setState({
-        name: db.Name,
-        internal_database_name: db.InternalDatabaseName,
-        url: db.URL,
-        port: db.Port,
-        protocol: db.Protocol,
-        username: db.Username,
-        password: db.Password,
-        type: db.Type,
-      });
-    }
-  }, [props.database]);
+    Broker.instance().subscribe(refImperativeHandles.current.processDbTested, 'tested_connection');
+    Broker.instance().subscribe(refImperativeHandles.current.processStateUpdated, 'updated_save_state');
+    Broker.instance().subscribe(refImperativeHandles.current.processStateUpdated, 'save_state');
+
+    return () => {
+      Broker.instance().unSubscribeAll('tested_connection');
+      Broker.instance().unSubscribeAll('updated_save_state');
+      Broker.instance().unSubscribeAll('save_state');
+    };
+  }, []);
 
   useEffect(() => {
-    if (connection.verified) {
-      api.GetAllDatabases().catch((e) => console.debug(e));
-      closeDialog();
+    if (props.saveState && props.open === 'update') {
+      setFormData(props.saveState);
+      setSampleDataPanel(null);
+    } else {
+      setSampleDataPanel(false);
     }
-  }, [connection.verified]);
+  }, [props.saveState]);
 
-  function handleInputChange(field: keyof AddDatabaseRequest, value: string) {
-    if (field != 'port' && field != 'type') {
-      setState((prevState) => ({ ...prevState, [field]: value }));
-    }
-  }
+  useEffect(() => {
+    formDataRef.current = formData;
+  }, [formData]);
 
   async function handleSubmit() {
     setConnection(() => ({
       updating: true,
-      status: 'Updating database credentials',
+      status: formTitle.slice(0, -1) + 'ing database credentials',
       verified: null,
     }));
 
-    if (props.database != null) {
-      try {
-        api
-          .UpdateDatabase({
-            name: props.database.Name,
-            internal_database_name: state.internal_database_name,
-            password: state.password,
-            port: state.port,
-            protocol: state.protocol,
-            type: state.type,
-            url: state.url,
-            username: state.username,
-          })
-          .then(() => {
-            setConnection(() => ({
-              updating: false,
-              status: 'Worked',
-              verified: true,
-            }));
-          })
-          .catch((e) => {
-            dispatch(addError('Database updating went wrong'));
-            setConnection((prevState) => ({
-              ...prevState,
-              updating: false,
-              status: 'Database updating went wrong',
-              verified: false,
-            }));
-          });
-      } catch (e) {
-        dispatch(addError('An error occured'));
-        setConnection((prevState) => ({
-          updating: false,
-          status: 'An error occured',
-          verified: false,
-        }));
-      }
-    }
+    wsTestDatabaseConnection(formData.db);
   }
 
   function handlePortChanged(port: string): void {
-    if (!isNaN(Number(port))) setState({ ...state, port: Number(port) });
+    if (!isNaN(Number(port)))
+      setFormData((draft) => {
+        draft.db.port = Number(port);
+        return draft;
+      });
   }
 
   function closeDialog(): void {
@@ -128,124 +141,51 @@ export const SettingsForm = (props: { onClose(): void; open: boolean; database:
       status: null,
       verified: null,
     });
-    setState(DEFAULT_DB);
-    props.onClose();
+    setFormData(INITIAL_SAVE_STATE);
     ref.current?.close();
+    props.onClose();
   }
 
   return (
-    <Dialog open={props.open} onClose={props.onClose}>
+    <Dialog open={!!props.open} onClose={props.onClose} className="lg:min-w-[50rem]">
       <div className="flex justify-between align-center">
-        <h1 className="card-title">Update {state.name} Database</h1>
+        <h1 className="text-xl font-bold">
+          {formTitle} {formData.name} Database
+        </h1>
+        <div>
+          {sampleDataPanel === true ? (
+            <Button variant="outline" label="Go back" onClick={() => setSampleDataPanel(false)} />
+          ) : sampleDataPanel === false ? (
+            <>
+              <h1 className="font-light text-xs">No data?</h1>
+              <p className="font-light text-sm cursor-pointer underline" onClick={() => setSampleDataPanel(true)}>
+                Try sample data
+              </p>
+            </>
+          ) : (
+            ''
+          )}
+        </div>
       </div>
 
       <>
-        <Input type="text" label="Name of database" value={state.name} onChange={() => {}} disabled />
-
-        <Input
-          type="text"
-          label="Internal database name"
-          value={state.internal_database_name}
-          placeholder="internal_database_name"
-          required
-          errorText="This field is required"
-          validate={(v) => {
-            setHasError({ ...hasError, internal_database_name: v.length === 0 });
-            return v.length > 0;
-          }}
-          onChange={(value: string) => handleInputChange('internal_database_name', value)}
-        />
-
-        <div className="flex w-full gap-2">
-          <Input
-            type="dropdown"
-            label="Database Type"
-            required
-            value={databaseNameMapping[state.type]}
-            options={databaseNameMapping}
-            onChange={(value: string | number) => {
-              setState({
-                ...state,
-                type: databaseNameMapping.indexOf(String(value)),
-              });
-            }}
-          />
-
-          <Input
-            type="dropdown"
-            label="Database Protocol"
-            required
-            value={state.protocol}
-            options={databaseProtocolMapping}
-            onChange={(value: string | number) => {
-              setState({
-                ...state,
-                protocol: String(value),
-              });
-            }}
-          />
-        </div>
-
-        <div className="flex w-full gap-2">
-          <Input
-            type="text"
-            label="Hostname/IP"
-            value={state.url}
-            placeholder="neo4j"
-            required
-            errorText="This field is required"
-            validate={(v) => {
-              setHasError({ ...hasError, url: v.length === 0 });
-              return v.length > 0;
-            }}
-            onChange={(value: string) => handleInputChange('url', value)}
-          />
-
-          <Input
-            type="text"
-            label="Port"
-            value={state.port.toString()}
-            placeholder="neo4j"
-            required
-            errorText="Must be between 1 and 9999"
-            validate={(v) => {
-              setHasError({ ...hasError, port: !(v <= 9999 && v > 0) });
-              return v <= 9999 && v > 0;
-            }}
-            onChange={(value: string) => handlePortChanged(value)}
-          />
-        </div>
-
-        <div className="flex w-full gap-2">
-          <Input
-            type="text"
-            label="Username"
-            value={state.username}
-            placeholder="username"
-            required
-            errorText="This field is required"
-            validate={(v) => {
-              setHasError({ ...hasError, username: v.length === 0 });
-              return v.length > 0;
+        {sampleDataPanel === true ? (
+          <SampleDatabaseSelector
+            onClick={(data) => {
+              setFormData(data);
+              setHasError(false);
+              setSampleDataPanel(false);
             }}
-            onChange={(value: string) => handleInputChange('username', value)}
           />
-
-          <Input
-            type="text"
-            label="Password"
-            value={state.password}
-            visible={false}
-            placeholder="password"
-            required
-            errorText="This field is required"
-            validate={(v) => {
-              setHasError({ ...hasError, password: v.length === 0 });
-              return v.length > 0;
+        ) : (
+          <DatabaseForm
+            data={formData}
+            onChange={(data: SaveStateI, error: boolean) => {
+              setFormData({ ...data, id: formData.id });
+              setHasError(error);
             }}
-            onChange={(value: string) => handleInputChange('password', value)}
           />
-        </div>
+        )}
 
         {!(connection.status === null) && (
           <div className={`flex flex-col justify-center items-center`}>
@@ -260,12 +200,12 @@ export const SettingsForm = (props: { onClose(): void; open: boolean; database:
         <div className="grid md:grid-cols-2 gap-3 card-actions w-full justify-stretch items-center">
           <Button
             type="primary"
-            label={connection.updating ? 'Updating...' : 'Update'}
+            label={connection.updating ? formTitle.slice(0, -1) + 'ing...' : formTitle}
             onClick={(event) => {
               event.preventDefault();
               handleSubmit();
             }}
-            disabled={connection.updating || Object.values(hasError).some((e) => e === true)}
+            disabled={connection.updating || hasError}
           />
           <Button
             variant="outline"
diff --git a/apps/web/src/components/navbar/databasemenu.tsx b/apps/web/src/components/navbar/databasemenu.tsx
index 9f53a853a..15a2e2f18 100644
--- a/apps/web/src/components/navbar/databasemenu.tsx
+++ b/apps/web/src/components/navbar/databasemenu.tsx
@@ -1,15 +1,15 @@
 import React from 'react';
-import { useSessionCache } from '@graphpolaris/shared/lib/data-access';
+import { SaveStateI, useSessionCache } from '@graphpolaris/shared/lib/data-access';
 
 export const DatabaseMenu = (props: { onClick: (database: string) => void }) => {
   const session = useSessionCache();
 
   return (
     <ul className="menu dropdown-content absolute right-48 z-[1] p-2 shadow-xl bg-secondary-50 rounded-box w-52" tabIndex={0}>
-      {session.databases &&
-        session.databases.map((db: any) => (
-          <li key={db.Name}>
-            <button onClick={() => props.onClick(db.Name)}>{db.Name}</button>
+      {session.saveStates &&
+        Object.values(session.saveStates).map((ss: SaveStateI) => (
+          <li key={ss.name}>
+            <button onClick={() => props.onClick(ss.name)}>{ss.name}</button>
           </li>
         ))}
     </ul>
diff --git a/apps/web/src/components/navbar/navbar.tsx b/apps/web/src/components/navbar/navbar.tsx
index e635ca6f0..47f714926 100644
--- a/apps/web/src/components/navbar/navbar.tsx
+++ b/apps/web/src/components/navbar/navbar.tsx
@@ -11,16 +11,17 @@
 import React, { useState, useRef, useEffect } from 'react';
 import logo_white from './gp-logo-white.svg';
 import logo from './gp-logo.svg';
-import { useAuthorizationCache } from '@graphpolaris/shared/lib/data-access';
+import { useAuthorizationCache, useAuth } from '@graphpolaris/shared/lib/data-access';
 import { SearchBar } from './search/SearchBar';
-import DatabaseSelector from './DatabaseManagement/DatabaseSelector';
+import DatabaseSelector from './DatabaseManagement/dbConnectionSelector';
 import { DropdownItem, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns';
 import ColorMode from '@graphpolaris/shared/lib/components/color-mode';
 import GpLogo from './gp-logo';
 
 export const Navbar = () => {
   const dropdownRef = useRef<HTMLDivElement>(null);
-  const auth = useAuthorizationCache();
+  const auth = useAuth();
+  const authCache = useAuthorizationCache();
   const [menuOpen, setMenuOpen] = useState(false);
 
   const currentLogo = !'dark' ? logo_white : logo; // TODO: support dark mode
@@ -55,32 +56,35 @@ export const Navbar = () => {
               className="relative inline-flex items-center justify-center w-8 h-8 overflow-hidden bg-secondary-500 rounded hover:bg-secondary-600 transition-colors duration-150 ease-in-out cursor-pointer"
               onClick={() => setMenuOpen(!menuOpen)}
             >
-              <span className="font-medium text-light">{auth.username?.slice(0, 2).toUpperCase()}</span>
+              <span className="font-medium text-light">{authCache.username?.slice(0, 2).toUpperCase()}</span>
             </div>
 
             {menuOpen && (
               <DropdownItemContainer className="w-56" align="right-7">
                 <div className="menu-title border-b">
-                  <h2>user: {auth.username}</h2>
-                  <h3 className="text-xs break-words">session: {auth.sessionID}</h3>
+                  <h2>user: {authCache.username}</h2>
+                  <h3 className="text-xs break-words">session: {authCache.sessionID}</h3>
                 </div>
 
-                {auth.authorized ? (
+                {authCache.authorized ? (
                   <>
                     <DropdownItem
                       value="Share"
-                      submenu={
-                        <>
-                          <DropdownItem value="Visual" onClick={() => {}} />
-                          <DropdownItem value="Knowledge base" onClick={() => {}} />
-                        </>
-                      }
+                      onClick={() => {
+                        auth.newShareRoom();
+                      }}
+                      // submenu={
+                      //   <>
+                      //     <DropdownItem value="Visual" onClick={() => {}} />
+                      //     <DropdownItem value="Knowledge base" onClick={() => {}} />
+                      //   </>
+                      // }
                     />
                     <DropdownItem
                       value="Advanced"
                       submenu={
                         <>
-                          <DropdownItem value="" onClick={() => {}} />
+                          <DropdownItem value="TBD" onClick={() => {}} />
                         </>
                       }
                     />
@@ -93,6 +97,11 @@ export const Navbar = () => {
                   </>
                 )}
 
+                {authCache?.roomID && (
+                  <div className="menu-title border-b">
+                    <h3 className="text-xs break-words">Share ID: {authCache.roomID}</h3>
+                  </div>
+                )}
                 <div className="menu-title border-t">
                   <h3 className="text-xs">Version: {buildInfo}</h3>
                 </div>
diff --git a/libs/shared/lib/components/dropdowns/index.tsx b/libs/shared/lib/components/dropdowns/index.tsx
index 4ce20ad79..96ec000e5 100644
--- a/libs/shared/lib/components/dropdowns/index.tsx
+++ b/libs/shared/lib/components/dropdowns/index.tsx
@@ -20,14 +20,16 @@ type DropdownButtonProps = {
   title: string | ReactNode;
   onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
   size?: 'xs' | 'sm' | 'md' | 'xl';
+  disabled?: boolean;
 };
 
-export function DropdownButton({ title, onClick, size }: DropdownButtonProps) {
+export function DropdownButton({ title, onClick, size, disabled }: DropdownButtonProps) {
   return (
     <>
       <button
-        className="inline-flex w-full justify-between items-center gap-x-1.5 rounded bg-light px-3 py-2 text-secondary-900 shadow-sm ring-1 ring-inset ring-secondary-300 hover:bg-secondary-50"
+        className="inline-flex w-full justify-between items-center gap-x-1.5 rounded bg-light px-3 py-2 text-secondary-900 shadow-sm ring-1 ring-inset ring-secondary-300 hover:bg-secondary-50 disabled:bg-secondary-100 disabled:cursor-not-allowed disabled:text-secondary-400"
         onClick={onClick}
+        disabled={disabled}
       >
         <span className={`text-${size}`}>{title}</span>
         <svg className="-mr-1 h-5 w-5 text-secondary-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
diff --git a/libs/shared/lib/data-access/api/database.ts b/libs/shared/lib/data-access/api/database.ts
deleted file mode 100644
index a8bd51911..000000000
--- a/libs/shared/lib/data-access/api/database.ts
+++ /dev/null
@@ -1,168 +0,0 @@
-// All database related API calls
-
-import { useAuth } from '../authorization';
-import { useAppDispatch, useSessionCache } from '../store';
-import { clearQB } from '../store/querybuilderSlice';
-import { clearSchema } from '../store/schemaSlice';
-import { updateCurrentDatabase, updateDatabaseList } from '../store/sessionSlice';
-
-export enum DatabaseType {
-  ArangoDB = 0,
-  Neo4j = 1,
-}
-
-export const databaseNameMapping: string[] = ['arangodb', 'neo4j'];
-export const databaseProtocolMapping: string[] = ['neo4j://', 'neo4j+s://', 'bolt://', 'bolt+s://'];
-
-export type AddDatabaseRequest = {
-  name: string;
-  internal_database_name: string;
-  url: string;
-  port: number;
-  protocol: string;
-  username: string;
-  password: string;
-  type: DatabaseType; // Database type. 0 = ArangoDB, 1 = Neo4j
-};
-
-export type DatabaseInfo = {
-  Name: string;
-  InternalDatabaseName: string;
-  URL: string;
-  Port: number;
-  Protocol: string;
-  Username: string;
-  Password: string;
-  Type: number;
-  status: boolean;
-};
-
-export type AddDatabaseOptions = {
-  setAsCurrent?: boolean;
-  updateDatabaseCache?: boolean;
-};
-
-export type GetDatabasesOptions = {
-  updateSessionCache?: boolean;
-};
-
-export type DeleteDatabasesOptions = {
-  updateSessionCache?: boolean;
-};
-
-export type VerifyConnectionRequest = string;
-
-export const useDatabaseAPI = () => {
-  const cache = useSessionCache();
-  const dispatch = useAppDispatch();
-  const domain = import.meta.env.BACKEND_URL;
-  const useruri = import.meta.env.BACKEND_USER;
-  const { fetchAuthenticated } = useAuth();
-
-  function AddDatabase(request: AddDatabaseRequest, options: AddDatabaseOptions = {}): Promise<void> {
-    const { setAsCurrent = true, updateDatabaseCache = false } = options;
-    return new Promise((resolve, reject) => {
-      fetchAuthenticated(`${domain}${useruri}/database`, {
-        method: 'POST',
-        body: JSON.stringify(request),
-      })
-        .then((response: Response) => {
-          console.info('Added Database', response);
-          if (!response.ok) {
-            reject(response.statusText);
-          }
-          if (setAsCurrent){ 
-            dispatch(updateCurrentDatabase(request.name));
-            dispatch(clearQB());
-            dispatch(clearSchema());
-          }
-          if (updateDatabaseCache) GetAllDatabases({ updateSessionCache: true }).catch(reject);
-
-          resolve();
-        })
-        .catch(reject);
-    });
-  }
-
-  function GetAllDatabases(options: GetDatabasesOptions = {}): Promise<Array<string>> {
-    const { updateSessionCache: updateDatabaseCache = true } = options;
-    return new Promise((resolve, reject) => {
-      fetchAuthenticated(`${domain}${useruri}/database`)
-        .then((response: Response) => {
-          if (!response.ok) {
-            new Promise((resolve, reject) => {
-              reject(response.statusText);
-            });
-          }
-          response
-            .json()
-            .then((json) => {
-              if (updateDatabaseCache) dispatch(updateDatabaseList(json.databases));
-              resolve(json.databases);
-            })
-            .catch(reject);
-        })
-        .catch(reject);
-    });
-  }
-
-  function DeleteDatabase(name: string, options: DeleteDatabasesOptions = {}): Promise<void> {
-    const { updateSessionCache: updateDatabaseCache = true } = options;
-    return new Promise((resolve, reject) => {
-      fetchAuthenticated(`${domain}${useruri}/database/` + name, {
-        method: 'DELETE',
-      })
-        .then((response: Response) => {
-          if (!response.ok) {
-            reject(response.statusText);
-          }
-
-          if (updateDatabaseCache) GetAllDatabases({ updateSessionCache: true }).catch(reject);
-
-          resolve();
-        })
-        .catch(reject);
-    });
-  }
-
-  function TestDatabaseConnection(request: AddDatabaseRequest): Promise<void> {
-    return new Promise((resolve, reject) => {
-      fetchAuthenticated(`${domain}${useruri}/database/test-connection`, {
-        method: 'POST',
-        body: JSON.stringify(request),
-      })
-        .then((response: Response) => {
-          if (!response.ok) {
-            reject(response.statusText);
-          }
-          resolve(response.json());
-        })
-        .catch(reject);
-    });
-  }
-
-  function UpdateDatabase(request: AddDatabaseRequest): Promise<void> {
-    return new Promise((resolve, reject) => {
-      fetchAuthenticated(`${domain}${useruri}/database/update`, {
-        method: 'PATCH',
-        body: JSON.stringify(request),
-      })
-        .then((response: Response) => {
-          if (!response.ok) {
-            reject(response.statusText);
-          }
-          resolve();
-        })
-        .catch(reject);
-    });
-  }
-
-  return {
-    DatabaseType,
-    AddDatabase,
-    GetAllDatabases,
-    DeleteDatabase,
-    TestDatabaseConnection,
-    UpdateDatabase,
-  };
-};
diff --git a/libs/shared/lib/data-access/api/eventBus.tsx b/libs/shared/lib/data-access/api/eventBus.tsx
new file mode 100644
index 000000000..1f41476ff
--- /dev/null
+++ b/libs/shared/lib/data-access/api/eventBus.tsx
@@ -0,0 +1,126 @@
+import {
+  useAuth,
+  useAuthorizationCache,
+  useAppDispatch,
+  useSessionCache,
+  useQuerybuilderGraph,
+  useQuerybuilderHash,
+  useML,
+  useMLEnabledHash,
+  useConfig,
+  useQuerybuilderSettings,
+  readInSchemaFromBackend,
+  assignNewGraphQueryResult,
+  setQuerybuilderNodes,
+  resetGraphQueryResults,
+  useQuerybuilder,
+} from '@graphpolaris/shared/lib/data-access';
+import { WebSocketHandler } from '@graphpolaris/shared/lib/data-access/socket';
+import Broker from '@graphpolaris/shared/lib/data-access/socket/broker';
+import { addError, addInfo } from '@graphpolaris/shared/lib/data-access/store/configSlice';
+import { GraphQueryResultFromBackendPayload, queryingBackend } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
+import { allMLTypes, LinkPredictionInstance, setMLResult } from '@graphpolaris/shared/lib/data-access/store/mlSlice';
+import { QueryBuilderState } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
+import { QueryMultiGraph, Query2BackendQuery } from '@graphpolaris/shared/lib/querybuilder';
+import { SchemaFromBackend } from '@graphpolaris/shared/lib/schema';
+import { useRef, useState, useEffect } from 'react';
+import { DatabaseInfo, DatabaseStatus, SaveStateI, TestDatabaseConnectionResponse, wsGetStates } from './wsState';
+import { wsSchemaRequest } from './wsSchema';
+import { addSaveState, testedSaveState, updateSaveStateList } from '../store/sessionSlice';
+
+export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }) => {
+  const { login } = useAuth();
+  const auth = useAuthorizationCache();
+  const dispatch = useAppDispatch();
+  const session = useSessionCache();
+  const queryHash = useQuerybuilderHash();
+  const queryBuilder = useQuerybuilder();
+  const mlHash = useMLEnabledHash();
+  const queryBuilderSettings = useQuerybuilderSettings();
+
+  useEffect(() => {
+    // Default
+    Broker.instance().subscribe((data: SchemaFromBackend) => {
+      dispatch(readInSchemaFromBackend(data));
+      dispatch(addInfo('Schema graph updated'));
+    }, 'schema_result');
+    Broker.instance().subscribe((data: GraphQueryResultFromBackendPayload) => {
+      dispatch(assignNewGraphQueryResult(data));
+      dispatch(addInfo('Query Executed!'));
+    }, 'query_result');
+    // Broker.instance().subscribe((data: QueryBuilderState) => dispatch(setQuerybuilderNodes(data)), 'query_builder_state');
+    allMLTypes.forEach((mlType) => {
+      Broker.instance().subscribe((data: LinkPredictionInstance[]) => dispatch(setMLResult({ type: mlType, result: data })), mlType);
+    });
+
+    Broker.instance().subscribe((data: SaveStateI[]) => {
+      dispatch(updateSaveStateList(data));
+    }, 'save_states');
+    Broker.instance().subscribe((data: any) => {}, 'save_state_status');
+    Broker.instance().subscribe((data: SaveStateI) => {
+      dispatch(addSaveState(data));
+    }, 'save_state');
+    Broker.instance().subscribe((data: SaveStateI) => {
+      wsGetStates();
+    }, 'delete_save_state');
+    Broker.instance().subscribe((response: TestDatabaseConnectionResponse) => {
+      if (response && response.status === 'success') dispatch(testedSaveState(response.saveStateID));
+    }, 'tested_connection');
+
+    login();
+
+    return () => {
+      Broker.instance().unSubscribeAll('schema_result');
+      Broker.instance().unSubscribeAll('query_result');
+
+      Broker.instance().unSubscribeAll('save_states');
+      Broker.instance().unSubscribeAll('save_state');
+      Broker.instance().unSubscribeAll('save_state_status');
+      Broker.instance().unSubscribeAll('delete_save_state');
+      Broker.instance().unSubscribeAll('tested_connection');
+      // Broker.instance().unSubscribeAll('query_builder_state');
+      allMLTypes.forEach((mlType) => {
+        Broker.instance().unSubscribeAll(mlType);
+      });
+    };
+  }, []);
+
+  useEffect(() => {
+    // New active database
+    if (session.currentSaveState) {
+      wsSchemaRequest(session.currentSaveState);
+    }
+  }, [session.currentSaveState]);
+
+  useEffect(() => {
+    // Newly (un)authorized
+    if (auth.authorized && auth.jwt) {
+      props.onAuthorized();
+      WebSocketHandler.instance()
+        .useAuth(auth)
+        .connect(() => {
+          wsGetStates();
+          // WebSocketHandler.instance().sendMessage({ //TODO!!
+          //   sessionID: auth?.sessionID || '',
+          //   key: 'broadcastState',
+          //   body: { type: 'subscribe', status: '', value: {} },
+          // });
+        });
+    } else {
+      // dispatch(logout());
+    }
+  }, [auth]);
+
+  useEffect(() => {
+    if (!queryBuilder.ignoreReactivity) {
+      props.onRunQuery();
+      // WebSocketHandler.instance().sendMessage({ //TODO!!
+      //   sessionID: auth?.sessionID || '',
+      //   key: 'broadcastState',
+      //   body: { type: 'query_builder_state', status: '', value: queryBuilder },
+      // });
+    }
+  }, [queryHash, mlHash, queryBuilderSettings]);
+
+  return <div className="hide"></div>;
+};
diff --git a/libs/shared/lib/data-access/api/index.ts b/libs/shared/lib/data-access/api/index.ts
index c31d6f4c9..e107aebcc 100644
--- a/libs/shared/lib/data-access/api/index.ts
+++ b/libs/shared/lib/data-access/api/index.ts
@@ -1,3 +1,2 @@
-export * from './database';
-export * from './schema';
-export * from './query';
+export * from './wsState';
+export * from './wsSchema';
diff --git a/libs/shared/lib/data-access/api/query.ts b/libs/shared/lib/data-access/api/query.ts
deleted file mode 100644
index 29e6752f1..000000000
--- a/libs/shared/lib/data-access/api/query.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-// All database related API calls
-
-import { BackendQueryFormat } from '../../querybuilder/model/BackendQueryFormat';
-import { useAuth } from '../authorization';
-import { useAuthorizationCache, useSessionCache } from '../store';
-
-export const useQueryAPI = () => {
-  const domain = import.meta.env.BACKEND_URL;
-  const query_url = import.meta.env.BACKEND_QUERY;
-  const { fetchAuthenticated } = useAuth();
-
-  async function execute(query: BackendQueryFormat) {
-    const response = await fetchAuthenticated(`${domain}${query_url}/execute`, {
-      method: 'POST',
-      body: JSON.stringify(query),
-    });
-
-    if (!response?.ok) {
-      const ret = await response.text();
-      console.error(response, ret);
-      throw Error(response.statusText);
-    }
-    const ret = await response.json();
-    console.debug('Sent Query EXECUTION', ret);
-  }
-
-  async function retrieveCachedQuery(queryID: string) {
-    // TODO: check if this method is needed!
-    // const response = await fetchAuthenticated(`${domain}/query/retrieve-cached/`, {
-    //     method: 'POST',
-    //     body: JSON.stringify({ queryID })
-    // });
-    // if (!response?.ok) {
-    //     const ret = await response.text();
-    //     console.error(response, ret);
-    // }
-    // // const ret = await response.json();
-    // console.log(response);
-  }
-
-  return { execute, retrieveCachedQuery };
-};
diff --git a/libs/shared/lib/data-access/api/schema.ts b/libs/shared/lib/data-access/api/schema.ts
deleted file mode 100644
index c310676c3..000000000
--- a/libs/shared/lib/data-access/api/schema.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-// All database related API calls
-
-import { useAuth } from '../authorization';
-import { useSessionCache } from '../store';
-
-export const useSchemaAPI = () => {
-  const cache = useSessionCache();
-  const domain = import.meta.env.BACKEND_URL;
-  const schema = import.meta.env.BACKEND_SCHEMA;
-  const { fetchAuthenticated } = useAuth();
-
-  async function RequestSchema(databaseName?: string) {
-    if (!databaseName) databaseName = cache.currentDatabase;
-    if (!databaseName) {
-      throw Error('Must call with a database name');
-    }
-
-    const request = {
-      databaseName,
-      cached: false,
-      // cached: true,
-    };
-
-    const response = await fetchAuthenticated(`${domain}${schema}`, {
-      method: 'POST',
-      body: JSON.stringify(request),
-    });
-
-    if (!response?.ok) {
-      const ret = await response.text();
-      console.error(response, ret);
-    }
-    // const ret = await response.json();
-    // console.debug('Schema Requested', response);
-  }
-
-  return { RequestSchema };
-};
diff --git a/libs/shared/lib/data-access/api/wsQuery.ts b/libs/shared/lib/data-access/api/wsQuery.ts
new file mode 100644
index 000000000..9c7cf9a71
--- /dev/null
+++ b/libs/shared/lib/data-access/api/wsQuery.ts
@@ -0,0 +1,16 @@
+// All database related API calls
+
+import { log } from 'console';
+import { useAuth } from '../authorization';
+import { WebSocketHandler } from '../socket';
+import { useSessionCache } from '../store';
+import { BackendQueryFormat } from '../../querybuilder';
+
+export function wsQueryRequest(query: BackendQueryFormat) {
+  if (query.cached === undefined) query.cached = false;
+  WebSocketHandler.instance().sendMessage({
+    key: 'query',
+    subKey: 'get',
+    body: query,
+  });
+}
diff --git a/libs/shared/lib/data-access/api/wsSchema.ts b/libs/shared/lib/data-access/api/wsSchema.ts
new file mode 100644
index 000000000..006e353b8
--- /dev/null
+++ b/libs/shared/lib/data-access/api/wsSchema.ts
@@ -0,0 +1,17 @@
+// All database related API calls
+
+import { log } from 'console';
+import { useAuth } from '../authorization';
+import { WebSocketHandler } from '../socket';
+import { useSessionCache } from '../store';
+
+export function wsSchemaRequest(saveStateID: string) {
+  WebSocketHandler.instance().sendMessage({
+    key: 'schema',
+    subKey: 'get',
+    body: {
+      cached: false,
+      saveStateID: saveStateID,
+    },
+  });
+}
diff --git a/libs/shared/lib/data-access/api/wsState.tsx b/libs/shared/lib/data-access/api/wsState.tsx
new file mode 100644
index 000000000..6a0ff2eab
--- /dev/null
+++ b/libs/shared/lib/data-access/api/wsState.tsx
@@ -0,0 +1,102 @@
+import { useEffect } from 'react';
+import { WebSocketHandler } from '../socket';
+import Broker from '../socket/broker';
+import { keyTypeI, subKeyTypeI } from '../socket/types';
+import { useAppDispatch, useConfig } from '../store';
+import { addSaveState, updateSaveStateList } from '../store/sessionSlice';
+
+// export function wsGetState() {
+//   Broker.instance().subscribe((data) => dispatch(readInSchemaFromBackend(data)), 'schema_result');
+//   WebSocketHandler.instance().sendMessage({});
+// }
+
+export const databaseNameMapping: string[] = ['arangodb', 'neo4j'];
+export const databaseProtocolMapping: string[] = ['neo4j://', 'neo4j+s://', 'bolt://', 'bolt+s://'];
+
+export enum DatabaseType {
+  ArangoDB = 0,
+  Neo4j = 1,
+}
+
+export enum DatabaseStatus {
+  untested = 0,
+  testing = 1,
+  tested = 2,
+}
+
+export type DatabaseInfo = {
+  internalDatabaseName: string;
+  url: string;
+  port: number;
+  protocol: string;
+  username: string;
+  password: string;
+  type: number;
+  status?: DatabaseStatus;
+};
+
+export const nilUUID = '00000000-0000-0000-0000-000000000000';
+
+export type SaveStateI = {
+  id: string;
+  name: string;
+  db: DatabaseInfo;
+};
+
+export function wsGetStates() {
+  WebSocketHandler.instance().sendMessage({
+    key: 'state',
+    subKey: 'getAll',
+  });
+}
+
+export function wsSelectState(saveStateId: string | undefined) {
+  if (saveStateId === undefined) saveStateId = '';
+  WebSocketHandler.instance().sendMessage({
+    key: 'state',
+    subKey: 'select',
+    body: { saveStateId: saveStateId }, //messageTypeGetSaveState
+  });
+  WebSocketHandler.instance().useSaveStateID(saveStateId);
+}
+
+export function wsCreateState(request: SaveStateI) {
+  WebSocketHandler.instance().sendMessage({
+    key: 'state',
+    subKey: 'create',
+    body: request, //SaveStateModel
+  });
+}
+
+export function wsDeleteState(id: string) {
+  WebSocketHandler.instance().sendMessage({
+    key: 'state',
+    subKey: 'delete',
+    body: { saveStateId: id }, //messageTypeGetSaveState
+  });
+}
+
+export function wsTestSaveStateConnection(id: string) {
+  WebSocketHandler.instance().sendMessage({
+    key: 'state',
+    subKey: 'testConnection',
+    body: { saveStateId: id }, //messageTypeGetSaveState
+  });
+}
+
+export function wsUpdateState(request: SaveStateI) {
+  WebSocketHandler.instance().sendMessage({
+    key: 'state',
+    subKey: 'update',
+    body: request, //SaveStateModel
+  });
+}
+
+export type TestDatabaseConnectionResponse = { status: 'success' | 'fail'; saveStateID: string };
+export function wsTestDatabaseConnection(dbConnection: DatabaseInfo) {
+  WebSocketHandler.instance().sendMessage({
+    key: 'dbConnection',
+    subKey: 'testConnection',
+    body: dbConnection, //DBConnectionModel
+  });
+}
diff --git a/libs/shared/lib/data-access/authorization/dashboardAlerts.tsx b/libs/shared/lib/data-access/authorization/dashboardAlerts.tsx
index 336b6af78..7809272df 100644
--- a/libs/shared/lib/data-access/authorization/dashboardAlerts.tsx
+++ b/libs/shared/lib/data-access/authorization/dashboardAlerts.tsx
@@ -4,8 +4,9 @@ import { useImmer } from 'use-immer';
 import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
 import { useAppDispatch, useConfig } from '../store';
 import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
-import { removeLastError, removeLastWarning } from '../store/configSlice';
+import { removeLastError, removeLastInfo, removeLastSuccess, removeLastWarning } from '../store/configSlice';
 import { includes } from 'lodash-es';
+import { ReceiveMessageI } from '../socket/types';
 
 type Message = {
   message: ReactNode;
@@ -63,37 +64,6 @@ export const DashboardAlerts = (props: { timer?: number }) => {
     }
   }
 
-  useEffect(() => {
-    Broker.instance().subscribeDefault((data: any, routingKey: string) => {
-      let message: Message | undefined = undefined;
-
-      // Use the logic below to define which broker messages should be shown as alerts.
-      if (routingKey === 'schema_result') {
-        message = {
-          message: (
-            <>
-              <CheckCircleOutlineIcon /> Schema graph updated
-            </>
-          ),
-          className: 'alert-success',
-        };
-      } else if (routingKey === 'query_result') {
-        message = {
-          message: (
-            <>
-              <CheckCircleOutlineIcon /> Query Executed!
-            </>
-          ),
-          className: 'alert-success',
-        };
-      }
-      processMessage(message, data, routingKey);
-    });
-    return () => {
-      Broker.instance().unSubscribeDefault();
-    };
-  }, []);
-
   async function processError() {
     if (config.errors.length > 0) {
       await processMessage(
@@ -106,7 +76,7 @@ export const DashboardAlerts = (props: { timer?: number }) => {
           className: 'alert-error',
         },
         undefined,
-        'error',
+        'error'
       );
       dispatch(removeLastError());
     }
@@ -124,12 +94,48 @@ export const DashboardAlerts = (props: { timer?: number }) => {
           className: 'alert-warning',
         },
         undefined,
-        'warning',
+        'warning'
       );
       dispatch(removeLastWarning());
     }
   }
 
+  async function processInfo() {
+    if (config.infos.length > 0) {
+      await processMessage(
+        {
+          message: (
+            <>
+              <CheckCircleOutlineIcon /> {config.infos[config.infos.length - 1]}
+            </>
+          ),
+          className: 'alert-success bg-primary',
+        },
+        undefined,
+        'info'
+      );
+      dispatch(removeLastInfo());
+    }
+  }
+
+  async function processSuccess() {
+    if (config.successes.length > 0) {
+      await processMessage(
+        {
+          message: (
+            <>
+              <CheckCircleOutlineIcon /> {config.successes[config.successes.length - 1]}
+            </>
+          ),
+          className: 'alert-success',
+        },
+        undefined,
+        'success'
+      );
+      dispatch(removeLastSuccess());
+    }
+  }
+
   useEffect(() => {
     processError();
   }, [config.errors]);
@@ -138,6 +144,14 @@ export const DashboardAlerts = (props: { timer?: number }) => {
     processWarning();
   }, [config.warnings]);
 
+  useEffect(() => {
+    processInfo();
+  }, [config.infos]);
+
+  useEffect(() => {
+    processSuccess();
+  }, [config.successes]);
+
   return (
     <>
       {messages &&
@@ -160,7 +174,6 @@ export const DashboardAlerts = (props: { timer?: number }) => {
               }}
             >
               <span className="flex flex-row content-center gap-3 text-light">{m.message.message}</span>
-              {/* {!message && (m.data?.status ? m.data.status : m.routingKey)} */}
             </div>
           );
         })}
diff --git a/libs/shared/lib/data-access/authorization/useAuth.tsx b/libs/shared/lib/data-access/authorization/useAuth.tsx
index 3d77c8907..abab06a86 100644
--- a/libs/shared/lib/data-access/authorization/useAuth.tsx
+++ b/libs/shared/lib/data-access/authorization/useAuth.tsx
@@ -1,11 +1,13 @@
 import { useEffect, useRef, useState } from 'react';
 import { useAppDispatch, useAuthorizationCache } from '../store';
 
-import { authorized } from '../store/authSlice';
+import { authorized, changeRoom } from '../store/authSlice';
 
 export type AuthenticationHeader = {
   username: string;
+  userID: string;
   sessionID: string;
+  roomID: string;
   jwt: string;
 };
 
@@ -23,20 +25,7 @@ export const useAuth = () => {
   const auth = useAuthorizationCache();
 
   const handleError = (err: any) => {
-    if (domain.includes('localhost')) {
-      console.warn('skipping login for localhost');
-      dispatch(
-        authorized({
-          username: 'UserID',
-          sessionID: 'SessionID',
-          jwt: 'jwt',
-          authorized: true,
-        })
-      );
-      return;
-    } else {
-      console.error(err);
-    }
+    console.error(err);
   };
 
   const login = () => {
@@ -48,6 +37,7 @@ export const useAuth = () => {
             dispatch(
               authorized({
                 username: res.username,
+                userID: res.userID,
                 sessionID: res.sessionID,
                 jwt: res.jwt,
                 authorized: true,
@@ -59,26 +49,20 @@ export const useAuth = () => {
       .catch(handleError);
   };
 
-  const fetchAuthenticated = (input: RequestInfo | URL, init?: RequestInit | undefined): Promise<Response> => {
-    if (!init) init = fetchSettings;
-
-    if (!domain.includes('localhost')) {
-      // Production logic
-      init.credentials = 'include';
-      init.redirect = 'follow';
-      init.method = init.method || 'GET';
-      init.headers = {
-        'Content-Type': 'application/json',
-        sessionid: auth.sessionID || '',
-        //   Authorization: `Bearer ${auth.jwt}`,
-        ...init.headers,
-      };
-    }
-
-    return fetch(input, init);
+  const newShareRoom = () => {
+    fetch(`${domain}${useruri}/share`, { ...fetchSettings, method: 'POST' })
+      .then((res) =>
+        res
+          .json()
+          .then((res: { Roomid: string; Sessionid: string }) => {
+            // TODO: send to backend current state and make redux accordingly
+            dispatch(changeRoom(res.Roomid));
+          })
+          .catch(handleError)
+      )
+      .catch(handleError);
   };
 
-  return { login, fetchAuthenticated };
+  return { login, newShareRoom };
 };
-
 // export useAuth;
diff --git a/libs/shared/lib/data-access/socket/backend-message-receiver/WebSocketHandler.tsx b/libs/shared/lib/data-access/socket/backend-message-receiver/WebSocketHandler.tsx
index d457018c0..096738ed4 100644
--- a/libs/shared/lib/data-access/socket/backend-message-receiver/WebSocketHandler.tsx
+++ b/libs/shared/lib/data-access/socket/backend-message-receiver/WebSocketHandler.tsx
@@ -4,17 +4,19 @@
  * © Copyright Utrecht University (Department of Information and Computing Sciences)
  */
 
-import { AuthenticationHeader } from '../..';
 import { UseIsAuthorizedState } from '../../store/authSlice';
 import Broker from '../broker';
+import { ReceiveMessageI, SendMessageI, SendMessageWithSessionI } from '../types';
 import BackendMessageReceiver from './BackendMessageReceiver';
 
 /** The websockethandler creates a websocket and wait for messages send to the socket. */
 export class WebSocketHandler implements BackendMessageReceiver {
+  private static singletonInstance: WebSocketHandler;
   private webSocket: WebSocket | undefined;
   private url: string;
   private connected: boolean;
   private authHeader: UseIsAuthorizedState | undefined;
+  private saveStateID: string | undefined;
 
   /** @param domain The domain to make the websocket connection with. */
   public constructor(domain: string) {
@@ -22,11 +24,21 @@ export class WebSocketHandler implements BackendMessageReceiver {
     this.connected = false;
   }
 
+  public static instance(): WebSocketHandler {
+    if (!this.singletonInstance) this.singletonInstance = new WebSocketHandler(import.meta.env.BACKEND_WSS_URL);
+    return this.singletonInstance;
+  }
+
   public useAuth(authHeader: UseIsAuthorizedState): WebSocketHandler {
     this.authHeader = authHeader;
     return this;
   }
 
+  public useSaveStateID(saveStateID: string): WebSocketHandler {
+    this.saveStateID = saveStateID;
+    return this;
+  }
+
   /**
    * Create a websocket to the given URL.
    * @param {string} URL is the URL to which the websocket connection is opened.
@@ -35,22 +47,42 @@ export class WebSocketHandler implements BackendMessageReceiver {
     // If there already is already a current websocket connection, close it first.
     if (this.webSocket) this.close();
 
-    const params = new URLSearchParams();
+    const params = new URLSearchParams(window.location.search);
+    if (this.authHeader?.userID) params.set('userID', this.authHeader?.userID ?? ''); // TODO!! need a better more safe way to do this
+    if (this.authHeader?.roomID) params.set('roomID', this.authHeader?.roomID ?? '');
+    if (this.saveStateID) params.set('saveStateID', this.saveStateID ?? '');
     if (this.authHeader?.sessionID) params.set('sessionID', this.authHeader?.sessionID ?? '');
     if (this.authHeader?.jwt) params.set('jwt', this.authHeader?.jwt ?? '');
     this.webSocket = new WebSocket(this.url + '?' + params.toString());
-    this.webSocket.onopen = () => onOpen();
+    this.webSocket.onopen = () => {
+      this.connected = true;
+      onOpen();
+    };
     this.webSocket.onmessage = this.onWebSocketMessage;
     this.webSocket.onerror = this.onError;
     this.webSocket.onclose = this.onClose;
+  }
 
-    this.connected = true;
+  public sendMessage(message: SendMessageI): void {
+    console.debug('%cSending WS message: ', 'background: #222; color: #bada55', message);
+    let fullMessage = message as SendMessageWithSessionI;
+    fullMessage.sessionID = this.authHeader?.sessionID ?? '';
+    if (message.body && typeof message.body !== 'string') {
+      fullMessage.body = JSON.stringify(message.body);
+    }
+
+    if (this.webSocket && this.connected && this.webSocket.readyState === 1) this.webSocket.send(JSON.stringify(fullMessage));
+    else
+      this.connect(() => {
+        if (this.webSocket && this.connected && this.webSocket.readyState === 1) this.webSocket.send(JSON.stringify(fullMessage));
+      });
   }
 
   /** Closes the current websocket connection. */
   public close = (): void => {
     if (this.webSocket) this.webSocket.close();
     this.connected = false;
+    this.webSocket = undefined;
   };
 
   /** @returns A boolean which indicates if there currently is a socket connection. */
@@ -58,12 +90,28 @@ export class WebSocketHandler implements BackendMessageReceiver {
     return this.connected;
   };
 
+  public attemptReconnect = () => {
+    console.warn('Attempting to reconnect WS');
+
+    if (!this.connected || !this.webSocket || this.webSocket.readyState !== 1) {
+      this.connect(() => {
+        setTimeout(() => WebSocketHandler.instance().attemptReconnect(), 5000);
+      });
+    } else {
+      console.log('WS reconnected', this.webSocket?.readyState, this.connected);
+    }
+  };
+
   /**
    * Websocket connection close event handler.
    * @param {any} event Contains the event data.
    */
   private onClose(event: any): void {
-    console.log(event.data);
+    console.warn('WS connection was closed from the server side', event.data);
+    if (this.webSocket) this.webSocket.close();
+    this.connected = false;
+    this.webSocket = undefined;
+    setTimeout(() => WebSocketHandler.instance().attemptReconnect(), 5000);
   }
 
   /**
@@ -71,7 +119,7 @@ export class WebSocketHandler implements BackendMessageReceiver {
    * @param {any} event Contains the event data.
    */
   public onWebSocketMessage = (event: MessageEvent<any>) => {
-    let data = JSON.parse(event.data);
+    let data: ReceiveMessageI = JSON.parse(event.data);
     Broker.instance().publish(data.value, data.type);
   };
 
@@ -80,6 +128,6 @@ export class WebSocketHandler implements BackendMessageReceiver {
    * @param {any} event contains the event data.
    */
   private onError(event: any): void {
-    console.error(event);
+    console.error('WS error', event);
   }
 }
diff --git a/libs/shared/lib/data-access/socket/broker/index.tsx b/libs/shared/lib/data-access/socket/broker/index.tsx
index 4f56a0a40..203c53090 100644
--- a/libs/shared/lib/data-access/socket/broker/index.tsx
+++ b/libs/shared/lib/data-access/socket/broker/index.tsx
@@ -4,6 +4,8 @@
  * © Copyright Utrecht University (Department of Information and Computing Sciences)
  */
 
+import { ReceiveMessageI } from '../types';
+
 /**
  * A broker that handles incoming messages from the backend.
  * It works with routingkeys, a listener can subscribe to messages from the backend with a specific routingkey.
@@ -20,7 +22,7 @@
 export default class Broker {
   private static singletonInstance: Broker;
   private listeners: Record<string, Record<string, Function>> = {};
-  private catchAllListener: Function | undefined;
+  private catchAllListener: ((data: Record<string, any>, routingKey: string) => void) | undefined;
 
   /** mostRecentMessages is a dictionary with <routingkey, messageObject>. It stores the most recent message for that routingkey. */
   private mostRecentMessages: Record<string, unknown> = {};
@@ -33,10 +35,10 @@ export default class Broker {
 
   /**
    * Notify all listeners which are subscribed with the specified routingkey.
-   * @param {unknown} jsonObject An json object with unknown type.
+   * @param {ReceiveMessageI} jsonObject An json object with unknown type.
    * @param {string} routingKey The routing to publish the message to.
    */
-  public publish(jsonObject: unknown, routingKey: string): void {
+  public publish(jsonObject: Record<string, any>, routingKey: string): void {
     this.mostRecentMessages[routingKey] = jsonObject;
 
     if (this.listeners[routingKey] && Object.keys(this.listeners[routingKey]).length != 0) {
@@ -44,15 +46,15 @@ export default class Broker {
         this.catchAllListener(jsonObject, routingKey);
       }
       Object.values(this.listeners[routingKey]).forEach((listener) => listener(jsonObject, routingKey));
-      console.debug(routingKey, `message processed with routing key`, jsonObject);
+      console.debug('%c' + routingKey + ` WS response`, 'background: #222; color: #DBAB2F', jsonObject);
     }
-    // If there are no listeners, log the message
+    // If there are no listeners, log the messagep
     else {
       if (this.catchAllListener) {
         this.catchAllListener(jsonObject, routingKey);
         console.debug(routingKey, `catch all used for message with routing key`, jsonObject);
       } else {
-        console.debug(routingKey, `no listeners for message with routing key`, jsonObject);
+        console.debug('%c' + routingKey + ` no listeners for message with routing key`, 'background: #663322; color: #DBAB2F', jsonObject);
       }
     }
   }
@@ -67,7 +69,7 @@ export default class Broker {
     newListener: Function,
     routingKey: string,
     key: string = (Date.now() + Math.floor(Math.random() * 100)).toString(),
-    consumeMostRecentMessage: boolean = true
+    consumeMostRecentMessage: boolean = false
   ): string {
     if (!this.listeners[routingKey]) this.listeners[routingKey] = {};
 
@@ -88,7 +90,7 @@ export default class Broker {
    * @param {string} routingKey The routingkey to subscribe to.
    * @param {boolean} consumeMostRecentMessage If true and there is a message for this routingkey available, notify the new listener. Default true.
    */
-  public subscribeDefault(newListener: Function): void {
+  public subscribeDefault(newListener: (data: Record<string, any>, routingKey: string) => void): void {
     this.catchAllListener = newListener;
   }
 
diff --git a/libs/shared/lib/data-access/socket/types.ts b/libs/shared/lib/data-access/socket/types.ts
new file mode 100644
index 000000000..ce9f967eb
--- /dev/null
+++ b/libs/shared/lib/data-access/socket/types.ts
@@ -0,0 +1,50 @@
+export type ReceiveMessageI = {
+  type: string;
+  status: string;
+  value: Record<string, any>;
+};
+
+type SchemaServiceOrchestratorMessage = {
+  databaseName: string;
+  cached: boolean;
+};
+type SchemaStatsServiceOrchestratorMessage = {
+  databaseName: string;
+  cached: boolean;
+};
+type QueryOrchestratorMessage = {
+  databaseName: string;
+  cached: boolean;
+  queryID: string;
+};
+
+export type keyTypeI = 'broadcastState' | 'dbConnection' | 'schema' | 'query' | 'state';
+export type subKeyTypeI =
+  // Crud
+  | 'create'
+  | 'getAll'
+  | 'delete'
+  | 'update'
+  | 'get'
+  | 'select'
+  // Custom
+  | 'newDbConnection'
+  | 'editDbConnection'
+  | 'deleteDbConnection'
+  | 'getDbConnection'
+  | 'getAllDbConnections'
+  | 'testConnection'
+  | 'getSchema'
+  | 'getSchemaStats'
+  | 'runQuery';
+
+export type SendMessageI = {
+  key: keyTypeI;
+  subKey?: subKeyTypeI;
+  body?: any;
+};
+
+export type SendMessageWithSessionI = SendMessageI & {
+  sessionID: string;
+  body?: string;
+};
diff --git a/libs/shared/lib/data-access/store/authSlice.ts b/libs/shared/lib/data-access/store/authSlice.ts
index 13b1bfbad..544272435 100644
--- a/libs/shared/lib/data-access/store/authSlice.ts
+++ b/libs/shared/lib/data-access/store/authSlice.ts
@@ -1,10 +1,15 @@
 import { createSlice, PayloadAction } from '@reduxjs/toolkit';
 import type { RootState } from './store';
 
-export type UseIsAuthorizedState = {
+export type UseIsAuthorizedState = SingleIsAuthorizedState & {
+  roomID: string | undefined;
+};
+
+export type SingleIsAuthorizedState = {
   authorized: boolean | undefined;
   jwt: string | undefined;
   sessionID: string | undefined;
+  userID: string | undefined;
   username: string | undefined;
 };
 
@@ -13,6 +18,8 @@ export const initialState: UseIsAuthorizedState = {
   authorized: undefined,
   jwt: undefined,
   sessionID: undefined,
+  roomID: undefined,
+  userID: undefined,
   username: undefined,
 };
 
@@ -21,28 +28,48 @@ export const authSlice = createSlice({
   // `createSlice` will infer the state type from the `initialState` argument
   initialState,
   reducers: {
-    authorized(state, action: PayloadAction<UseIsAuthorizedState>) {
-      console.info('Authorized');
+    authorized(state, action: PayloadAction<SingleIsAuthorizedState>) {
+      console.info('%cAuthorized', 'background-color: blue');
       state.authorized = action.payload.authorized;
       state.jwt = action.payload.jwt;
+      state.userID = action.payload.userID;
       state.sessionID = action.payload.sessionID;
       state.username = action.payload.username;
     },
+    changeRoom(state, action: PayloadAction<string | undefined>) {
+      console.info('Changing Room to', action.payload);
+      state.roomID = action.payload;
+      const query = new URLSearchParams(window.location.search);
+      if (!!action?.payload) {
+        query.set('roomID', action?.payload || 'null');
+        history.pushState(null, '', '?' + query.toString());
+      } else {
+        query.delete('roomID');
+        history.pushState(null, '', '?' + query.toString());
+      }
+    },
     logout(state) {
       console.info('Logging out');
       state.authorized = undefined;
       state.jwt = undefined;
       state.sessionID = undefined;
+      state.userID = undefined;
       state.username = undefined;
+      const query = new URLSearchParams(window.location.search);
+      query.delete('roomID');
+      history.pushState(null, '', '?' + query.toString());
     },
     unauthorized(state) {
       console.warn('Unauthorized');
       state.authorized = false;
+      const query = new URLSearchParams(window.location.search);
+      query.delete('roomID');
+      history.pushState(null, '', '?' + query.toString());
     },
   },
 });
 
-export const { authorized, unauthorized, logout } = authSlice.actions;
+export const { authorized, unauthorized, logout, changeRoom } = authSlice.actions;
 
 // Other code such as selectors can use the imported `RootState` type
 export const authState = (state: RootState) => state.auth;
diff --git a/libs/shared/lib/data-access/store/configSlice.ts b/libs/shared/lib/data-access/store/configSlice.ts
index 2d6d2393c..3e3fa8c91 100644
--- a/libs/shared/lib/data-access/store/configSlice.ts
+++ b/libs/shared/lib/data-access/store/configSlice.ts
@@ -3,26 +3,17 @@ import type { RootState } from './store';
 
 // Define the initial state using that type
 export const initialState: {
-  queryListOpen: boolean;
-  queryStatusList: {
-    queries: Record<string, any>;
-    queryIDsOrder: string[];
-  };
-  functionsMenuOpen: boolean;
-  currentDatabaseKey: string;
-  elementsperDatabaseObject: Record<string, number>;
   autoSendQueries: boolean;
   errors: string[];
   warnings: string[];
+  infos: string[];
+  successes: string[];
 } = {
-  queryListOpen: false,
-  queryStatusList: { queries: {}, queryIDsOrder: [] },
-  functionsMenuOpen: false,
-  currentDatabaseKey: '',
-  elementsperDatabaseObject: {},
   autoSendQueries: true,
   errors: [],
   warnings: [],
+  infos: [],
+  successes: [],
 };
 
 export const configSlice = createSlice({
@@ -30,6 +21,18 @@ export const configSlice = createSlice({
   // `createSlice` will infer the state type from the `initialState` argument
   initialState,
   reducers: {
+    addSuccess: (state, action: PayloadAction<string>) => {
+      state.successes.push(action.payload);
+    },
+    removeLastSuccess: (state) => {
+      state.successes.shift();
+    },
+    addInfo: (state, action: PayloadAction<string>) => {
+      state.infos.push(action.payload);
+    },
+    removeLastInfo: (state) => {
+      state.infos.shift();
+    },
     addError: (state, action: PayloadAction<string>) => {
       console.error('Error Received!', action.payload);
       state.errors.push(action.payload);
@@ -47,7 +50,8 @@ export const configSlice = createSlice({
   },
 });
 
-export const { addError, removeLastError, addWarning, removeLastWarning } = configSlice.actions;
+export const { addError, removeLastError, addWarning, removeLastWarning, addSuccess, removeLastSuccess, addInfo, removeLastInfo } =
+  configSlice.actions;
 
 // Other code such as selectors can use the imported `RootState` type
 export const configState = (state: RootState) => state.config;
diff --git a/libs/shared/lib/data-access/store/hooks.ts b/libs/shared/lib/data-access/store/hooks.ts
index e22bf1997..282db06af 100644
--- a/libs/shared/lib/data-access/store/hooks.ts
+++ b/libs/shared/lib/data-access/store/hooks.ts
@@ -5,6 +5,7 @@ import type { RootState, AppDispatch } from './store';
 import { configState } from '@graphpolaris/shared/lib/data-access/store/configSlice';
 import {
   queryBuilderSettingsState,
+  queryBuilderState,
   selectQuerybuilderGraph,
   selectQuerybuilderHash,
 } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
@@ -22,15 +23,16 @@ export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
 /** Gives the graphQueryResult from the store */
 export const useGraphQueryResult = () => useAppSelector(selectGraphQueryResult);
 
-// Gives the schema form the store (as a graphology object)
+// Gives the schema
 export const useSchemaGraph = () => useAppSelector(schemaGraph);
 export const useSchemaSettings = () => useAppSelector(schemaSettingsState);
-
-// Gives the schema form the store (as a graphology object)
 export const useSchemaLayout = () => useAppSelector(selectSchemaLayout);
+
+// Querybuilder Slices
 export const useQuerybuilderGraph = () => useAppSelector(selectQuerybuilderGraph);
 export const useQuerybuilderHash = () => useAppSelector(selectQuerybuilderHash);
 export const useQuerybuilderSettings = () => useAppSelector(queryBuilderSettingsState);
+export const useQuerybuilder = () => useAppSelector(queryBuilderState);
 
 // Overall Configuration of the app
 export const useConfig = () => useAppSelector(configState);
diff --git a/libs/shared/lib/data-access/store/index.ts b/libs/shared/lib/data-access/store/index.ts
index 7bff27287..8b0db040c 100644
--- a/libs/shared/lib/data-access/store/index.ts
+++ b/libs/shared/lib/data-access/store/index.ts
@@ -2,7 +2,7 @@ export * from './store';
 export * from './hooks';
 
 export { setSchema, readInSchemaFromBackend, schemaSlice, selectSchemaLayout } from './schemaSlice';
-export { querybuilderSlice, setQuerybuilderGraph as setQuerybuilderNodes } from './querybuilderSlice';
+export { querybuilderSlice, setQuerybuilderGraph, setQuerybuilderNodes } from './querybuilderSlice';
 export {
   selectGraphQueryResult,
   selectGraphQueryResultLinks,
diff --git a/libs/shared/lib/data-access/store/querybuilderSlice.ts b/libs/shared/lib/data-access/store/querybuilderSlice.ts
index 65673108e..98e282a8c 100644
--- a/libs/shared/lib/data-access/store/querybuilderSlice.ts
+++ b/libs/shared/lib/data-access/store/querybuilderSlice.ts
@@ -13,12 +13,16 @@ export type QueryBuilderSettings = {
   layout: AllLayoutAlgorithms | 'manual';
 };
 
-// Define the initial state using that type
-export const initialState: {
+export type QueryBuilderState = {
   graph: QueryMultiGraph;
+  ignoreReactivity: boolean;
   settings: QueryBuilderSettings;
-} = {
+};
+
+// Define the initial state using that type
+export const initialState: QueryBuilderState = {
   graph: defaultGraph(),
+  ignoreReactivity: false,
   settings: {
     limit: 500,
     depth: { min: 1, max: 1 },
@@ -34,6 +38,12 @@ export const querybuilderSlice = createSlice({
     setQuerybuilderGraph: (state, action: PayloadAction<QueryMultiGraph>) => {
       // @ts-ignore
       state.graph = action.payload;
+      state.ignoreReactivity = false;
+    },
+    setQuerybuilderNodes: (state, action: PayloadAction<QueryBuilderState>) => {
+      state.graph = action.payload.graph;
+      state.settings = action.payload.settings;
+      state.ignoreReactivity = true;
     },
     clearQB: (state) => {
       state.graph = defaultGraph();
@@ -44,6 +54,7 @@ export const querybuilderSlice = createSlice({
   },
 });
 
+export const queryBuilderState = (state: RootState) => state.querybuilder;
 export const queryBuilderSettingsState = (state: RootState) => state.querybuilder.settings;
 
 export const setQuerybuilderGraphology = (payload: QueryGraphology) => {
@@ -97,4 +108,4 @@ export const selectQuerybuilderHash = (state: RootState): any => {
 //   state.schema.schemaLayout;
 
 export default querybuilderSlice.reducer;
-export const { setQuerybuilderGraph, clearQB, setQuerybuilderSettings } = querybuilderSlice.actions;
+export const { setQuerybuilderGraph, clearQB, setQuerybuilderSettings, setQuerybuilderNodes } = querybuilderSlice.actions;
diff --git a/libs/shared/lib/data-access/store/sessionSlice.ts b/libs/shared/lib/data-access/store/sessionSlice.ts
index d3d94aa8b..e4c1a559c 100644
--- a/libs/shared/lib/data-access/store/sessionSlice.ts
+++ b/libs/shared/lib/data-access/store/sessionSlice.ts
@@ -1,7 +1,7 @@
+import { includes } from 'lodash-es';
 import { createSlice, PayloadAction } from '@reduxjs/toolkit';
 import type { RootState } from './store';
-import { BackendQueryFormat } from '../../querybuilder';
-import { DatabaseInfo } from '../api';
+import { DatabaseInfo, DatabaseStatus, SaveStateI, wsSelectState } from '../api/wsState';
 
 /** Message format of the error message from the backend */
 export type ErrorMessage = {
@@ -11,40 +11,52 @@ export type ErrorMessage = {
 
 /** Cache type */
 export type SessionCacheI = {
-  currentDatabase?: string;
-  databases: DatabaseInfo[] | undefined;
+  currentSaveState?: string; // id of the current save state
+  saveStates: Record<string, SaveStateI>;
 };
 
 // Define the initial state using that type
 export const initialState: SessionCacheI = {
-  currentDatabase: undefined,
-  databases: undefined,
+  currentSaveState: undefined,
+  saveStates: {},
 };
 
 export const sessionSlice = createSlice({
   name: 'session',
-  // `createSlice` will infer the state type from the `initialState` argument
   initialState,
   reducers: {
-    updateCurrentDatabase(state, action: PayloadAction<string|undefined>) {
-      state.currentDatabase = action.payload;
+    updateCurrentSaveState(state, action: PayloadAction<string | undefined>) {
+      state.currentSaveState = action.payload;
+      wsSelectState(state.currentSaveState);
     },
-    updateDatabaseList(state, action: PayloadAction<DatabaseInfo[]>) {
-      console.debug('Updating database list', action);
-      state.databases = action.payload;
-      if (state.databases.length > 0) {
-        const foundDatabase = state.databases.find((db) => db.Name === state.currentDatabase);
-        if (!foundDatabase) {
-          state.currentDatabase = state.databases[0].Name;
-        } else {
-          state.currentDatabase = foundDatabase?.Name || undefined;
-        }
+    updateSaveStateList(state, action: PayloadAction<SaveStateI[]>) {
+      state.saveStates = {};
+      action.payload.forEach((ss) => {
+        state.saveStates[ss.id] = ss;
+      });
+
+      if (!state.currentSaveState || !(state.currentSaveState in state.saveStates)) {
+        if (Object.keys(state.saveStates).length > 0) {
+          state.currentSaveState = Object.keys(state.saveStates)[0];
+        } else state.currentSaveState = undefined;
+      }
+      wsSelectState(state.currentSaveState);
+    },
+    addSaveState(state, action: PayloadAction<SaveStateI>) {
+      if (state.saveStates === undefined) state.saveStates = {};
+      state.saveStates[action.payload.id] = action.payload;
+      state.currentSaveState = action.payload.id;
+      wsSelectState(state.currentSaveState);
+    },
+    testedSaveState(state, action: PayloadAction<string>) {
+      if (action.payload in state.saveStates) {
+        state.saveStates[action.payload].db.status = DatabaseStatus.tested;
       }
     },
   },
 });
 
-export const { updateCurrentDatabase, updateDatabaseList } = sessionSlice.actions;
+export const { updateCurrentSaveState, updateSaveStateList, addSaveState, testedSaveState } = sessionSlice.actions;
 
 // Other code such as selectors can use the imported `RootState` type
 export const sessionCacheState = (state: RootState) => state.sessionCache;
diff --git a/libs/shared/lib/querybuilder/model/BackendQueryFormat.tsx b/libs/shared/lib/querybuilder/model/BackendQueryFormat.tsx
index de4c9ef31..7fc2ddbc1 100644
--- a/libs/shared/lib/querybuilder/model/BackendQueryFormat.tsx
+++ b/libs/shared/lib/querybuilder/model/BackendQueryFormat.tsx
@@ -10,7 +10,7 @@ import { MLTypes } from '../../data-access/store/mlSlice';
 
 /** JSON query format used to send a query to the backend. */
 export interface BackendQueryResultFormat {
-  databaseName: string;
+  saveStateID: string;
   return: {
     entities: number[];
     relations: number[];
@@ -27,7 +27,7 @@ export interface BackendQueryResultFormat {
 
 /** JSON query format used to send a query to the backend. */
 export interface BackendQueryFormat {
-  databaseName: string;
+  saveStateID: string;
   limit: number;
   return: string[];
   query: QueryStruct[];
@@ -38,6 +38,7 @@ export interface BackendQueryFormat {
   machineLearning: MachineLearning[];
   // modifiers: ModifierStruct[];
   // prefix: string;
+  cached: boolean | undefined;
 }
 
 /** Interface for an entity in the JSON for the query. */
diff --git a/libs/shared/lib/querybuilder/panel/querybuilder.tsx b/libs/shared/lib/querybuilder/panel/querybuilder.tsx
index a671ece20..e6d3b474e 100644
--- a/libs/shared/lib/querybuilder/panel/querybuilder.tsx
+++ b/libs/shared/lib/querybuilder/panel/querybuilder.tsx
@@ -1,6 +1,7 @@
 import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
 import {
   useConfig,
+  useQuerybuilder,
   useQuerybuilderGraph,
   useQuerybuilderSettings,
   useSchemaGraph,
@@ -60,7 +61,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
       relation: RelationPill,
       logic: LogicPill,
     }),
-    [],
+    []
   );
 
   var edgeTypes = useMemo(() => ({ connection: ConnectionLine, attribute_connection: ConnectionLine }), []);
@@ -186,7 +187,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
             name: dragData.name,
             schemaKey: dragData.name,
           },
-          schema.getNodeAttribute(dragData.name, 'attributes'),
+          schema.getNodeAttribute(dragData.name, 'attributes')
         );
 
         dispatch(setQuerybuilderGraphology(graphologyGraph));
@@ -203,7 +204,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
             schemaKey: dragData.label,
             collection: dragData.collection,
           },
-          schema.getEdgeAttribute(dragData.label, 'attributes'),
+          schema.getEdgeAttribute(dragData.label, 'attributes')
         );
 
         if (config.autoSendQueries) {
@@ -256,7 +257,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
         }
       }
     },
-    [graph],
+    [graph]
   );
 
   const onConnectStart = useCallback(
@@ -274,7 +275,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
         attribute: { handleData: handleData },
       };
     },
-    [graph],
+    [graph]
   );
 
   const onConnectEnd = useCallback(
@@ -314,7 +315,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
         // setToggleSettings('logic');
       }
     },
-    [reactFlow.project],
+    [reactFlow.project]
   );
 
   const onEdgeUpdateStart = useCallback(() => {
@@ -339,7 +340,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
         dispatch(setQuerybuilderGraphology(graphologyGraph));
       }
     },
-    [graph],
+    [graph]
   );
 
   const onEdgesChange = (params: OnEdgesChange) => {
@@ -356,7 +357,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
       }
       isEdgeUpdating.current = false;
     },
-    [graph],
+    [graph]
   );
 
   const onNodeContextMenu = (event: React.MouseEvent, node: Node) => {
diff --git a/libs/shared/lib/querybuilder/panel/shemaquerybuilder.stories.tsx b/libs/shared/lib/querybuilder/panel/shemaquerybuilder.stories.tsx
index 3f6ebb25b..bb9dc3aac 100644
--- a/libs/shared/lib/querybuilder/panel/shemaquerybuilder.stories.tsx
+++ b/libs/shared/lib/querybuilder/panel/shemaquerybuilder.stories.tsx
@@ -1,7 +1,7 @@
 import React from 'react';
 import { Meta } from '@storybook/react';
 import { Provider } from 'react-redux';
-import { setQuerybuilderNodes, setSchema, store } from '@graphpolaris/shared/lib/data-access/store';
+import { setQuerybuilderGraph, setSchema, store } from '@graphpolaris/shared/lib/data-access/store';
 
 import { SchemaUtils } from '@graphpolaris/shared/lib/schema/schema-utils';
 import { Schema } from '@graphpolaris/shared/lib/schema/panel';
@@ -56,7 +56,7 @@ export const SchemaAndQueryBuilderInteractivity = {
     const schema = SchemaUtils.schemaBackend2Graphology(movieSchemaRaw);
 
     const graph = new QueryMultiGraphology();
-    dispatch(setQuerybuilderNodes(graph.export()));
+    dispatch(setQuerybuilderGraph(graph.export()));
     dispatch(setSchema(schema.export()));
   },
 };
diff --git a/libs/shared/lib/querybuilder/panel/stories/querybuilder-simple.stories.tsx b/libs/shared/lib/querybuilder/panel/stories/querybuilder-simple.stories.tsx
index 8f40b7593..cd0530b4e 100644
--- a/libs/shared/lib/querybuilder/panel/stories/querybuilder-simple.stories.tsx
+++ b/libs/shared/lib/querybuilder/panel/stories/querybuilder-simple.stories.tsx
@@ -1,5 +1,5 @@
 import React from 'react';
-import { querybuilderSlice, setQuerybuilderNodes, setSchema, store } from '@graphpolaris/shared/lib/data-access/store';
+import { querybuilderSlice, setQuerybuilderGraph, setSchema, store } from '@graphpolaris/shared/lib/data-access/store';
 
 import { configureStore } from '@reduxjs/toolkit';
 import { Meta } from '@storybook/react';
@@ -149,7 +149,7 @@ export const Simple = {
     //   type: 'entity_relation',
     //   sourceHandle: handles.relation.entity,
     // });
-    store.dispatch(setQuerybuilderNodes(graph.export()));
+    store.dispatch(setQuerybuilderGraph(graph.export()));
   },
 };
 
diff --git a/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-entity.stories.tsx b/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-entity.stories.tsx
index 376d05b1f..9eb58c7ba 100644
--- a/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-entity.stories.tsx
+++ b/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-entity.stories.tsx
@@ -1,5 +1,5 @@
 import React from 'react';
-import { querybuilderSlice, setQuerybuilderNodes, setSchema, store } from '@graphpolaris/shared/lib/data-access/store';
+import { querybuilderSlice, setQuerybuilderGraph, setSchema, store } from '@graphpolaris/shared/lib/data-access/store';
 
 import { configureStore } from '@reduxjs/toolkit';
 import { Meta } from '@storybook/react';
@@ -36,7 +36,7 @@ export const SingleEntity = {
       y: 100,
       name: 'Entity Pill',
     });
-    store.dispatch(setQuerybuilderNodes(graph.export()));
+    store.dispatch(setQuerybuilderGraph(graph.export()));
   },
 };
 
diff --git a/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-relationship.stories.tsx b/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-relationship.stories.tsx
index e3b9f4cc3..fe73dc689 100644
--- a/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-relationship.stories.tsx
+++ b/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-relationship.stories.tsx
@@ -1,5 +1,5 @@
 import React from 'react';
-import { querybuilderSlice, setQuerybuilderNodes, setSchema, store } from '@graphpolaris/shared/lib/data-access/store';
+import { querybuilderSlice, setQuerybuilderGraph, setSchema, store } from '@graphpolaris/shared/lib/data-access/store';
 
 import { configureStore } from '@reduxjs/toolkit';
 import { Meta } from '@storybook/react';
@@ -38,7 +38,7 @@ export const SingleRelationship = {
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
     });
-    store.dispatch(setQuerybuilderNodes(graph.export()));
+    store.dispatch(setQuerybuilderGraph(graph.export()));
   },
 };
 
diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill-full.stories.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill-full.stories.tsx
index 139c807ac..9e5750185 100644
--- a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill-full.stories.tsx
+++ b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill-full.stories.tsx
@@ -1,10 +1,5 @@
 import React from 'react';
-import {
-  querybuilderSlice,
-  schemaSlice,
-  setQuerybuilderNodes,
-  searchResultSlice,
-} from '@graphpolaris/shared/lib/data-access/store';
+import { querybuilderSlice, schemaSlice, setQuerybuilderGraph, searchResultSlice } from '@graphpolaris/shared/lib/data-access/store';
 
 import { configureStore } from '@reduxjs/toolkit';
 import { Meta } from '@storybook/react';
@@ -34,7 +29,7 @@ export const Flow = {
 
     const graph = new QueryMultiGraphology();
     graph.addPill2Graphology({ id: '2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Entity Pill' });
-    dispatch(setQuerybuilderNodes(graph.export()));
+    dispatch(setQuerybuilderGraph(graph.export()));
   },
   args: {},
 };
diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-full_reactflow.stories.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-full_reactflow.stories.tsx
index b0a877a18..5da8f51bc 100644
--- a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-full_reactflow.stories.tsx
+++ b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-full_reactflow.stories.tsx
@@ -1,5 +1,5 @@
 import React from 'react';
-import { querybuilderSlice, schemaSlice, setQuerybuilderNodes, searchResultSlice } from '@graphpolaris/shared/lib/data-access/store';
+import { querybuilderSlice, schemaSlice, setQuerybuilderGraph, searchResultSlice } from '@graphpolaris/shared/lib/data-access/store';
 
 import { configureStore } from '@reduxjs/toolkit';
 import { Meta } from '@storybook/react';
@@ -32,7 +32,7 @@ graph.addPill2Graphology({
 });
 console.log(graph.export());
 
-mockStore.dispatch(setQuerybuilderNodes(graph.export()));
+mockStore.dispatch(setQuerybuilderGraph(graph.export()));
 
 export const Flow = {
   args: {},
diff --git a/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts b/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts
index 085dcba4e..b57c1eaa9 100644
--- a/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts
+++ b/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts
@@ -9,7 +9,8 @@ import { MathAggregations } from '../model/logic/numberAggregations';
 import { QueryBuilderSettings } from '../../data-access/store/querybuilderSlice';
 
 const defaultQuery = {
-  databaseName: 'database',
+  saveStateID: 'database',
+  cached: false,
   query: [],
   limit: 500,
   return: ['*'],
diff --git a/libs/shared/lib/querybuilder/query-utils/query2backend.ts b/libs/shared/lib/querybuilder/query-utils/query2backend.ts
index 57b65c31d..9246a7286 100644
--- a/libs/shared/lib/querybuilder/query-utils/query2backend.ts
+++ b/libs/shared/lib/querybuilder/query-utils/query2backend.ts
@@ -157,17 +157,18 @@ function queryLogicUnion(graphLogicChunks: AllLogicStatement[]): AllLogicStateme
  * @returns {BackendQueryFormat} A JSON object in the `JSONFormat`.
  */
 export function Query2BackendQuery(
-  databaseName: string,
+  saveStateID: string,
   graph: QueryMultiGraph,
   settings: QueryBuilderSettings,
   ml: ML = mlDefaultState
 ): BackendQueryFormat {
   let query: BackendQueryFormat = {
-    databaseName: databaseName,
+    saveStateID: saveStateID,
     query: [],
     machineLearning: [],
     limit: settings.limit,
     return: ['*'], // TODO
+    cached: false,
   };
 
   Object.keys(ml).forEach((mlType) => {
@@ -200,7 +201,7 @@ export function Query2BackendQuery(
       });
     });
 
-    return Query2BackendQuery(databaseName, graphologyQuery.export(), settings, ml);
+    return Query2BackendQuery(saveStateID, graphologyQuery.export(), settings, ml);
   }
   // Chunk extraction: traverse graph to find all paths of logic between relations and entities
   let graphSequenceChunks: QueryGraphNodes[][] = [];
@@ -286,7 +287,7 @@ export function Query2BackendQuery(
     return ret;
   });
 
-  console.debug('New processed query', graph, query);
+  console.debug('%cNew processed query', 'color: aquamarine', graph, query);
 
   return query;
 }
diff --git a/libs/shared/lib/schema/panel/schema.tsx b/libs/shared/lib/schema/panel/schema.tsx
index fa6442236..73b9a8fd7 100644
--- a/libs/shared/lib/schema/panel/schema.tsx
+++ b/libs/shared/lib/schema/panel/schema.tsx
@@ -13,11 +13,12 @@ import { EntityNode } from '../pills/nodes/entity/entity-node';
 import { RelationNode } from '../pills/nodes/relation/relation-node';
 import NodeEdge from '../pills/edges/node-edge';
 import SelfEdge from '../pills/edges/self-edge';
-import { useSchemaAPI } from '../../data-access';
 import { SchemaDialog } from './schemaDialog';
+import { wsSchemaRequest } from '@graphpolaris/shared/lib/data-access/api/wsSchema';
 import { toSchemaGraphology } from '../../data-access/store/schemaSlice';
 import { Button } from '../../components/buttons';
 import ControlContainer from '../../components/controls';
+import { wsGetStates } from '../../data-access';
 
 interface Props {
   content?: string;
@@ -42,7 +43,6 @@ const edgeTypes = {
 };
 
 export const Schema = (props: Props) => {
-  const api_schema = useSchemaAPI();
   const session = useSessionCache();
   const settings = useSchemaSettings();
   const searchResults = useSearchResultSchema();
@@ -99,7 +99,7 @@ export const Schema = (props: Props) => {
       nds.map((node) => ({
         ...node,
         selected: searchResults.includes(node.id) || searchResults.includes(node.data.label),
-      })),
+      }))
     );
   }, [searchResults]);
 
@@ -117,7 +117,8 @@ export const Schema = (props: Props) => {
             iconName="Cached"
             onClick={(e) => {
               e.stopPropagation();
-              api_schema.RequestSchema(session.currentDatabase);
+              if (session.currentSaveState) wsSchemaRequest(session.currentSaveState);
+              else wsGetStates();
             }}
           />
           <Button
diff --git a/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx b/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx
index c5518de96..6520c642e 100644
--- a/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx
+++ b/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx
@@ -30,8 +30,6 @@ export const RelationNode = React.memo(({ id, selected, data }: NodeProps<Schema
    * @param event React Mouse drag event.
    */
   const onDragStart = (event: React.DragEvent<HTMLDivElement>) => {
-    console.log(data);
-
     const eventData: SchemaEdge = {
       type: QueryElementTypes.Relation,
       name: id, //TODO id?
diff --git a/libs/shared/lib/vis/index.tsx b/libs/shared/lib/vis/index.tsx
index 27152704b..3abc7b697 100644
--- a/libs/shared/lib/vis/index.tsx
+++ b/libs/shared/lib/vis/index.tsx
@@ -37,8 +37,6 @@ export const createVisualizationComponent = () => {
 
     const VisualizationComponent: VISComponentType = Visualizations[vis.activeVisualization];
 
-    console.log('createVisualizationComponent', VisualizationComponent, vis);
-
     React.useEffect(() => {
       dispatch(addVisualization({ id: VisualizationComponent.displayName, settings: VisualizationComponent.localConfigSchema }));
     }, [vis.activeVisualization]);
diff --git a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.tsx b/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.tsx
index 8bb1eff66..67afb9d1f 100644
--- a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.tsx
+++ b/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.tsx
@@ -39,7 +39,7 @@ export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationProp
 
   useEffect(() => {
     if (data) {
-      console.debug('graphQueryResult', data);
+      console.debug('%cResult from graphQuery', 'color: aquamarine', data);
 
       setGraph(
         parseQueryResult(data, ml, {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 176c04faf..c584b0f6f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -110,10 +110,13 @@ importers:
         version: 6.9.0(react-dom@18.2.0)(react@18.2.0)
       reactflow:
         specifier: 11.4.0-next.1
-        version: 11.4.0-next.1(react-dom@18.2.0)(react@18.2.0)
+        version: 11.4.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0)
       styled-components:
         specifier: ^5.3.6
         version: 5.3.9(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)
+      use-immer:
+        specifier: ^0.9.0
+        version: 0.9.0(immer@10.0.2)(react@18.2.0)
     devDependencies:
       '@import-meta-env/cli':
         specifier: ^0.6.5
@@ -5656,14 +5659,14 @@ packages:
       '@babel/runtime': 7.21.0
     dev: true
 
-  /@reactflow/background@11.1.0-next.1(react-dom@18.2.0)(react@18.2.0):
+  /@reactflow/background@11.1.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-OXCWt3rKz7/pctEqL2e82ziIJwfxGO9McC2a/JGso75rhCu+b7dWejhESNRS+9rgu1PdQpjDvB/wgQKIQqGoWA==}
     peerDependencies:
       react: '>=17'
       react-dom: '>=17'
     dependencies:
       '@babel/runtime': 7.21.0
-      '@reactflow/core': 11.4.0-next.1(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/core': 11.4.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0)
       classcat: 5.0.4
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -5687,14 +5690,14 @@ packages:
       - immer
     dev: false
 
-  /@reactflow/controls@11.1.0-next.1(react-dom@18.2.0)(react@18.2.0):
+  /@reactflow/controls@11.1.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-jqvwxI9VFc4ZPBfZE98MigwE+UJbxLHBC47y0pt1bGqnxzK1XcAMocDVcvlTMbHWbDmouFmpk+cUoYSkQx5/cQ==}
     peerDependencies:
       react: '>=17'
       react-dom: '>=17'
     dependencies:
       '@babel/runtime': 7.21.0
-      '@reactflow/core': 11.4.0-next.1(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/core': 11.4.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0)
       classcat: 5.0.4
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -5716,7 +5719,7 @@ packages:
       - immer
     dev: false
 
-  /@reactflow/core@11.4.0-next.1(react-dom@18.2.0)(react@18.2.0):
+  /@reactflow/core@11.4.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-OgHMl9qs7ZMidoc+pUcZ4O1TxszrpW0jcb2tZQOfB5WpJL40HmwXrGYZdk9IhG1ANo4N0nwS5MBvho2Ddo7aSw==}
     peerDependencies:
       react: '>=17'
@@ -5779,14 +5782,14 @@ packages:
       - immer
     dev: false
 
-  /@reactflow/minimap@11.3.0-next.1(react-dom@18.2.0)(react@18.2.0):
+  /@reactflow/minimap@11.3.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-ZNo6oLTKSLHO/rdfribRO5pmBMWT8Y5Hbn5zKkzJgjPKmaa7sG6ZjuOJNBz4LuKy7GrP5Uu2wGSc8svxNvYogA==}
     peerDependencies:
       react: '>=17'
       react-dom: '>=17'
     dependencies:
       '@babel/runtime': 7.21.0
-      '@reactflow/core': 11.4.0-next.1(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/core': 11.4.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0)
       '@types/d3-selection': 3.0.5
       '@types/d3-zoom': 3.0.2
       classcat: 5.0.4
@@ -5835,14 +5838,14 @@ packages:
       - immer
     dev: false
 
-  /@reactflow/node-toolbar@1.1.0-next.1(react-dom@18.2.0)(react@18.2.0):
+  /@reactflow/node-toolbar@1.1.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-eNMS5nr9ehGnzIWqJohVx3uAGrCGxmcW7SonRyolIO6f3DqxNeg4jt6r+uNLkc6ZfndLgPZa34LsQA4EEU/aEg==}
     peerDependencies:
       react: '>=17'
       react-dom: '>=17'
     dependencies:
       '@babel/runtime': 7.21.0
-      '@reactflow/core': 11.4.0-next.1(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/core': 11.4.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0)
       classcat: 5.0.4
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -17584,17 +17587,17 @@ packages:
       react: 18.2.0
     dev: false
 
-  /reactflow@11.4.0-next.1(react-dom@18.2.0)(react@18.2.0):
+  /reactflow@11.4.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-csAycDAeNSq1saVPhjNoNpell9VfLeXId0ojgl/GqASq2YLVDfBr9xVM34obB8l+k4zs2M4zpj1wZsBDqSdzBA==}
     peerDependencies:
       react: '>=17'
       react-dom: '>=17'
     dependencies:
-      '@reactflow/background': 11.1.0-next.1(react-dom@18.2.0)(react@18.2.0)
-      '@reactflow/controls': 11.1.0-next.1(react-dom@18.2.0)(react@18.2.0)
-      '@reactflow/core': 11.4.0-next.1(react-dom@18.2.0)(react@18.2.0)
-      '@reactflow/minimap': 11.3.0-next.1(react-dom@18.2.0)(react@18.2.0)
-      '@reactflow/node-toolbar': 1.1.0-next.1(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/background': 11.1.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/controls': 11.1.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/core': 11.4.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/minimap': 11.3.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/node-toolbar': 1.1.0-next.1(immer@10.0.2)(react-dom@18.2.0)(react@18.2.0)
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
     transitivePeerDependencies:
@@ -20302,7 +20305,7 @@ packages:
     dependencies:
       '@types/node': 17.0.12
       esbuild: 0.17.12
-      postcss: 8.4.21
+      postcss: 8.4.31
       resolve: 1.22.1
       rollup: 3.20.0
       sass: 1.64.2
-- 
GitLab