Data Access Layer #13

Merged
guus merged 4 commits from add-data-layer into master 2025-07-28 20:11:09 +02:00
4 changed files with 107 additions and 9 deletions
Showing only changes of commit ef0f82d516 - Show all commits
+11
View File
@@ -8,6 +8,7 @@
"@next/font": "^14.2.15", "@next/font": "^14.2.15",
"next": "^15.3.3", "next": "^15.3.3",
"oidc-client-ts": "^3.3.0", "oidc-client-ts": "^3.3.0",
"openapi-typescript-fetch": "^2.2.1",
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-oidc-context": "^3.3.0" "react-oidc-context": "^3.3.0"
@@ -1532,6 +1533,16 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/openapi-typescript-fetch": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/openapi-typescript-fetch/-/openapi-typescript-fetch-2.2.1.tgz",
"integrity": "sha512-aBp1cR5FTNxp4HA8bb2ST53aIqEiJgoOMyXiyzKi6YF7vogW8KkyyUQ1FeDz8D05uspxFrKvFbkVU2YiiKkULA==",
"license": "MIT",
"engines": {
"node": ">= 12.0.0",
"npm": ">= 7.0.0"
}
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+1
View File
@@ -3,6 +3,7 @@
"@next/font": "^14.2.15", "@next/font": "^14.2.15",
"next": "^15.3.3", "next": "^15.3.3",
"oidc-client-ts": "^3.3.0", "oidc-client-ts": "^3.3.0",
"openapi-typescript-fetch": "^2.2.1",
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-oidc-context": "^3.3.0" "react-oidc-context": "^3.3.0"
+4 -9
View File
@@ -1,20 +1,15 @@
"use client"; "use client";
import { getUser } from "muzak/data/fetch"; import { getUser } from "muzak/data/fetch";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import api from "muzak/data/fetcher";
export default function Page() { export default function Page() {
const [userData, setUserData] = useState<User[]>([]); const [userData, setUserData] = useState([]);
useEffect(() => { useEffect(() => {
async function getUsers() { async function getUsers() {
const user = getUser(); const { data: users } = await api.users.list({});
const access_token = user?.access_token; setUserData(users);
const headers = new Headers();
headers.set("Authorization", `Bearer ${access_token}`);
headers.set("Content-Type", "application/json; charset=utf-8");
const users = await fetch("http://127.0.0.1:8001/api/user/", { headers });
const userData = await users.json();
setUserData(userData);
} }
getUsers(); getUsers();
}, []); }, []);
+91
View File
@@ -0,0 +1,91 @@
"use client";
import { Fetcher } from "openapi-typescript-fetch";
import { getUser } from "./fetch";
interface GenericPaths {
[path: string]: {
[method: string]: any;
};
}
const fetcher = Fetcher.for<GenericPaths>();
const BASE_URL = process.env.BACKEND_HOST || "http://127.0.0.1:8001";
// Configure the fetcher
fetcher.configure({
baseUrl: BASE_URL,
init: {
headers: {
"Content-Type": "application/json",
},
},
use: [
// Authentication middleware
async (url, init, next) => {
const user = getUser();
if (user?.access_token) {
init.headers = {
...init.headers,
Authorization: `Bearer ${user.access_token}`,
};
}
return next(url, init);
},
// Error handling middleware
async (url, init, next) => {
const response = await next(url, init);
if (!response.ok) {
// Handle authentication errors
if (response.status === 401) {
console.warn("Authentication failed - token may be expired");
// You might want to trigger a redirect to login or token refresh here
}
// Log other errors
console.error(`API Error: ${response.status} ${response.statusText}`, {
url,
method: init.method,
});
}
return response;
},
],
});
// Create your API methods
export const api = {
tracks: {
list: fetcher.path("/api/track/").method("get").create(),
detail: fetcher.path("/api/track/{id}").method("get").create(),
},
artists: {
list: fetcher.path("/api/artist").method("get").create(),
detail: fetcher.path("/api/artist/{id}").method("get").create(),
},
albums: {
list: fetcher.path("/api/album/").method("get").create(),
detail: fetcher.path("/api/album/{id}").method("get").create(),
},
votes: {
list: fetcher.path("/api/vote/").method("get").create(),
detail: fetcher.path("/api/vote/{id}").method("get").create(),
userVotes: fetcher.path("/api/user/{user_id}/votes").method("get").create(),
},
users: {
list: fetcher.path("/api/user/").method("get").create(),
detail: fetcher.path("/api/user/{id}").method("get").create(),
},
profiles: {
detail: fetcher.path("/api/profile/{id}").method("get").create(),
backgrounds: {
list: fetcher.path("/api/profile/background").method("get").create(),
detail: fetcher
.path("/api/profile/background/{id}")
.method("get")
.create(),
},
},
};
export default api;