feat: Add Apollo Client integration to web-dashboard with GraphQL support

- Add @apollo/client ^4.1.9 and graphql ^16.10.0 dependencies to package.json
- Add apollo-client.ts with ApolloClient configuration, HTTP link to API server, and auth link for token-based authentication
- Add Providers component wrapping children with ApolloProvider
- Wrap Sidebar and main content with Providers in root layout
- Configure cache-and-network and network-only fetch policies for queries and mutations
This commit is contained in:
nutcas3
2026-04-25 03:36:26 +03:00
parent 201ecdf62f
commit a8c6f9393f
5 changed files with 178 additions and 2 deletions
+2
View File
@@ -12,8 +12,10 @@
"test:coverage": "jest --coverage"
},
"dependencies": {
"@apollo/client": "^4.1.9",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"graphql": "^16.10.0",
"lucide-react": "^1.7.0",
"next": "16.2.2",
"radix-ui": "^1.4.3",
+109
View File
@@ -8,12 +8,18 @@ importers:
.:
dependencies:
'@apollo/client':
specifier: ^4.1.9
version: 4.1.9([email protected])([email protected]([email protected]))([email protected])([email protected])
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
clsx:
specifier: ^2.1.1
version: 2.1.1
graphql:
specifier: ^16.10.0
version: 16.13.2
lucide-react:
specifier: ^1.7.0
version: 1.7.0([email protected])
@@ -91,6 +97,25 @@ packages:
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
'@apollo/[email protected]':
resolution: {integrity: sha512-qfpkQD51tdU/7iAR6aLb4w9o/L7I475DluWHRb61U/3Q0AH29nNOxOBHjBbWDdf16ncPOoQuxne1sEs2NjqBFw==}
peerDependencies:
graphql: ^16.0.0
graphql-ws: ^5.5.5 || ^6.0.3
react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc
react-dom: ^17.0.0 || ^18.0.0 || >=19.0.0-rc
rxjs: ^7.3.0
subscriptions-transport-ws: ^0.9.0 || ^0.11.0
peerDependenciesMeta:
graphql-ws:
optional: true
react:
optional: true
react-dom:
optional: true
subscriptions-transport-ws:
optional: true
'@babel/[email protected]':
resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
engines: {node: '>=6.9.0'}
@@ -378,6 +403,11 @@ packages:
'@floating-ui/[email protected]':
resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==}
'@graphql-typed-document-node/[email protected]':
resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==}
peerDependencies:
graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@hono/[email protected]':
resolution: {integrity: sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==}
engines: {node: '>=18.14.1'}
@@ -1831,6 +1861,22 @@ packages:
cpu: [x64]
os: [win32]
'@wry/[email protected]':
resolution: {integrity: sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==}
engines: {node: '>=8'}
'@wry/[email protected]':
resolution: {integrity: sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==}
engines: {node: '>=8'}
'@wry/[email protected]':
resolution: {integrity: sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==}
engines: {node: '>=8'}
'@wry/[email protected]':
resolution: {integrity: sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
deprecated: Use your platform's native atob() and btoa() methods instead
@@ -2807,6 +2853,12 @@ packages:
[email protected]:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
[email protected]:
resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==}
engines: {node: '>=10'}
peerDependencies:
graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
[email protected]:
resolution: {integrity: sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==}
engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
@@ -3702,6 +3754,9 @@ packages:
resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==}
engines: {node: '>=20'}
[email protected]:
resolution: {integrity: sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==}
[email protected]:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
@@ -4021,6 +4076,9 @@ packages:
[email protected]:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
[email protected]:
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
[email protected]:
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
engines: {node: '>=0.4'}
@@ -4608,6 +4666,21 @@ snapshots:
'@alloc/[email protected]': {}
'@apollo/[email protected]([email protected])([email protected]([email protected]))([email protected])([email protected])':
dependencies:
'@graphql-typed-document-node/core': 3.2.0([email protected])
'@wry/caches': 1.0.1
'@wry/equality': 0.5.7
'@wry/trie': 0.5.0
graphql: 16.13.2
graphql-tag: 2.12.6([email protected])
optimism: 0.18.1
rxjs: 7.8.2
tslib: 2.8.1
optionalDependencies:
react: 19.2.4
react-dom: 19.2.4([email protected])
'@babel/[email protected]':
dependencies:
'@babel/helper-validator-identifier': 7.28.5
@@ -4968,6 +5041,10 @@ snapshots:
'@floating-ui/[email protected]': {}
'@graphql-typed-document-node/[email protected]([email protected])':
dependencies:
graphql: 16.13.2
'@hono/[email protected]([email protected])':
dependencies:
hono: 4.12.10
@@ -6515,6 +6592,22 @@ snapshots:
'@unrs/[email protected]':
optional: true
'@wry/[email protected]':
dependencies:
tslib: 2.8.1
'@wry/[email protected]':
dependencies:
tslib: 2.8.1
'@wry/[email protected]':
dependencies:
tslib: 2.8.1
'@wry/[email protected]':
dependencies:
tslib: 2.8.1
[email protected]: {}
[email protected]:
@@ -7714,6 +7807,11 @@ snapshots:
[email protected]: {}
[email protected]([email protected]):
dependencies:
graphql: 16.13.2
tslib: 2.8.1
[email protected]: {}
[email protected]: {}
@@ -8771,6 +8869,13 @@ snapshots:
powershell-utils: 0.1.0
wsl-utils: 0.3.1
[email protected]:
dependencies:
'@wry/caches': 1.0.1
'@wry/context': 0.7.4
'@wry/trie': 0.5.0
tslib: 2.8.1
[email protected]:
dependencies:
deep-is: 0.1.4
@@ -9142,6 +9247,10 @@ snapshots:
dependencies:
queue-microtask: 1.2.3
[email protected]:
dependencies:
tslib: 2.8.1
[email protected]:
dependencies:
call-bind: 1.0.8
+5 -2
View File
@@ -3,6 +3,7 @@ import { Geist, Geist_Mono, Inter } from "next/font/google";
import "./globals.css";
import { cn } from "@/lib/utils";
import { Sidebar } from "@/components/sidebar";
import { Providers } from "./providers";
const inter = Inter({ subsets: ["latin"], variable: "--font-sans" });
@@ -32,8 +33,10 @@ export default function RootLayout({
className={cn("h-full", "antialiased", geistSans.variable, geistMono.variable, "font-sans", inter.variable)}
>
<body className="min-h-full flex">
<Sidebar />
<main className="flex-1 overflow-auto">{children}</main>
<Providers>
<Sidebar />
<main className="flex-1 overflow-auto">{children}</main>
</Providers>
</body>
</html>
);
+8
View File
@@ -0,0 +1,8 @@
'use client';
import { ApolloProvider } from '@apollo/client';
import { apolloClient } from '@/lib/apollo-client';
export function Providers({ children }: { children: React.ReactNode }) {
return <ApolloProvider client={apolloClient}>{children}</ApolloProvider>;
}
@@ -0,0 +1,54 @@
import { ApolloClient, InMemoryCache, createHttpLink, from } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
// GraphQL API endpoint
const httpLink = createHttpLink({
uri: process.env.NEXT_PUBLIC_API_SERVER_URL || 'http://localhost:8080/v1/graphql',
});
// Auth link to add authentication token to requests
const authLink = setContext((_, { headers }) => {
// Get the authentication token from local storage if it exists
const token = localStorage.getItem('auth_token');
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});
// Create Apollo Client instance
export const apolloClient = new ApolloClient({
link: from([authLink, httpLink]),
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: {
fetchPolicy: 'cache-and-network',
},
query: {
fetchPolicy: 'network-only',
},
mutate: {
fetchPolicy: 'network-only',
},
},
});
// GraphQL queries and mutations can be defined here
// Example:
/*
import { gql } from '@apollo/client';
export const GET_SERVICES = gql`
query GetServices {
services {
id
name
status
health
}
}
`;
*/