diff --git a/Dockerfile b/Dockerfile
index e80dad2870d3f917b38aa6b4ea655377bf808738..149d670b74f052834d8d6604edbbdb9c4e94930c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,7 +7,7 @@ COPY . .
 RUN mkdir public/js
 RUN mkdir public/css
 RUN yarn install --network-timeout 1000000 
-RUN yarn compile
+RUN yarn build
 
 # |--------------------------------------------------------------------------
 # | Install PHP dependencies
diff --git a/composer.json b/composer.json
index a82125110c168e8c1620f5a7ec212eaef32e3461..1287a32669d41e4683cec684bc4e5fe9f54c3009 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,8 @@
 {
     "require": {
         "aws/aws-sdk-php": "^3.181",
-        "guzzlehttp/guzzle": "^7.0"
+        "guzzlehttp/guzzle": "^7.0",
+        "webonyx/graphql-php": "^14.11"
     },
     "config": {
         "vendor-dir": "public/API/vendor"
diff --git a/composer.lock b/composer.lock
index 6cc263e80a97fab7d510f732f44bdc5bd327f6f1..cea0c4628a65177f7e2af0acb5dba364620385ef 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "f47a9b8d286ca72493a7fe02f263976e",
+    "content-hash": "31a3a0321659f9c8afff63a68a9fafb6",
     "packages": [
         {
             "name": "aws/aws-crt-php",
@@ -669,6 +669,72 @@
                 }
             ],
             "time": "2021-05-27T12:26:48+00:00"
+        },
+        {
+            "name": "webonyx/graphql-php",
+            "version": "v14.11.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/webonyx/graphql-php.git",
+                "reference": "ffa431c0821821839370a68dab3c2597c06bf7f0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/ffa431c0821821839370a68dab3c2597c06bf7f0",
+                "reference": "ffa431c0821821839370a68dab3c2597c06bf7f0",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-mbstring": "*",
+                "php": "^7.1 || ^8"
+            },
+            "require-dev": {
+                "amphp/amp": "^2.3",
+                "doctrine/coding-standard": "^6.0",
+                "nyholm/psr7": "^1.2",
+                "phpbench/phpbench": "^1.2",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "0.12.82",
+                "phpstan/phpstan-phpunit": "0.12.18",
+                "phpstan/phpstan-strict-rules": "0.12.9",
+                "phpunit/phpunit": "^7.2 || ^8.5",
+                "psr/http-message": "^1.0",
+                "react/promise": "2.*",
+                "simpod/php-coveralls-mirror": "^3.0",
+                "squizlabs/php_codesniffer": "3.5.4"
+            },
+            "suggest": {
+                "psr/http-message": "To use standard GraphQL server",
+                "react/promise": "To leverage async resolving on React PHP platform"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "GraphQL\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A PHP port of GraphQL reference implementation",
+            "homepage": "https://github.com/webonyx/graphql-php",
+            "keywords": [
+                "api",
+                "graphql"
+            ],
+            "support": {
+                "issues": "https://github.com/webonyx/graphql-php/issues",
+                "source": "https://github.com/webonyx/graphql-php/tree/v14.11.5"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/webonyx-graphql-php",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2022-01-24T11:13:31+00:00"
         }
     ],
     "packages-dev": [],
@@ -679,5 +745,5 @@
     "prefer-lowest": false,
     "platform": [],
     "platform-dev": [],
-    "plugin-api-version": "2.1.0"
+    "plugin-api-version": "2.0.0"
 }
