diff --git a/.envrc b/.envrc
index 4f0af3a80ef9b7d25eaa8177ee64bcd7310af8b4..3b5bb1416c85df12ee45836120a60c4dcab29a79 100644
--- a/.envrc
+++ b/.envrc
@@ -7,6 +7,3 @@ fi
 
 # loads personal (secret) data from separate env file (not checked in)
 source_env_if_exists .envrc.local
-
-type yarn >/dev/null 2>&1 && PATH="$PATH:$(yarn global bin)"
-export PATH
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2d206274a49c870537cdb702758bdf88b416a0d8..0fd733088730ec925aa81515df5920c850a79061 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,7 +4,7 @@ default:
     - env
   interruptible: true
   tags:
-    - holi-small # build on smaller machine
+    - 1cpu-4gb # build on smaller machine
 
 variables:
   API_DOMAIN_PATH: "$CI_PROJECT_DIR/api_domain"
diff --git a/.husky/pre-commit b/.husky/pre-commit
deleted file mode 100755
index c1d699695f34ed0a0d789b795412ef67013e7458..0000000000000000000000000000000000000000
--- a/.husky/pre-commit
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-. "$(dirname "$0")/_/husky.sh"
-
-gitleaks protect --staged -v -c ../.gitleaks.toml
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..2f34ebc58adb3df395e52bedee0d670532274122
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,9 @@
+repos:
+-   repo: local
+    hooks:
+    -   id: gitleaks
+        name: gitleaks
+        language: system
+        entry: gitleaks protect --staged -v -c ../.gitleaks.toml
+        pass_filenames: false
+        always_run: true
diff --git a/app/betterplace.ts b/app/betterplace.ts
index 96bd037d2ac9a78e7f876daeaf8dae5160896530..c76686e6451e6055faa92d1f2ae2b08f5986ad6b 100644
--- a/app/betterplace.ts
+++ b/app/betterplace.ts
@@ -138,26 +138,27 @@ const transformNews = (blogPost: ApiBlogPost): News => ({
   url: blogPost.links.find((p) => p.rel === "platform")!.href,
 });
 
-export const fetchCategories = (projectId: number): Promise<string[]> => {
+export const fetchCategories = async (projectId: number): Promise<string[]> => {
   // even though the original query might use a different locale, we are using "en" as locale deliberately
   // betterplace translates category slugs but we need stable identifiers, therefore we default to "en"
   const href =
     `https://api.betterplace.org/en/api_v4/projects/${projectId}/categories.json`;
   logger.info(`fetching project categories from ${href}`);
-  return fetch(href)
-    .then((result) => result.json())
-    .then((json) => json.data as Array<ApiProjectCategory>)
-    .then((categories) => categories.map(transformProjectCategory))
-    .catch((e) => {
-      logger.error(
-        `Error while fetching project categories from '${href}', using empty array to handle this (somewhat) gracefully`,
-        e,
-      );
-      return [];
-    });
+  try {
+    const response = await fetch(href);
+    const json = await response.json();
+    const categories = json.data as Array<ApiProjectCategory>;
+    return categories.map(transformProjectCategory);
+  } catch (e) {
+    logger.error(
+      `Error while fetching project categories from '${href}', using empty array to handle this (somewhat) gracefully`,
+      e,
+    );
+    return [];
+  }
 };
 
-export const fetchNews = (
+export const fetchNews = async (
   project: Project,
   parameters: NewsParameters,
 ): Promise<News[]> => {
@@ -165,38 +166,37 @@ export const fetchNews = (
     `?per_page=${parameters.limit || 5}&order=published_at:desc`;
   logger.info(`fetching blog posts from ${href}`);
 
-  return fetch(href)
-    .then((result) => result.json())
-    .then((json) => json.data as ApiBlogPost[])
-    .then((news) => news.map(transformNews))
-    .catch((e) => {
-      logger.error(
-        `Error while fetching blog posts from '${href}', using empty array to handle this (somewhat) gracefully`,
-        e,
-      );
-      return [];
-    });
+  try {
+    const response = await fetch(href);
+    const json = await response.json();
+    const news = json.data as ApiBlogPost[];
+    return news.map(transformNews);
+  } catch (e) {
+    logger.error(
+      `Error while fetching blog posts from '${href}', using empty array to handle this (somewhat) gracefully`,
+      e,
+    );
+    return [];
+  }
 };
 
-export const fetchInitiativeUrl = (
+export const fetchInitiativeUrl = async (
   initiative: Initiative,
 ): Promise<string | undefined> => {
   if (!initiative.url) {
     return Promise.resolve(initiative.url);
   }
-  return fetch(initiative.url)
-    .then((result) => result.json())
-    .then(
-      (organisation: ApiOrganisation) =>
-        organisation.links.find((l) => l.rel === "platform")?.href,
-    )
-    .catch((e) => {
-      logger.error(
-        `Error while fetching organisation url from '${initiative.url}', using api url to handle this (somewhat) gracefully`,
-        e,
-      );
-      return initiative.url;
-    });
+  try {
+    const response = await fetch(initiative.url);
+    const organisation: ApiOrganisation = await response.json();
+    return organisation.links.find((l) => l.rel === "platform")?.href;
+  } catch (e) {
+    logger.error(
+      `Error while fetching organisation url from '${initiative.url}', using api url to handle this (somewhat) gracefully`,
+      e,
+    );
+    return initiative.url;
+  }
 };
 
 function _debugP<In>(desc: string | undefined = undefined) {
@@ -228,35 +228,33 @@ const buildProjectsUrl = (
   return url;
 };
 
-const fetchPageOfProjects = (
+const fetchPageOfProjects = async (
   params: ProjectsParameters,
   language: BetterPlaceLanguage = DEFAULT_LANGUAGE,
 ): Promise<ProjectsResponse> => {
   const url = buildProjectsUrl(params, language);
   const start = Date.now();
   logger.info(`fetching projects from ${url}`);
-  return fetch(url)
-    .then((result) => result.json())
-    .then(transformProjectsResponse)
-    .then((result) => {
-      const duration = Date.now() - start;
-      logger.debug(`fetching projects took ${duration} ms`);
-      return result;
-    })
-    .catch((e) => {
-      const duration = Date.now() - start;
-      logger.error(
-        `Error performing request to ${url} after ${duration} ms: ${e.message}`,
-      );
-      throw e;
-    });
+  try {
+    const fetchResponse = await fetch(url);
+    const jsonResponse = await fetchResponse.json();
+    return transformProjectsResponse(jsonResponse);
+  } catch (e) {
+    logger.error(
+      `Error performing request to ${url}: ${e.message}`,
+    );
+    throw e;
+  } finally {
+    const duration = Date.now() - start;
+    logger.debug(`fetching projects took ${duration} ms`);
+  }
 };
 
 // Do not select projects from too far back
 const randomPageForToday = () =>
   (new Date().setUTCHours(0, 0, 0, 0) / 100000) % MAX_PAGE_FOR_RANDOMIZATION;
 
-export const fetchProjects = (
+export const fetchProjects = async (
   { offset = 0, limit = DEFAULT_PAGE_SIZE, location }: ProjectsParameters,
   language: BetterPlaceLanguage = DEFAULT_LANGUAGE,
 ): Promise<ProjectsResponse> => {
@@ -266,13 +264,14 @@ export const fetchProjects = (
   const isFirstPageOfUnfilteredList = offset < limit && !isFiltering;
   if (isFirstPageOfUnfilteredList) {
     logger.info(`fetching random projects from page ${randomPage}`);
-    return fetchPageOfProjects({
+    const projects = await fetchPageOfProjects({
       offset: randomPage * DEFAULT_PAGE_SIZE,
       limit: DEFAULT_PAGE_SIZE,
-    }, language).then((result) => ({
-      ...result,
-      data: result.data.slice(0, limit),
-    }));
+    }, language);
+    return ({
+      ...projects,
+      data: projects.data.slice(0, limit),
+    });
   }
   // After one randomly selected page, "normal" pagination starts with offset 0 and skips the random page that was already displayed
   const afterRandomPage = offset > randomPage * DEFAULT_PAGE_SIZE;
@@ -283,33 +282,29 @@ export const fetchProjects = (
   }, language);
 };
 
-export const fetchProject = (
+export const fetchProject = async (
   { id }: ProjectParameters,
   language: BetterPlaceLanguage = DEFAULT_LANGUAGE,
 ): Promise<Project> => {
   const url = `${BASE_URL}/${language}/api_v4/projects/${id}.json`;
   logger.info(`fetching project from ${url}`);
   const start = Date.now();
-  return fetch(url)
-    .then((response) => {
-      if (response.status === 404) {
-        throw new GraphQLError("Not found", {
-          extensions: { "code": "NOT_FOUND" },
-        });
-      }
-      return response.json();
-    })
-    .then(transformProject)
-    .then((result) => {
-      const duration = Date.now() - start;
-      logger.debug(`fetching project with ID ${id} took ${duration} ms`);
-      return result;
-    })
-    .catch((e) => {
-      const duration = Date.now() - start;
-      logger.error(
-        `Error performing request to ${url} after ${duration} ms: ${e.message}`,
-      );
-      throw e;
-    });
+  try {
+    const response = await fetch(url);
+    if (response.status === 404) {
+      throw new GraphQLError("Not found", {
+        extensions: { "code": "NOT_FOUND" },
+      });
+    }
+    const json = await response.json();
+    return transformProject(json);
+  } catch (e) {
+    logger.error(
+      `Error performing request to ${url}: ${e.message}`,
+    );
+    throw e;
+  } finally {
+    const duration = Date.now() - start;
+    logger.debug(`fetching project with ID ${id} took ${duration} ms`);
+  }
 };
diff --git a/app/common_test.ts b/app/common_test.ts
index cc23dcd7df1e0ea7a56332001639669df0aad7e5..479fce8e98c033ec2224f46cc464f33538f3c7dc 100644
--- a/app/common_test.ts
+++ b/app/common_test.ts
@@ -22,6 +22,7 @@ export const processGqlRequest = (
     headers: {
       "content-type": "application/json",
       "accept": "*/*",
+      ...headers,
     },
     body: JSON.stringify({
       operationName: null,
diff --git a/app/server.ts b/app/server.ts
index 7f830504cbc61cf49f5d7417fc2bdebf62c2abdb..ebc6f8470faad5bb3b8f5997e2a7cf76227a9edf 100644
--- a/app/server.ts
+++ b/app/server.ts
@@ -117,16 +117,20 @@ const createResolvers = (_config: ServerConfig) => ({
     ): Promise<ProjectsResponse> =>
       _config.fake
         ? Promise.resolve({ totalResults: 0, data: [] })
-        : fetchProjects(parameters, context.language),
+        : fetchProjects(
+          parameters,
+          getLanguage(context?.request?.headers),
+        ),
     project: (
       // deno-lint-ignore no-explicit-any
       _parent: any,
       parameters: ProjectParameters,
       context: GraphQLContext,
     ): Promise<Project> =>
-      _config.fake
-        ? Promise.resolve(fakeProject)
-        : fetchProject(parameters, context.language),
+      _config.fake ? Promise.resolve(fakeProject) : fetchProject(
+        parameters,
+        getLanguage(context?.request?.headers),
+      ),
   },
   Project: {
     categories: (args: Project): Promise<string[]> =>
@@ -151,23 +155,23 @@ export interface ServerConfig {
   fake: boolean; // For local development. If set, the API returns dummy data
 }
 
-const getLanguage = (languages = "") =>
-  languages
+const getLanguage = (headers?: Headers) => {
+  const languages: string = headers?.get("accept-language") || DEFAULT_LANGUAGE;
+  return (languages
     .split(",") // languages are comma separated
     .map((language) => language.split("-")[0]) // languages may include country code 'de-DE' e.g.
     .map((language) => language.split(";")[0]) // languages may include a 'de;q=0.6' e.g.
+    .map((language) => language.trim())
     .find((language) => SUPPORTED_LANGUAGES.includes(language)) ||
-  DEFAULT_LANGUAGE;
+    DEFAULT_LANGUAGE) as BetterPlaceLanguage;
+};
 
 export const createGraphQLServer = (config: ServerConfig): GraphQLServer => {
   const plugins = config.cacheEnabled
     ? [
       useResponseCache({
         // global cache per language, shared by all users
-        session: (request: Request) =>
-          getLanguage(
-            request.headers.get("accept-language") || DEFAULT_LANGUAGE,
-          ),
+        session: (request: Request) => getLanguage(request.headers),
         ttlPerSchemaCoordinate: {
           "Query.projects": config.cacheTtlMsBetterplace,
           "Query.project": config.cacheTtlMsBetterplace,
@@ -180,14 +184,6 @@ export const createGraphQLServer = (config: ServerConfig): GraphQLServer => {
     schema: createSchema({ resolvers, typeDefs }),
     graphiql: true,
     plugins,
-    context: (context: GraphQLContext) => {
-      const headers = new Headers(context.params?.extensions?.headers);
-      const languages = headers.get("accept-language") || DEFAULT_LANGUAGE;
-      return {
-        ...context,
-        language: getLanguage(languages),
-      };
-    },
   });
 };
 
@@ -195,6 +191,7 @@ export const createGraphQLServer = (config: ServerConfig): GraphQLServer => {
 export type GraphQLServer = any;
 
 type GraphQLContext = {
+  request?: { headers: Headers };
   params?: {
     extensions?: {
       headers?: {
@@ -202,12 +199,11 @@ type GraphQLContext = {
       };
     };
   };
-  language: BetterPlaceLanguage;
 };
 
 export const startServer = (config: ServerConfig): Promise<void> => {
   const graphQLServer: GraphQLServer = createGraphQLServer(config);
-  return serve(graphQLServer.handleRequest, {
+  return serve(graphQLServer, {
     port: config.port,
     onListen({ port, hostname }) {
       logger.info(
diff --git a/terraform/environments/deployment.tf b/terraform/environments/deployment.tf
index 3266c54b928e5a159f4e59af960710ccd80c0d90..bb0ce296880f262d63753e6d70b0aee5747c56bd 100644
--- a/terraform/environments/deployment.tf
+++ b/terraform/environments/deployment.tf
@@ -54,7 +54,7 @@ resource "google_cloud_run_service" "donations_api" {
         }
         env {
           name  = "CACHE_TTL_MS_BETTERPLACE"
-          value = local.environment == "production" ? "3600000" : "60000"
+          value = local.environment == "production" ? "3600000" : "3600000"
           # 1 hour for production, otherwise 1 minute
         }
 
@@ -65,7 +65,7 @@ resource "google_cloud_run_service" "donations_api" {
             memory = local.environment == "production" ? "512Mi" : "256Mi"
           }
           requests = {
-            cpu    = local.environment == "production" ? "1000m" : "10m"
+            cpu    = local.environment == "production" ? "1000m" : "1000m"
             memory = local.environment == "production" ? "512Mi" : "256Mi"
           }
         }