diff --git a/js/customElements/blogFooter.js b/js/customElements/blogFooter.js
index 526894c49adc635fba4004b83412b78faaf0de59..5be26eac8fc96f8965e90c596b06ba61cae492aa 100644
--- a/js/customElements/blogFooter.js
+++ b/js/customElements/blogFooter.js
@@ -1,31 +1,31 @@
 class blogFooter extends HTMLElement {
     constructor(){
         super();
-        let xhr = new XMLHttpRequest();
-        let ul = document.createElement("ul");
-        xhr.onreadystatechange = () => {
-            if(xhr.readyState === 4) {
-                if (xhr.status === 200) {
-                    let blog = JSON.parse(xhr.responseText);
-                    blog.forEach((element) => {
-                        let li = document.createElement("li");
-                        let a = document.createElement("a");
-                        a.href = "/post.html?id=" + element["id"];
-                        a.innerText = element["title"];
-                        li.appendChild(a);
-                        ul.appendChild(li);
-                    });
-                    this.appendChild(ul);
-                } else {
-                    let p = document.createElement("p");
-                    p.innerText = "Leider konnte dieser Inhalt nicht geladen werden, bitte versuche die Seite neu zu laden oder komme später wieder zurück";
-                    this.appendChild(p);
+        this.getBlogEntries();
+    }
 
-                }
-            }
+    async getBlogEntries() {
+        let ul = document.createElement("ul");
+        this.appendChild(ul);
+        var graphql = JSON.stringify({
+        query: 'query($count: Int!) { blogPosts(count: $count) { title id }}',
+        variables: {
+            "count": 5
         }
-        xhr.open("GET", "/API/getBlogElements.php?position=footer");
-        xhr.send();
+        })
+        var requestOptions = {
+        method: 'POST',
+        body: graphql,
+        };
+        let posts = (await (await fetch("http://localhost:1234/API/graphql.php", requestOptions)).json()).data.blogPosts;
+        posts.forEach((element) => {
+            let li = document.createElement("li");
+            let a = document.createElement("a");
+            a.href = "/post.html?id=" + element["id"];
+            a.innerText = element["title"];
+            li.appendChild(a);
+            ul.appendChild(li);
+        });
     }
 }
 
diff --git a/js/customElements/blogIndex.js b/js/customElements/blogIndex.js
index b356df7970ef4d14c85ed600928ae8cdac868df2..b3091b00495caed466437c481f72e87a4938e360 100644
--- a/js/customElements/blogIndex.js
+++ b/js/customElements/blogIndex.js
@@ -1,48 +1,48 @@
 class BlogIndex extends HTMLElement {
     constructor() {
         super();
-        let xhr = new XMLHttpRequest();
-        xhr.onreadystatechange = () => {
-            if (xhr.readyState === 4) {
-                if (xhr.status === 200) {
-                    let blog = JSON.parse(xhr.responseText);
-                    blog.forEach((element) => {
-                        const article = document.createElement("article");
-                        article.classList.add("breakWord");
-                        this.appendChild(article);
-
-                        const h2 = document.createElement("h2");
-                        h2.innerText = element["title"];
-                        article.appendChild(h2);
-
-                        const content = document.createElement("p");
-                        content.classList.add("breakWord");
-                        content.innerHTML = element["content"];
-                        article.appendChild(content);
-
-                        const moreP = document.createElement("p");
-                        moreP.classList.add("center");
-                        article.appendChild(moreP);
-
-                        const moreLink = document.createElement("a");
-                        moreLink.href = "/post.html?id=" + element["id"];
-                        moreP.appendChild(moreLink);
-
-                        const moreButton = document.createElement("button");
-                        moreButton.innerText = "Mehr lesen";
-                        moreLink.appendChild(moreButton);
-                    });
-
-                } else {
-                    let p = document.createElement("p");
-                    p.innerText = "Leider konnte dieser Inhalt nicht geladen werden, bitte versuche die Seite neu zu laden oder komme später wieder zurück";
-                    this.appendChild(p);
+        this.getBlogPosts();
+    }
 
-                }
-            }
+    async getBlogPosts() {
+        var graphql = JSON.stringify({
+        query: 'query($count: Int! $contentLength: Int!) { blogPosts(count: $count contentLength: $contentLength) { content title id }}',
+        variables: {
+            "count": 3,
+            "contentLength": 300
         }
-        xhr.open("GET", "/API/getBlogElements.php?position=index");
-        xhr.send();
+        })
+        var requestOptions = {
+        method: 'POST',
+        body: graphql,
+        };
+        let posts = (await (await fetch("http://localhost:1234/API/graphql.php", requestOptions)).json()).data.blogPosts;
+        posts.forEach((element) => {
+            const article = document.createElement("article");
+            article.classList.add("breakWord");
+            this.appendChild(article);
+
+            const h2 = document.createElement("h2");
+            h2.innerText = element["title"];
+            article.appendChild(h2);
+
+            const content = document.createElement("p");
+            content.classList.add("breakWord");
+            content.innerHTML = element["content"];
+            article.appendChild(content);
+
+            const moreP = document.createElement("p");
+            moreP.classList.add("center");
+            article.appendChild(moreP);
+
+            const moreLink = document.createElement("a");
+            moreLink.href = "/post.html?id=" + element["id"];
+            moreP.appendChild(moreLink);
+
+            const moreButton = document.createElement("button");
+            moreButton.innerText = "Mehr lesen";
+            moreLink.appendChild(moreButton);
+        });
     }
 }
 
diff --git a/js/customElements/commentsDisplay.js b/js/customElements/commentsDisplay.js
index fb86044c742ba74f0564f867bfe7642093dae57d..48a2c5f1739ac052c49f881e1d239af4d44db02b 100644
--- a/js/customElements/commentsDisplay.js
+++ b/js/customElements/commentsDisplay.js
@@ -1,48 +1,46 @@
 class commentsDisplay extends HTMLElement {
     constructor() {
         super();
-        let path = window.location.pathname;
-        let pageName = path.split("/").pop();
-
-        let xhr = new XMLHttpRequest();
-        xhr.onreadystatechange = () => {
-            if (xhr.readyState === 4) {
-                if (xhr.status === 200) {
-                    let comments = JSON.parse(xhr.responseText);
-                    comments.forEach((element) => {
-                        const h3 = document.createElement("h3");
-                        h3.classList.add("commentTitle");
-                        h3.innerText = element["name"];
-                        this.appendChild(h3);
-
-                        const commentDiv = document.createElement("div");
-                        commentDiv.classList.add("comment");
-                        this.appendChild(commentDiv);
-
-                        const image = document.createElement("img");
-                        image.src = element["gravatarURL"];
-                        commentDiv.appendChild(image);
-
-                        const article = document.createElement("article");
-                        article.classList.add("commentArticle");
-                        commentDiv.appendChild(article);
-
-                        const commentText = document.createElement("p");
-                        commentText.classList.add("commentText");
-                        commentText.innerText = element["comment"];
-                        article.appendChild(commentText);
-
-                    });
-                } else {
-                    let p = document.createElement("p");
-                    p.innerText = "Leider konnte dieser Inhalt nicht geladen werden, bitte versuche die Seite neu zu laden oder komme später wieder zurück.";
-                    this.appendChild(p);
+        this.getComments()
+    }
 
-                }
-            }
+    async getComments() {
+        var graphql = JSON.stringify({
+        query: 'query($article: String!) { comments(article: $article) { name comment gravatarURL }}',
+        variables: {
+            "article": window.location.pathname
         }
-        xhr.open("GET", "/API/projectComments.php?article=" + pageName);
-        xhr.send();
+        })
+        var requestOptions = {
+        method: 'POST',
+        body: graphql,
+        };
+        let comments = (await (await fetch("http://localhost:1234/API/graphql.php", requestOptions)).json()).data.comments;
+        this.innerHTML = "";
+        comments.forEach((element) => {
+            const h3 = document.createElement("h3");
+            h3.classList.add("commentTitle");
+            h3.innerText = element["name"];
+            this.appendChild(h3);
+
+            const commentDiv = document.createElement("div");
+            commentDiv.classList.add("comment");
+            this.appendChild(commentDiv);
+
+            const image = document.createElement("img");
+            image.src = element["gravatarURL"];
+            commentDiv.appendChild(image);
+
+            const article = document.createElement("article");
+            article.classList.add("commentArticle");
+            commentDiv.appendChild(article);
+
+            const commentText = document.createElement("p");
+            commentText.classList.add("commentText");
+            commentText.innerText = element["comment"];
+            article.appendChild(commentText);
+
+        });
     }
 }
 
diff --git a/js/customElements/contactMailButton.js b/js/customElements/contactMailButton.js
index 646110d8b2ad69bb1e9a397f6530f5bdafab839e..5d1aa176d7e9ca91b63289db5bd71bafacda980f 100644
--- a/js/customElements/contactMailButton.js
+++ b/js/customElements/contactMailButton.js
@@ -6,7 +6,15 @@ class contactMailButton extends HTMLElement {
     }
 
     async addButton() {
-        let sitekey = await (await fetch("/API/config.php?name=sitekey")).text();
+        var graphql = JSON.stringify({
+            query: "query {sitekey}"
+          })
+          var requestOptions = {
+            method: 'POST',
+            body: graphql,
+          };
+
+        let sitekey = (await (await fetch("/API/graphql.php", requestOptions)).json()).data.sitekey;
         console.log(sitekey);
         this.innerHTML = `E-Mail: <button id="emailButton" class="h-captcha" data-sitekey="${sitekey}" data-callback="onSubmit">laden</button><br>`;
         const script = document.createElement("script");
diff --git a/js/customElements/ebkBanner.js b/js/customElements/ebkBanner.js
index 6be307612863c023f2015a7843e96d93c9d760c9..9ff55f32438a20c6fbcd298fe86a0ba45f085d6e 100644
--- a/js/customElements/ebkBanner.js
+++ b/js/customElements/ebkBanner.js
@@ -1,20 +1,24 @@
 class ebkBanner extends HTMLElement {
     constructor(){
         super();
-        let xhr = new XMLHttpRequest();
-        xhr.onreadystatechange = () => {
-            if(xhr.readyState === 4 && xhr.status === 200){
-                if(xhr.responseText > 0) {
-                    const h2 = document.createElement("h2");
-                    h2.classList.add("red");
-                    h2.innerHTML = "Ich biete aktuell wieder verschiedene Artikel zum verkauf an, eine genaue Übersicht ist <a class=\"red\" href=\"/selling.html\">hier</a> zu sehen."
-                    this.appendChild(h2);
-                }
-            }
-        }
+        this.generateBanner();
+    }
 
-        xhr.open("GET", "/API/ebk.php?count");
-        xhr.send();
+    async generateBanner() {
+        var graphql = JSON.stringify({
+            query: 'query { ebayKleinanzeigen{ count }}',
+        })
+        var requestOptions = {
+            method: 'POST',
+            body: graphql,
+        };
+        let elementCount = (await (await fetch("http://localhost:1234/API/graphql.php", requestOptions)).json()).data.ebayKleinanzeigen.count;
+        if(elementCount > 0) {
+            const h2 = document.createElement("h2");
+            h2.classList.add("red");
+            h2.innerHTML = "Ich biete aktuell wieder verschiedene Artikel zum verkauf an, eine genaue Übersicht ist <a class=\"red\" href=\"/selling.html\">hier</a> zu sehen."
+            this.appendChild(h2);
+        }
     }
 }
 
diff --git a/js/customElements/newComment.js b/js/customElements/newComment.js
index 1523128ddbd648cf1b4ce06e00d644dbc36e592e..4e8d9444e7405effc4294f9ed71d305bbafad26a 100644
--- a/js/customElements/newComment.js
+++ b/js/customElements/newComment.js
@@ -8,31 +8,127 @@ class newComment extends HTMLElement {
     }
 
     async setupForm() {
-        let sitekey = await (await fetch("/API/config.php?name=sitekey")).text();
+        var graphql = JSON.stringify({
+            query: "query {sitekey}"
+          })
+          var requestOptions = {
+            method: 'POST',
+            body: graphql,
+          };
+
+        let sitekey = (await (await fetch("/API/graphql.php", requestOptions)).json()).data.sitekey;
 
         let script = document.createElement('script');
         script.src = "https://hCaptcha.com/1/api.js";
         script.type = 'text/javascript';
         script.onload = () => {
-            let pageName = window.location.pathname.split("/").pop();
-            this.parentElement.innerHTML = ` 
-                <form action="/API/newComment.php" method="post">
-                    <label for="name">Name:</label><br>
-                    <input type="text" id="name" name="name"><br><br>
+            let pageName = window.location.pathname
+            const parent = this.parentElement;
+            parent.innerHTML = "";
+
+            const form = document.createElement("form");
+            parent.appendChild(form);
+
+            const labelName = document.createElement("label")
+            labelName.setAttribute("for", "name");
+            labelName.innerText = "Name:";
+            form.appendChild(labelName);
+
+            const nameInput = document.createElement("input");
+            nameInput.type = "text";
+            nameInput.name = "name";
+            nameInput.id = "name";
+            form.appendChild(nameInput);
+
+            let linebreak = document.createElement("br");
+            form.appendChild(linebreak);
+
+            const labelMail = document.createElement("label")
+            labelMail.setAttribute("for", "email");
+            labelMail.innerText = "E-Mail: (wird nicht veröffentlicht)";
+            form.appendChild(labelMail);
+
+            const mailInput = document.createElement("input");
+            mailInput.type = "email";
+            mailInput.name = "email";
+            mailInput.id = "email";
+            form.appendChild(mailInput);
+
+            linebreak = document.createElement("br");
+            form.appendChild(linebreak);
+
+            const labelComment = document.createElement("label")
+            labelComment.setAttribute("for", "comment");
+            labelComment.innerText = "Kommentar:";
+            form.appendChild(labelComment);
+
+            const commentInput = document.createElement("textarea");
+            commentInput.name = "comment";
+            commentInput.id = "comment";
+            form.appendChild(commentInput);
+
             
-                    <label for="email">E-Mail: (wird nicht ver&ouml;ffentlicht)</label><br>
-                    <input type="text" id="email" name="email"><br><br>
+            linebreak = document.createElement("br");
+            form.appendChild(linebreak);
+
+            const hcaptcha = document.createElement("div");
+            hcaptcha.classList.add("h-captcha");
+            hcaptcha.setAttribute("data-theme", "dark");
+            hcaptcha.setAttribute("data-sitekey", sitekey);
+            form.appendChild(hcaptcha);
             
-                    <label for="comment">Kommentar:</label><br>
-                    <textarea name="comment" id="comment"></textarea><br><br>
-                    
-                    <div class="h-captcha" data-theme="dark" data-sitekey="${sitekey}"></div><br>
-                    
-                    <input type="hidden" name="pagename" id="pagename" value="${pageName}">
-                    <input type="submit" value="Kommentar ver&ouml;ffentlichen"><br>
-                    <p>Mit dem Klick auf den obigen Button erkl&auml;ren sie sich mit der <a href="/datenschutzerklaerung.html">Datenschutzerkl&auml;rung</a> einverstanden.</p>
-                </form>
-            `;
+            linebreak = document.createElement("br");
+            form.appendChild(linebreak);
+
+            const submitButton = document.createElement("input");
+            submitButton.value = "Kommentar veröffentlichen";
+            submitButton.type = "submit";
+            form.appendChild(submitButton);
+
+            const labelDatenschutz = document.createElement("p");
+            labelDatenschutz.innerText = "Mit dem Klick auf den obigen Button erklären sie sich mit der ";
+            form.appendChild(labelDatenschutz);
+
+            const datenschutzLink = document.createElement("a");
+            datenschutzLink.innerText = "Datenschutzerklärung";
+            datenschutzLink.href = "/datenschutzerklaerung.html";
+            labelDatenschutz.appendChild(datenschutzLink);
+
+            const datenschutzTextNode = document.createTextNode(" einverstanden");
+            labelDatenschutz.appendChild(datenschutzTextNode);
+
+            submitButton.onclick = async () => {
+                if(nameInput.value == "" || commentInput.value == "") {
+                    alert("Name oder Kommentar nicht ausgefüllt.");
+                    return;
+                }
+
+                var graphql = JSON.stringify({
+                query: 'query($article: String!, $name: String!, $hCaptchaResponse: String!, $email: String!, $comment: String!) { newComment(article: $article, name: $name, email: $email, comment: $comment, hCaptchaResponse: $hCaptchaResponse)}',
+                variables: {
+                    "article": pageName,
+                    "name": nameInput.value,
+                    "email": mailInput.value,
+                    "comment": commentInput.value,
+                    "hCaptchaResponse": form.querySelector(".h-captcha iframe").getAttribute("data-hcaptcha-response")
+                }
+                })
+                var requestOptions = {
+                method: 'POST',
+                body: graphql,
+                };
+                let data = (await (await fetch("http://localhost:1234/API/graphql.php", requestOptions)).json()).data;
+                if(data.newComment == "OK"){
+                    document.querySelector("jl-comments_display").getComments();
+                    parent.innerHTML = "<jl-new_comment></jl-new_comment>"
+                } else {
+                    alert("Fehler: " + data.newComment);
+                }
+            }
+
+            form.onsubmit = () => {
+                return false;
+            }
         }
         document.body.append(script);
     }
diff --git a/js/customElements/sellingTable.js b/js/customElements/sellingTable.js
index cf88d68a1471e10e89282c421c530e8d4f9f85c4..53bb82da5e084cbab7914d1a242f3dd6581ea855 100644
--- a/js/customElements/sellingTable.js
+++ b/js/customElements/sellingTable.js
@@ -1,13 +1,17 @@
 import * as basicLightbox from 'basiclightbox'
 
 class sellingTable extends HTMLElement {
-    constructor(){
-        const config = [
+    
+    constructor() {
+        super();
+
+        this.config = [
             {
                 "title": "Bild",
-                "fieldName": "previewImage",
+                "fieldName": "preview",
                 "displayType": "image",
-                "fullImage": "image"
+                "fullImage": "image",
+                "index": 0
             },
             {
                 "title": "Titel",
@@ -31,68 +35,70 @@ class sellingTable extends HTMLElement {
                 "linkText": "Anzeige ansehen",
                 "target": "_blank"
             },
-        ]
+        ];
 
-        super();
+        this.generateTable();
+    }
+
+    async generateTable() {
         const table = document.createElement("table");
         this.appendChild(table);
 
         const tr = document.createElement("tr");
         table.appendChild(tr);
 
-        config.forEach(element => {
+        this.config.forEach(element => {
             const th = document.createElement("th");
             th.innerText = element["title"];
             tr.appendChild(th);
         });
 
-        let xhr = new XMLHttpRequest();
-        xhr.onreadystatechange = () => {
-            if(xhr.readyState === 4 && xhr.status === 200){
-                const response = JSON.parse(xhr.responseText);
-                response.forEach( ad => {
-                    const tr = document.createElement("tr");
-                    table.appendChild(tr);
-                    config.forEach(element => {
-                        const th = document.createElement("th");
+        var graphql = JSON.stringify({
+            query: 'query { ebayKleinanzeigen(imageCount: 1) { elements { images { preview image } title price shipping link }}}',
+        })
+        var requestOptions = {
+            method: 'POST',
+            body: graphql,
+        };
+        let elements = (await (await fetch("http://localhost:1234/API/graphql.php", requestOptions)).json()).data.ebayKleinanzeigen.elements;
+        elements.forEach(ad => {
+            const tr = document.createElement("tr");
+            table.appendChild(tr);
+            this.config.forEach(element => {
+                const th = document.createElement("th");
 
-                        switch(element["displayType"]) {
-                            case "text":
-                                th.innerText = ad[element["fieldName"]];
-                                break;
-                            case "link":
-                                const link = document.createElement("a");
-                                th.appendChild(link);
-                                link.href = ad[element["fieldName"]];
-                                link.innerText = element["linkText"];
-
-                                if("target" in  element) {
-                                    link.target = element["target"];
-                                }
-                                break;
-                            case "image":
-                                const img = document.createElement("img");
-                                th.appendChild(img);
-                                img.src = ad[element["fieldName"]];
-                                img.onclick = () => {
-                                    const instance = basicLightbox.create(`
-                                    <img src="${ad[element["fullImage"]]}">
-                                    `);
-                                    instance.show();
-                                }
-                                break;
+                switch (element["displayType"]) {
+                    case "text":
+                        th.innerText = ad[element["fieldName"]];
+                        break;
+                    case "link":
+                        const link = document.createElement("a");
+                        th.appendChild(link);
+                        link.href = ad[element["fieldName"]];
+                        link.innerText = element["linkText"];
 
+                        if ("target" in element) {
+                            link.target = element["target"];
+                        }
+                        break;
+                    case "image":
+                        const img = document.createElement("img");
+                        th.appendChild(img);
+                        img.src = ad["images"][element["index"]][element["fieldName"]];
+                        img.onclick = () => {
+                            const instance = basicLightbox.create(`
+                            <img src="${ad["images"][element["index"]][element["fullImage"]]}">
+                            `);
+                            instance.show();
                         }
+                        break;
 
-                        tr.appendChild(th);
-                    });
-                    
-                })
-            }
-        }
+                }
 
-        xhr.open("GET", "/API/ebk.php");
-        xhr.send();
+                tr.appendChild(th);
+            });
+
+        });
     }
 }
 
diff --git a/js/customElements/skills.js b/js/customElements/skills.js
index 65561b928c4e0343bba68c4dab54ccc1cd0f8939..c37631b016f7d8cb658b51f096baf9776440d327 100644
--- a/js/customElements/skills.js
+++ b/js/customElements/skills.js
@@ -1,19 +1,26 @@
 class Skill extends HTMLElement {
     constructor() {
         super();
-        let xhr = new XMLHttpRequest();
-        xhr.onreadystatechange = () => {
-            if (xhr.readyState == 4 && xhr.status == 200) {
-                JSON.parse(xhr.responseText).forEach(skill => {
-                    const image = document.createElement("img");
-                    image.classList.add("skills");
-                    image.src = "/API/getFile.php?filename=" + skill;
-                    this.appendChild(image);
-                });
-            }
-        }
-        xhr.open("GET", "/API/skills.php");
-        xhr.send();
+        this.getSkills();
+    }
+
+    async getSkills(){
+        var graphql = JSON.stringify({
+            query: "query {skills}"
+          })
+          var requestOptions = {
+            method: 'POST',
+            body: graphql,
+          };
+
+        let skills = (await (await fetch("/API/graphql.php", requestOptions)).json()).data.skills;
+        skills.forEach(skill => {
+            const image = document.createElement("img");
+            image.classList.add("skills");
+            image.src = "/API/getFile.php?filename=" + skill;
+            this.appendChild(image);
+        });
+
     }
 }
 
diff --git a/js/viewPost.js b/js/viewPost.js
index 01241e908784e33d2907bf31be386a0f8cda6ecf..c7e3d63d778457ac1d048e2668fc5d9ddf94197b 100644
--- a/js/viewPost.js
+++ b/js/viewPost.js
@@ -26,7 +26,17 @@ async function loadPost() {
     if(id == null) {
         content.innerHTML = "<h1>404 - Post not found</h1>";
     } else {
-        let post = await (await fetch("/API/getPost.php?id=" + id)).json();
+        var graphql = JSON.stringify({
+        query: 'query($postID: String!) {blogPost(id: $postID) {content title}}',
+        variables: {
+            "postID": id
+        }
+        })
+        var requestOptions = {
+        method: 'POST',
+        body: graphql,
+        };
+        let post = (await (await fetch("http://localhost:1234/API/graphql.php", requestOptions)).json()).data.blogPost;
         content.innerHTML = post["content"];
         document.title = post["title"] + " - Jonas Leder";
 
diff --git a/package.json b/package.json
index 8fa718de2c45ce4fd8483bdc1c7d9ef18a08787a..53805d5b6bb32108561e733377f6a53e7f4b03d5 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
     "author": "jonasled <git@jonasled.de>",
     "license": "GPL-3.0-or-later",
     "scripts": {
-        "compile": "concurrently \"yarn css\" \"yarn js\"",
+        "build": "concurrently \"yarn css\" \"yarn js\"",
         "css": "stylus styl/ -o public/css/ ",
         "js": "webpack --config ./webpack.conf.js",
         "watch": "concurrently \"stylus -w styl/ -o public/css/\" \"cd public && php -S 0.0.0.0:1234\" \"webpack --config ./webpack.conf.js --mode development --watch\""
diff --git a/public/API/config.php b/public/API/config.php
deleted file mode 100644
index 336fdbd8d55bfecd9718ba2aedb017eb4cfcee07..0000000000000000000000000000000000000000
--- a/public/API/config.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-require "./lib/config.php";
-
-$configValue = $_GET['name'];
-
-switch ($configValue){
-    case "sitekey":
-        echo($sitekey);
-        break;
-    default:
-        echo("notFound");
-}
diff --git a/public/API/ebk.php b/public/API/ebk.php
deleted file mode 100644
index 29541c1e17e824e809ede226252a95a8369ae9ea..0000000000000000000000000000000000000000
--- a/public/API/ebk.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-require 'vendor/autoload.php';
-require "./lib/config.php";
-
-use GuzzleHttp\Client;
-
-$responseJSON = [];
-
-$client = new Client();
-$headers = [
-    'authorization' => 'Basic ' . $ebayKleinanzeigenToken,
-    'user-agent' => 'okhttp/4.9.1',
-    'x-ebayk-app' => '4e10d7fd-6fef-4f87-afb0-b8ede2f494071636475109828',
-    'Host' => 'api.ebay-kleinanzeigen.de',
-    'Accept' => '*/*',
-    'Accept-Encoding' => 'gzip, deflate, br'
-];
-$response = $client->request('GET', "https://api.ebay-kleinanzeigen.de/api/ads.json?_in=title,price,pictures,link,features-active,search-distance,negotiation-enabled,attributes,medias,medias.media,medias.media.title,medias.media.media-link,store-id,store-title&page=0&size=31&userIds=$ebayKleinanzeigenUserId&pictureRequired=false&includeTopAds=false&limitTotalResultCount=true", [
-    'headers' => $headers ]);
-
-$response = json_decode($response->getBody(), true);
-$ads = $response["{http://www.ebayclassifiedsgroup.com/schema/ad/v1}ads"]["value"]["ad"];
-
-foreach($ads as $ad) {
-    $element = [
-        "title" => $ad["title"]["value"],
-        "price" => $ad["price"]["amount"]["value"] . " €",
-        "shipping" => "nein"
-    ];
-
-    foreach($ad["attributes"]["attribute"] as $attribute) {
-        if(str_contains($attribute["name"], "versand")) {
-            $element["shipping"] = $attribute["value"][0]["value"];
-        }
-    }
-
-    foreach($ad["link"] as $link) {
-        if($link["rel"] == "self-public-website") {
-            $element["link"] = $link["href"];
-        }
-    }
-
-    if(sizeof($ad["pictures"]["picture"]) > 0) {
-        foreach($ad["pictures"]["picture"][0]["link"] as $picture) {
-            if($picture["rel"] == "teaser") {
-                $element["previewImage"] = str_replace("https://i.ebayimg.com", "/API/ebayimg.php?url=", $picture["href"]);
-            }
-            if($picture["rel"] == "XXL") {
-                
-                $element["image"] = str_replace("https://i.ebayimg.com", "/API/ebayimg.php?url=", $picture["href"]);
-            }
-        }
-    }
-
-    array_push($responseJSON, $element);
-}
-
-if(isset($_GET["count"])) {
-    echo sizeof($responseJSON);
-    die();
-}
-echo json_encode($responseJSON);
\ No newline at end of file
diff --git a/public/API/getBlogElements.php b/public/API/getBlogElements.php
deleted file mode 100644
index 8271e20e55a8135c3570d2c82ad006d22d0567ca..0000000000000000000000000000000000000000
--- a/public/API/getBlogElements.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-include "./lib/config.php";
-include "./lib/mysql.php";
-
-$position = $_GET['position'];
-
-if($position == "index"){
-    $limit = $homeMaxPost;
-} else if($position == "footer"){
-    $limit = $footerMaxPost;
-} else {
-    die("wrong parameter");
-}
-$responseJSON = [];
-
-$result = $conn->query("SELECT * FROM posts order by id desc limit $limit");
-if ($result->num_rows > 0) {
-    while ($row = $result->fetch_assoc()) {
-        $blogElement = [
-            "title" => $row["title"],
-            "id" => $row["id"],
-            "content" => $row["content"]
-        ];
-
-        array_push($responseJSON, $blogElement);
-    }
-}
-header('Content-Type: application/json');
-echo json_encode($responseJSON);
\ No newline at end of file
diff --git a/public/API/getMail.php b/public/API/getMail.php
deleted file mode 100644
index b2f438b113e2f7f78d036114fd6212d49096c500..0000000000000000000000000000000000000000
--- a/public/API/getMail.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-include("./lib/config.php");
-
-require("./vendor/autoload.php");
-use GuzzleHttp\Client;
-
-$data = array(
-    'secret' => $secretkey,
-    'response' => $_POST['h-captcha-response']
-);
-$client = new Client();
-
-$response = $client->post("https://hcaptcha.com/siteverify", [
-    "form_params" => $data
-]);
-
-$responseData = json_decode($response->getBody());
-if($responseData->success) {
-    echo("$contactmail");
-} else {
-    echo("Failed to verify Captcha");
-}
\ No newline at end of file
diff --git a/public/API/getPost.php b/public/API/getPost.php
deleted file mode 100644
index 9ac86e89b3919c1e679beba9d301f7632810095c..0000000000000000000000000000000000000000
--- a/public/API/getPost.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-include "./lib/config.php";
-include "./lib/mysql.php";
-
-$id = $conn->real_escape_string($_GET["id"]);
-$result = $conn->query("SELECT * FROM posts WHERE id=$id");
-if ($result->num_rows > 0) {
-    $row = $result->fetch_assoc();
-} else {
-    die("Post not found");
-}
-
-$title = $row["title"];
-$content = $row["content"];
-$date = $row["date"];
-$id = $row["id"];
-
-header('Content-Type: application/json');
-echo json_encode([
-    "title" => $title,
-    "content" => $content,
-    "date" => $date,
-    "id" => $id
-]);
\ No newline at end of file
diff --git a/public/API/graphql.php b/public/API/graphql.php
new file mode 100644
index 0000000000000000000000000000000000000000..cec07bc38dea6dcc516d20c86c8fb583fffa00b3
--- /dev/null
+++ b/public/API/graphql.php
@@ -0,0 +1,35 @@
+<?php
+use GraphQL\GraphQL;
+use GraphQL\Type\Schema;
+
+require 'vendor/autoload.php';
+require "./lib/config.php";
+require "./lib/mysql.php";
+require "./queries/queries.php";
+
+$schema = new Schema([
+    'query' => $queryType
+]);
+
+$rawInput = file_get_contents('php://input');
+$input = json_decode($rawInput, true);
+$query = $input['query'];
+$variableValues = isset($input['variables']) ? $input['variables'] : null;
+
+try {
+    $rootValue = [
+        "db"=> $conn
+    ];
+    $result = GraphQL::executeQuery($schema, $query, $rootValue, null, $variableValues);
+    $output = $result->toArray();
+} catch (\Exception $e) {
+    $output = [
+        'errors' => [
+            [
+                'message' => $e->getMessage()
+            ]
+        ]
+    ];
+}
+header('Content-Type: application/json');
+echo json_encode($output);
\ No newline at end of file
diff --git a/public/API/newComment.php b/public/API/newComment.php
deleted file mode 100644
index ab8d29800c3352b30c90f4d5c9da5b87e42a4320..0000000000000000000000000000000000000000
--- a/public/API/newComment.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-require './vendor/autoload.php';
-include("./lib/config.php");
-include("./lib/mysql.php");
-
-use GuzzleHttp\Client;
-
-$data = array(
-    'secret' => $secretkey,
-    'response' => $_POST['h-captcha-response']
-);
-
-$client = new Client();
-
-$response = $client->post("https://hcaptcha.com/siteverify", [
-    "form_params" => $data
-]);
-
-$responseData = json_decode($response->getBody());
-
-
-
-if($responseData->success) {
-
-    $article =$conn->escape_string($_POST["pagename"]);
-    $name = $conn->escape_string($_POST["name"]);
-    $email = $conn->escape_string($_POST["email"]);
-    $comment = $conn->escape_string($_POST["comment"]);
-
-    $sql = "INSERT INTO comments (name, email, comment, article) VALUES ('$name', '$email', '$comment', '$article')";
-
-    if ($conn->query($sql) === TRUE) {
-        header("Location: " . $_SERVER["HTTP_REFERER"]);
-    } else {
-        echo "Error: " . $sql . "<br>" . $conn->error;
-    }
-} else {
-    echo "Failed to verify captcha.";
-}
diff --git a/public/API/projectComments.php b/public/API/projectComments.php
deleted file mode 100644
index e39b3fe9ea3a2ba8d73822f313c1b1f869ca73d3..0000000000000000000000000000000000000000
--- a/public/API/projectComments.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-include "./lib/config.php";
-include "./lib/mysql.php";
-include "./lib/getGravatar.php";
-
-$article = $conn->real_escape_string($_GET["article"]);
-
-$responseJSON = [];
-
-$result = $conn->query("SELECT * FROM comments WHERE article='$article'");
-if ($result->num_rows > 0) {
-    while ($row = $result->fetch_assoc()) {
-        $commentElement = [
-            "name" => $row["name"],
-            "comment" => $row["comment"],
-            "gravatarURL" => get_gravatar($row["email"])
-        ];
-
-        array_push($responseJSON, $commentElement);
-    }
-}
-header('Content-Type: application/json');
-echo json_encode($responseJSON);
\ No newline at end of file
diff --git a/public/API/queries/blogPost.php b/public/API/queries/blogPost.php
new file mode 100644
index 0000000000000000000000000000000000000000..6f3d732706defa0f7675c2050c76e4c97e0145e5
--- /dev/null
+++ b/public/API/queries/blogPost.php
@@ -0,0 +1,65 @@
+<?php
+
+use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+
+$blogPostFields = new ObjectType([
+    "name" => "Blog",
+    "fields" => [
+        "title" => Type::string(),
+        "content" => Type::string(),
+        "date" => Type::string(),
+        "id" => Type::string()
+    ],
+]);
+
+function blogPost($id, $conn)
+{
+    $id = $conn->real_escape_string($id);
+    $result = $conn->query("SELECT * FROM posts WHERE id=$id");
+    if ($result->num_rows > 0) {
+        $row = $result->fetch_assoc();
+    } else {
+        return [
+            "title" => "Nicht Gefunden",
+            "content" => "Post wurde nicht gefunden",
+            "date" => "2000-01-01 00:00:00",
+            "id" => "-1"
+        ];
+    }
+
+    return [
+        "title" => $row["title"],
+        "content" => $row["content"],
+        "date" => $row["date"],
+        "id" => $row["id"],
+    ];
+}
+
+function blogPosts($count, $contentLength, $conn)
+{
+    $response = [];
+    $result = $conn->query("SELECT * FROM posts order by id desc limit $count");
+    if ($result->num_rows > 0) {
+        while ($row = $result->fetch_assoc()) {
+            $content = $row["content"];
+            if($contentLength != null && strlen($content) > $contentLength) {
+                $contentNew = substr($content, 0, $contentLength);
+                $contentRest = substr($content, $contentLength);
+
+                $content = $contentNew . explode(" ", $contentRest)[0] . " ...";
+
+            }
+            $blogElement = [
+                "title" => $row["title"],
+                "content" => $content,
+                "date" => $row["date"],
+                "id" => $row["id"],
+            ];
+    
+            array_push($response, $blogElement);
+        }
+    }
+    
+    return $response;
+}
\ No newline at end of file
diff --git a/public/API/queries/comments.php b/public/API/queries/comments.php
new file mode 100644
index 0000000000000000000000000000000000000000..3b3d46ba952ab523e35140ffd1299ea0ee1c54dd
--- /dev/null
+++ b/public/API/queries/comments.php
@@ -0,0 +1,65 @@
+<?php
+
+use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+use GuzzleHttp\Client;
+
+include "lib/getGravatar.php";
+$commentField = new ObjectType([
+    "name" => "Comment",
+    "fields" => [
+        "name" => Type::string(),
+        "comment" => Type::string(),
+        "gravatarURL" => Type::string(),
+        "id" => Type::int()
+    ],
+]);
+
+function comments($article, $conn)
+{
+    $response = [];
+    $result = $conn->query("SELECT * FROM comments WHERE article='$article'");
+    while ($row = $result->fetch_assoc()) {
+        $commentElement = [
+            "name" => $row["name"],
+            "comment" => $row["comment"],
+            "gravatarURL" => get_gravatar($row["email"]),
+            "id" => $row["id"]
+        ];
+
+        array_push($response, $commentElement);
+    }
+    return $response;
+}
+
+function newComment($conn, $article, $name, $email, $comment, $hCaptchaResponse)
+{
+    require "./lib/config.php";
+    $data = array(
+        'secret' => $secretkey,
+        'response' => $hCaptchaResponse
+    );
+    $client = new Client();
+
+    $response = $client->post("https://hcaptcha.com/siteverify", [
+        "form_params" => $data
+    ]);
+
+    $responseData = json_decode($response->getBody());
+    if (!$responseData->success) {
+        return "Failed to verify Captcha";
+    }
+
+    $article = $conn->escape_string($article);
+    $name = $conn->escape_string($name);
+    $email = $conn->escape_string($email);
+    $comment = $conn->escape_string($comment);
+
+    $sql = "INSERT INTO comments (name, email, comment, article) VALUES ('$name', '$email', '$comment', '$article')";
+
+    if ($conn->query($sql) === TRUE) {
+        return "OK";
+    } else {
+        return "Error: " . $sql . "<br>" . $conn->error;
+    }
+}
diff --git a/public/API/queries/ebayKleinanzeigen.php b/public/API/queries/ebayKleinanzeigen.php
new file mode 100644
index 0000000000000000000000000000000000000000..a479dec181fd9dc35abcf7fd9a9f749c890635d1
--- /dev/null
+++ b/public/API/queries/ebayKleinanzeigen.php
@@ -0,0 +1,103 @@
+<?php
+
+use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+use GuzzleHttp\Client;
+
+$ebayKleinanzeigenImages = new ObjectType([
+    "name" => "EBK Image",
+    "fields" => [
+        "preview" => Type::string(),
+        "image" => Type::string()
+    ]
+]);
+
+$ebayKleinanzeigenElements = new ObjectType([
+    "name" => "EBK Elements",
+    "fields" => [
+        "title" => Type::string(),
+        "price" => Type::string(),
+        "shipping" => Type::string(),
+        "link" => Type::string(),
+        "images" => [
+            "type" => Type::listOf($ebayKleinanzeigenImages),    
+            "args" => [
+                "count" => Type::int()
+            ]
+        ],
+        "id" => Type::string()
+    ]
+]);
+
+$ebayKleinanzeigenFields = new ObjectType([
+    "name" => "Ebay Kleinanzeigen",
+    "fields" => [
+        "count" => Type::int(),
+        "elements" => Type::listOf($ebayKleinanzeigenElements)
+    ],
+]);
+
+function ebayKleinanzeigen($imageCount) {
+    require "./lib/config.php";
+    $elements = [];
+
+    $client = new Client();
+    $headers = [
+        'authorization' => 'Basic ' . $ebayKleinanzeigenToken,
+        'user-agent' => 'okhttp/4.9.1',
+        'x-ebayk-app' => '4e10d7fd-6fef-4f87-afb0-b8ede2f494071636475109828',
+        'Host' => 'api.ebay-kleinanzeigen.de',
+        'Accept' => '*/*',
+        'Accept-Encoding' => 'gzip, deflate, br'
+    ];
+    $response = $client->request('GET', "https://api.ebay-kleinanzeigen.de/api/ads.json?_in=title,price,pictures,link,features-active,search-distance,negotiation-enabled,attributes,medias,medias.media,medias.media.title,medias.media.media-link,store-id,store-title&page=0&size=31&userIds=$ebayKleinanzeigenUserId&pictureRequired=false&includeTopAds=false&limitTotalResultCount=true", [
+        'headers' => $headers ]);
+
+    $response = json_decode($response->getBody(), true);
+    $ads = $response["{http://www.ebayclassifiedsgroup.com/schema/ad/v1}ads"]["value"]["ad"];
+
+    foreach($ads as $ad) {
+        $element = [
+            "title" => html_entity_decode($ad["title"]["value"]),
+            "id" => $ad["id"],
+            "price" => $ad["price"]["amount"]["value"] . " €",
+            "shipping" => "nein"
+        ];
+
+        foreach($ad["attributes"]["attribute"] as $attribute) {
+            if(str_contains($attribute["name"], "versand")) {
+                $element["shipping"] = $attribute["value"][0]["value"];
+            }
+        }
+
+        foreach($ad["link"] as $link) {
+            if($link["rel"] == "self-public-website") {
+                $element["link"] = $link["href"];
+            }
+        }
+
+        $images = [];
+        foreach(array_slice($ad["pictures"]["picture"], 0, $imageCount) as $picture) {
+            $image = [];
+            
+            foreach($picture["link"] as $pictureSize) {
+                if($pictureSize["rel"] == "teaser") {
+                    $image["preview"] = str_replace("https://i.ebayimg.com", "/API/ebayimg.php?url=", $pictureSize["href"]);
+                }
+                if($pictureSize["rel"] == "XXL") {
+                    
+                    $image["image"] = str_replace("https://i.ebayimg.com", "/API/ebayimg.php?url=", $pictureSize["href"]);
+                }
+            }
+            array_push($images, $image);
+        }
+        $element["images"] = $images;
+
+        array_push($elements, $element);
+    }
+
+    return [
+        "count" => sizeof($elements),
+        "elements" => $elements
+    ];
+}
\ No newline at end of file
diff --git a/public/API/queries/mailAddress.php b/public/API/queries/mailAddress.php
new file mode 100644
index 0000000000000000000000000000000000000000..17807df99aa67375d4c8012a2cb21bddc7d99802
--- /dev/null
+++ b/public/API/queries/mailAddress.php
@@ -0,0 +1,22 @@
+<?php
+use GuzzleHttp\Client;
+
+function mailAddress($hCaptchaResponse) {
+    require "./lib/config.php";
+    $data = array(
+        'secret' => $secretkey,
+        'response' => $hCaptchaResponse
+    );
+    $client = new Client();
+    
+    $response = $client->post("https://hcaptcha.com/siteverify", [
+        "form_params" => $data
+    ]);
+    
+    $responseData = json_decode($response->getBody());
+    if($responseData->success) {
+        return "$contactmail";
+    } else {
+        return "Failed to verify Captcha";
+    }
+}
\ No newline at end of file
diff --git a/public/API/queries/queries.php b/public/API/queries/queries.php
new file mode 100644
index 0000000000000000000000000000000000000000..bbd802a299751516ad1a5bdee50aaf2e55948962
--- /dev/null
+++ b/public/API/queries/queries.php
@@ -0,0 +1,79 @@
+<?php
+use GraphQL\Type\Definition\ObjectType;
+use GraphQL\Type\Definition\Type;
+
+require "./queries/skills.php";
+require "./queries/blogPost.php";
+require "./queries/comments.php";
+require "./queries/mailAddress.php";
+require "./queries/ebayKleinanzeigen.php";
+
+
+$queryType = new ObjectType([
+    'name' => 'Query',
+    'fields' => [
+        'sitekey' => [
+            'type' => Type::string(),
+            'resolve' => fn ($rootValue, $args) => $sitekey,
+        ],
+        'mailAddress' => [
+            'type' => Type::string(),
+            "args" => [
+                "hCaptchaResponse" => Type::string()
+            ],
+            'resolve' => fn ($rootValue, $args) => mailAddress($args["hCaptchaResponse"]),
+        ],
+        'skills' => [
+            'type' => Type::listOf(Type::string()),
+            'resolve' => fn ($rootValue, $args) => getSkills(),
+        ],
+        'blogPost' => [
+            "type" => $blogPostFields,
+            'args' => [
+                'id' => Type::nonNull(Type::string()),
+            ],
+            'resolve' => fn ($rootValue, $args) => blogPost($args["id"], $rootValue["db"]),
+        ],
+        'blogPosts' => [
+            "type" => Type::listOf($blogPostFields),
+            "args" => [
+                "count" => Type::nonNull(Type::int()),
+                "contentLength" => [
+                    "type" => Type::int(),
+                    "defaultValue" => null
+                ]
+                
+            ],
+            'resolve' => fn ($rootValue, $args) => blogPosts($args["count"], $args["contentLength"], $rootValue["db"]),
+        ],
+        'comments' => [
+            "type" => Type::listOf($commentField),
+            "args" => [
+                "article" => Type::nonNull(Type::string()),
+            ],
+            'resolve' => fn ($rootValue, $args) => comments($args["article"], $rootValue["db"]),
+        ],
+        "newComment" => [
+            "type" => Type::string(),
+            "args" => [
+                "article" => Type::string(),
+                "name" => Type::string(),
+                "email" => Type::string(),
+                "comment" => Type::string(),
+                "hCaptchaResponse" => Type::string()
+            ],
+            'resolve' => fn ($rootValue, $args) => newComment($rootValue["db"], $args["article"], $args["name"], $args["email"], $args["comment"], $args["hCaptchaResponse"]),
+        ],
+        'ebayKleinanzeigen' => [
+            "type" => $ebayKleinanzeigenFields,
+            "args" => [
+                "imageCount" => [
+                    "type" => Type::int(),
+                    "defaultValue" => 0
+                ]
+            ],
+            'resolve' => fn ($rootValue, $args) => ebayKleinanzeigen($args["imageCount"]),
+        ]
+
+    ],
+]);
diff --git a/public/API/queries/skills.php b/public/API/queries/skills.php
new file mode 100644
index 0000000000000000000000000000000000000000..31670650e1ff02c573b4c61eceaddea7f976061d
--- /dev/null
+++ b/public/API/queries/skills.php
@@ -0,0 +1,23 @@
+<?php
+
+function getSkills() {
+        require "./lib/config.php";
+        $s3Client = new Aws\S3\S3Client([
+                'version' => 'latest',
+                'region'  => 'us-east-1',
+                'endpoint' => $S3Server,
+                'use_path_style_endpoint' => true,
+                'credentials' => [
+                        'key'    => $S3AccessKey,
+                        'secret' => $S3SecretKey,
+                ],
+        ]);
+
+        $result = $s3Client->ListObjects(['Bucket' => $S3BucketName, 'Delimiter'=>'/', 'Prefix' => 'skills/']);
+
+        $response = [];
+        foreach ($result["Contents"] as $skill){
+                array_push($response, $skill["Key"]);
+        }
+        return $response;
+}
\ No newline at end of file
diff --git a/public/API/skills.php b/public/API/skills.php
deleted file mode 100644
index 28116e71d41379b6cb73a03a7f102e9e74d63d99..0000000000000000000000000000000000000000
--- a/public/API/skills.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-include("./lib/config.php");
-require 'vendor/autoload.php';
-
-$s3Client = new Aws\S3\S3Client([
-        'version' => 'latest',
-        'region'  => 'us-east-1',
-        'endpoint' => $S3Server,
-        'use_path_style_endpoint' => true,
-        'credentials' => [
-                'key'    => $S3AccessKey,
-                'secret' => $S3SecretKey,
-            ],
-]);
-
-$result = $s3Client->ListObjects(['Bucket' => $S3BucketName, 'Delimiter'=>'/', 'Prefix' => 'skills/']);
-
-$response = [];
-foreach ($result["Contents"] as $skill){
-        array_push($response, $skill["Key"]);
-}
-
-header("Content-Type: application/json");
-echo json_encode($response);
\ No newline at end of file
diff --git a/public/impressum.html b/public/impressum.html
index 92ca021d5381c9f87c8817fc116cf00f6a6ea16b..55ec96f6fb92889d2abd8e4fefcef34bae6df3a5 100644
--- a/public/impressum.html
+++ b/public/impressum.html
@@ -49,20 +49,24 @@
         <a href="https://hcaptcha.com/privacy">Privacy Policy</a> and
         <a href="https://hcaptcha.com/terms">Terms of Service</a> apply.
         <script type="text/javascript">
-            function onSubmit(token) {
-                let xmlhttp = new XMLHttpRequest();
-                xmlhttp.onreadystatechange = function() {
-                    if (this.readyState == 4 && this.status == 200) {
-                        let button = document.getElementById("emailButton");
-                        let emailElement = document.createElement("p");
-                        emailElement.className = "emailBox";
-                        emailElement.innerText = this.responseText;
-                        button.parentNode.replaceChild(emailElement, button);
-                    }
+            async function onSubmit(token) {
+                var graphql = JSON.stringify({
+                query: 'query($hCaptchaResponse: String!) { mailAddress(hCaptchaResponse: $hCaptchaResponse)}',
+                variables: {
+                    "hCaptchaResponse": token
+                }
+                })
+                var requestOptions = {
+                method: 'POST',
+                body: graphql,
                 };
-                xmlhttp.open("POST", "/API/getMail.php", true);
-                xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
-                xmlhttp.send("h-captcha-response=" + token);
+                let mailAddress = (await (await fetch("http://localhost:1234/API/graphql.php", requestOptions)).json()).data.mailAddress;
+
+                let button = document.getElementById("emailButton");
+                let emailElement = document.createElement("p");
+                emailElement.className = "emailBox";
+                emailElement.innerText = mailAddress;
+                button.parentNode.replaceChild(emailElement, button);
             }
         </script>
     </div>