diff --git a/CMakeLists.txt b/CMakeLists.txt
index eb3340b0a9e3f8fe4786f3cd497c10edf225510b..e00824a7202102c136eb443f76196874aca64c44 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,8 +24,8 @@ set(libcaosdb_VERSION 0.0.4)
 
 project(libcaosdb
     VERSION ${libcaosdb_VERSION}
-    DESCRIPTION "C++ client libraries for CaosDB"
-    LANGUAGES CXX)
+    DESCRIPTION "C and C++ client libraries for CaosDB"
+    LANGUAGES CXX C)
 
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -116,28 +116,76 @@ add_custom_command(
         ${PROTO_FILES}
       DEPENDS ${PROTO_FILES})
 
+###############################################################################
+### Set up main targets
+### * [caosdb_grpc] - only in Debug builds. Otherwise this library is compiled
+###   into caosdb libraray
+### * caosdb (links to caosdb_grpc) - The main library.
+### * cxxcaosdbcli - A C++ test client.
+### * ccaosdb - A C-Wrapper of the C++ caosdb library.
+### * ccaosdbcli - A plain C test client.
+###############################################################################
 
 if("${CMAKE_BUILD_TYPE}" MATCHES "Debug")
     add_library(caosdb_grpc STATIC ${GRPC_GENERATED})
     add_library(caosdb STATIC ${libcaosdb_INCL} ${libcaosdb_SRC})
     target_link_libraries(caosdb caosdb_grpc)
     set(LIBCAOSDB caosdb caosdb_grpc)
+
+    target_include_directories(caosdb_grpc PUBLIC
+        $<BUILD_INTERFACE:${libcaosdb_SOURCE_DIR}/include>
+        $<BUILD_INTERFACE:${libcaosdb_BINARY_DIR}/include>
+
+        $<INSTALL_INTERFACE:include>
+        ${CONAN_INCLUDE_DIRS}
+    )
 else()
     add_library(caosdb
         STATIC ${libcaosdb_INCL} ${libcaosdb_SRC} ${GRPC_GENERATED})
     set(LIBCAOSDB caosdb)
 endif()
-add_executable(caosdbcli src/caosdbcli.cpp)
-
 target_link_libraries(caosdb
     ${CONAN_LIBS}
 )
+target_include_directories(caosdb PUBLIC
+    $<BUILD_INTERFACE:${libcaosdb_SOURCE_DIR}/include>
+    $<BUILD_INTERFACE:${libcaosdb_BINARY_DIR}/include>
+    $<INSTALL_INTERFACE:include>
+    ${CONAN_INCLUDE_DIRS}
+)
+
+add_library(ccaosdb STATIC src/ccaosdb.cpp)
+target_link_libraries(ccaosdb
+    ${LIBCAOSDB}
+    ${CONAN_LIBS}
+)
+
+add_executable(ccaosdbcli src/ccaosdbcli.c)
+target_include_directories(ccaosdbcli PUBLIC
+    $<BUILD_INTERFACE:${libcaosdb_SOURCE_DIR}/include>
+    $<BUILD_INTERFACE:${libcaosdb_BINARY_DIR}/include>
+    $<INSTALL_INTERFACE:include>
+    ${CONAN_INCLUDE_DIRS}
+)
+target_link_libraries(ccaosdbcli
+    ccaosdb
+    ${CONAN_LIBS}
+)
 
-target_link_libraries(caosdbcli
+add_executable(cxxcaosdbcli src/cxxcaosdbcli.cpp)
+target_include_directories(cxxcaosdbcli PUBLIC
+    $<BUILD_INTERFACE:${libcaosdb_SOURCE_DIR}/include>
+    $<BUILD_INTERFACE:${libcaosdb_BINARY_DIR}/include>
+    $<INSTALL_INTERFACE:include>
+    ${CONAN_INCLUDE_DIRS}
+)
+target_link_libraries(cxxcaosdbcli
     ${LIBCAOSDB}
     ${CONAN_LIBS}
 )
 
+
+
 #######################################################
 ### LINTING with CLANG-TIDY and INCLUDE-WHAT-YOU-USE
 #######################################################
@@ -165,9 +213,12 @@ if(_LINTING)
         set_target_properties(caosdb PROPERTIES
             CXX_INCLUDE_WHAT_YOU_USE "${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE}"
             )
-        set_target_properties(caosdbcli PROPERTIES
+        set_target_properties(cxxcaosdbcli PROPERTIES
             CXX_INCLUDE_WHAT_YOU_USE "${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE}"
             )
+        set_target_properties(ccaosdbcli PROPERTIES
+            C_INCLUDE_WHAT_YOU_USE "${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE}"
+            )
     endif()
 
     find_program(clang_tidy NAMES clang-tidy clang-tidy-11)
@@ -189,9 +240,12 @@ if(_LINTING)
         set_target_properties(caosdb PROPERTIES
             CXX_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY};${_CMAKE_CXX_CLANG_TIDY_CHECKS}"
             )
-        set_target_properties(caosdbcli PROPERTIES
+        set_target_properties(cxxcaosdbcli PROPERTIES
             CXX_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY};${_CMAKE_CXX_CLANG_TIDY_CHECKS}"
             )
+        set_target_properties(ccaosdbcli PROPERTIES
+            C_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY};${_CMAKE_CXX_CLANG_TIDY_CHECKS}"
+            )
     endif()
 endif()
 
@@ -212,32 +266,6 @@ endif()
 set(libcaosdb_INCLUDE_DEST "include/caosdb")
 set(libcaosdb_LIB_DEST "lib")
 
-target_include_directories(caosdb PUBLIC
-    # headers to include when building from source
-    $<BUILD_INTERFACE:${libcaosdb_SOURCE_DIR}/include>
-    $<BUILD_INTERFACE:${libcaosdb_BINARY_DIR}/include>
-
-    # headers to include when installing  (implicitly prefixes with ${CMAKE_INSTALL_PREFIX}).
-    $<INSTALL_INTERFACE:include>
-)
-
-if("${CMAKE_BUILD_TYPE}" MATCHES "Debug")
-    target_include_directories(caosdb_grpc PUBLIC
-        # headers to include when building from source
-        $<BUILD_INTERFACE:${libcaosdb_SOURCE_DIR}/include>
-        $<BUILD_INTERFACE:${libcaosdb_BINARY_DIR}/include>
-
-        # headers to include when installing  (implicitly prefixes with ${CMAKE_INSTALL_PREFIX}).
-        $<INSTALL_INTERFACE:include>
-     )
-endif()
-
-target_include_directories(caosdbcli PUBLIC
-   ${libcaosdb_SOURCE_DIR}/include>
-   ${libcaosdb_BINARY_DIR}/include>
-   ${CONAN_INCLUDE_DIRS}
-)
-
 set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/.local/")
 install(
     # targets to install
@@ -290,7 +318,10 @@ if(AUTOFORMATTING)
     file(GLOB format_test_sources test/*.cpp test/*.h)
     execute_process(COMMAND clang-format -i --verbose ${libcaosdb_INCL}
         ${libcaosdb_SRC} ${libcaosdb_TEST_SRC}
-        ${PROJECT_SOURCE_DIR}/src/caosdbcli.cpp
+        ${PROJECT_SOURCE_DIR}/src/cxxcaosdbcli.cpp
+        ${PROJECT_SOURCE_DIR}/src/ccaosdbcli.c
+        ${PROJECT_SOURCE_DIR}/src/ccaosdb.cpp
+        ${PROJECT_SOURCE_DIR}/include/ccaosdb.h
         ${format_test_sources}
         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
 endif()
diff --git a/include/caosdb/constants.h.in b/include/caosdb/constants.h.in
index 02c0341d17b77bbf843debea349af45b91516662..566cbb1c87e17f03a548c1e1386dc0ddb06decd4 100644
--- a/include/caosdb/constants.h.in
+++ b/include/caosdb/constants.h.in
@@ -22,11 +22,15 @@
 
 #ifndef CAOSDB_CONSTANTS_H
 #define CAOSDB_CONSTANTS_H
+#ifdef __cplusplus
 namespace caosdb {
+#endif
 // clang-format off
-constexpr int LIBCAOSDB_VERSION_MAJOR = @libcaosdb_VERSION_MAJOR@;
-constexpr int LIBCAOSDB_VERSION_MINOR = @libcaosdb_VERSION_MINOR@;
-constexpr int LIBCAOSDB_VERSION_PATCH = @libcaosdb_VERSION_PATCH@;
+const int LIBCAOSDB_VERSION_MAJOR = @libcaosdb_VERSION_MAJOR@;
+const int LIBCAOSDB_VERSION_MINOR = @libcaosdb_VERSION_MINOR@;
+const int LIBCAOSDB_VERSION_PATCH = @libcaosdb_VERSION_PATCH@;
 // clang-format on
+#ifdef __cplusplus
 } // namespace caosdb
 #endif
+#endif
diff --git a/include/caosdb/utils.h b/include/caosdb/utils.h
index 20aebbfe7c776f0323db3383d589f05c214d8e55..78d388a2e129820204420fbfc9f5148f9d772a00 100644
--- a/include/caosdb/utils.h
+++ b/include/caosdb/utils.h
@@ -21,14 +21,23 @@
 
 #ifndef CAOSDB_UTILS_H
 #define CAOSDB_UTILS_H
+#include <cassert>
 #include <iostream>
 #include <string_view>
 #include <fstream>
 #include <string>
 #include <cstdlib>
+#include <boost/json.hpp>
 #include <boost/beast/core/detail/base64.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
 
 namespace caosdb::utils {
+using boost::filesystem::exists;
+using boost::filesystem::ifstream;
+using boost::filesystem::path;
+using boost::json::stream_parser;
+using boost::json::value;
 
 /**
  * @brief Read a text file into a string and return the file's content.
@@ -50,18 +59,25 @@ inline auto load_string_file(const std::string &path) -> std::string {
   return result;
 }
 
+inline auto get_env_var(const char *key, const char *fall_back) -> const
+  char * {
+  const char *val = getenv(key);
+  if (val == nullptr) {
+    return fall_back;
+  } else {
+    return val;
+  }
+}
+
 /**
  * @brief Return the value of an environment variable or - if undefined - the
  * fall_back value.
  */
 inline auto get_env_var(const std::string &key, const std::string &fall_back)
-  -> std::string {
-  const char *val = getenv(key.c_str());
-  if (val == nullptr) {
-    return fall_back;
-  }
+  -> const std::string {
+  const char *val = get_env_var(key.c_str(), fall_back.c_str());
 
-  auto result = std::string(val);
+  auto const result = std::string(val);
   return result;
 }
 
@@ -79,5 +95,24 @@ inline auto base64_encode(const std::string &plain) -> std::string {
   return std::string(encoded, encoded + size_encoded);
 }
 
+inline auto load_json_file(const path &json_file) -> value {
+  assert(exists(json_file));
+
+  constexpr auto buffer_size = std::size_t(4096);
+  auto stream = ifstream(json_file);
+  stream.exceptions(std::ios_base::badbit);
+
+  stream_parser parser;
+  auto result = std::string();
+  auto buffer = std::string(buffer_size, '\0');
+  while (stream.read(&buffer[0], buffer_size)) {
+    parser.write(buffer.c_str(), stream.gcount());
+  }
+  parser.write(buffer.c_str(), stream.gcount());
+
+  assert(parser.done());
+  return parser.release();
+}
+
 } // namespace caosdb::utils
 #endif
diff --git a/include/ccaosdb.h b/include/ccaosdb.h
new file mode 100644
index 0000000000000000000000000000000000000000..382364f44f5fbdfabfed8b9b181ee5f94ca75787
--- /dev/null
+++ b/include/ccaosdb.h
@@ -0,0 +1,97 @@
+#ifdef __cplusplus
+extern "C" {
+#else
+#include <stdbool.h>
+#endif
+
+/**
+ * A wrapper of the C++ CaosDBConnection class.
+ *
+ * We use a wrapper for future extensibility and in order to have a minimal
+ * capability for type checking in C even though the C++ class
+ * CaosDBConnection is opaque in C.
+ */
+typedef struct {
+  void *wrapped_connection;
+} caosdb_connection_connection;
+
+/**
+ * A wrapper of the C++ CaosDBConnectionConfig class.
+ *
+ * We use a wrapper for future extensibility and in order to have a minimal
+ * capability for type checking in C even though the C++ class
+ * CaosDBConnection is opaque in C.
+ */
+typedef struct {
+  void *wrapped_connection_config;
+} caosdb_connection_connection_config;
+
+/**
+ * A wrapper of the C++ VersionInfo class.
+ *
+ * We use a wrapper for future extensibility and in order to have a minimal
+ * capability for type checking in C even though the C++ class
+ * CaosDBConnection is opaque in C.
+ */
+typedef struct {
+  void *wrapped_version_info;
+} caosdb_info_version_info;
+
+/**
+ * Return the environment variable of the given name.
+ *
+ * If the environment variable is not set, return the fall_back instead.
+ */
+const char *caosdb_utils_get_env_var(const char *name, const char *fall_back);
+
+/**
+ * Create a connection config.
+ *
+ * The config is needed to instantiate a connection.
+ *
+ * The config is ready to be used but you might want use
+ * caosdb_connection_config_add_cacert and
+ * caosdb_connection_config_add_plain_authenticator to specify more options of
+ * the connection.
+ */
+int caosdb_connection_create_config(caosdb_connection_connection_config *config,
+                                    const char *host, const int port,
+                                    const bool tls);
+
+/**
+ * Add a public certificate of a trusted certificate authority to an
+ * existing, tls-enabled connection configuration.
+ *
+ * @param cacert path to a pem-file.
+ */
+int caosdb_connection_config_add_cacert(
+  caosdb_connection_connection_config *config, const char *cacert);
+
+/**
+ * Add a plain text authenticator to an existing, tls enabled connection
+ * configuration.
+ */
+int caosdb_connection_config_add_plain_authenticator(
+  caosdb_connection_connection_config *config, const char *username,
+  const char *password);
+
+/**
+ * Create a connection instance.
+ *
+ * The connection is needed to create transactions and to initiate any other
+ * interaction with a CaosDB server.
+ */
+int caosdb_connection_create_connection(
+  caosdb_connection_connection *connection,
+  const caosdb_connection_connection_config *config);
+
+/**
+ * Request the version of the server.
+ */
+int caosdb_connection_get_version_info(
+  caosdb_info_version_info *version_info,
+  const caosdb_connection_connection *connection);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/ccaosdb.cpp b/src/ccaosdb.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..28ea242cb3093f34f5a52641e299966be2b7986e
--- /dev/null
+++ b/src/ccaosdb.cpp
@@ -0,0 +1,25 @@
+#include "caosdb/utils.h"
+#include "caosdb/constants.h"
+#include "ccaosdb.h"
+
+extern "C" {
+const int LIBCAOSDB_VERSION_MINOR = caosdb::LIBCAOSDB_VERSION_MAJOR;
+const int LIBCAOSDB_VERSION_MAJOR = caosdb::LIBCAOSDB_VERSION_MINOR;
+const int LIBCAOSDB_VERSION_PATCH = caosdb::LIBCAOSDB_VERSION_PATCH;
+
+const char *caosdb_utils_get_env_var(const char *name, const char *fall_back) {
+  return caosdb::utils::get_env_var(name, fall_back);
+}
+
+// int caosdb_connection_create_config(caosdb_connection_config *config, const
+// char *host, const int port, const bool tls);
+
+// int caosdb_connection_config_add_cacert(caosdb_connection_config *config,
+// const char *cacert);
+
+// int caosdb_connection_config_add_plain_authenticator(caosdb_connection_config
+// *config, const char *username, const char *password);
+
+// int caosdb_connection_create_connection(caosdb_connection *connection, const
+// caosdb_connection_config *config);
+}
diff --git a/src/ccaosdbcli.c b/src/ccaosdbcli.c
new file mode 100644
index 0000000000000000000000000000000000000000..3ba70bcc0d44f0076699fc4ca5fae3a76070fd34
--- /dev/null
+++ b/src/ccaosdbcli.c
@@ -0,0 +1,20 @@
+#include "ccaosdb.h"
+#include "caosdb/constants.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(void) {
+  printf(
+    "CaosDB C client (libcaosdb %d.%d.%d)\nWe don't miss the H of caos.\n\n",
+    LIBCAOSDB_VERSION_MAJOR, LIBCAOSDB_VERSION_MINOR, LIBCAOSDB_VERSION_PATCH);
+
+  const char *host =
+    caosdb_utils_get_env_var("CAOSDB_SERVER_HOST", "localhost");
+  const char *port_str = caosdb_utils_get_env_var("CAOSDB_SERVER_HOST", "8443");
+  char *end = NULL;
+  const int port = (int)strtol(port_str, &end, 10);
+
+  printf("Connecting to host: %s:%d\n", host, port);
+
+  return 0;
+}
diff --git a/src/caosdbcli.cpp b/src/cxxcaosdbcli.cpp
similarity index 89%
rename from src/caosdbcli.cpp
rename to src/cxxcaosdbcli.cpp
index a128887ef7acc5004c60ffba4dbb9da2033f95ae..443c071b0eeaf06c3e58d3bb114caa802a14468d 100644
--- a/src/caosdbcli.cpp
+++ b/src/cxxcaosdbcli.cpp
@@ -33,8 +33,8 @@
 #include "caosdb/transaction.h" // for Transaction, UniqueResult
 
 auto main() -> int {
-
-  std::cout << "CaosDB (libcaosdb " << caosdb::LIBCAOSDB_VERSION_MINOR << "."
+  std::cout << "CaosDB C++ client (libcaosdb "
+            << caosdb::LIBCAOSDB_VERSION_MINOR << "."
             << caosdb::LIBCAOSDB_VERSION_MINOR << "."
             << caosdb::LIBCAOSDB_VERSION_PATCH << ")\n"
             << "We don't miss the H of caos.\n"
@@ -42,13 +42,14 @@ auto main() -> int {
 
   const auto pem_file =
     caosdb::utils::get_env_var("CAOSDB_SERVER_CERT", std::string());
-  const auto host =
+  const auto *const host =
     caosdb::utils::get_env_var("CAOSDB_SERVER_HOST", "localhost");
-  const auto port_str =
+  const auto *const port_str =
     caosdb::utils::get_env_var("CAOSDB_SERVER_GRPC_PORT_HTTPS", "8443");
   const auto port = std::stoi(port_str);
-  const auto user = caosdb::utils::get_env_var("CAOSDB_USER", "admin");
-  const auto password = caosdb::utils::get_env_var("CAOSDB_PASSWORD", "caosdb");
+  const auto *const user = caosdb::utils::get_env_var("CAOSDB_USER", "admin");
+  const auto *const password =
+    caosdb::utils::get_env_var("CAOSDB_PASSWORD", "caosdb");
 
   // setup the connection
   auto auth =
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 2616296f18b66d16d1054da5e89bb3fa7b9921da..027307844c3baed8d1d62415cc91f44b3d362ba5 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -24,6 +24,7 @@ set(test_cases
     test_info
     test_transaction
     test_utils
+    test_ccaosdb
     )
 
 ###################################################
@@ -38,6 +39,7 @@ set(_CMAKE_CXX_CLANG_TIDY_TEST_CHECKS
 # add special cmake functions for gtest
 include(GoogleTest)
 
+
 # loop over all test cases and add them to the test runner
 list(LENGTH test_cases len_test_cases)
 math(EXPR len_test_cases "${len_test_cases} - 1")
@@ -47,9 +49,9 @@ foreach (i RANGE "${len_test_cases}")
     set(libcaosdb_TEST_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${test_case_name}.cpp
         ${libcaosdb_TEST_SRC}")
     target_link_libraries(${test_case_name}
-        PRIVATE ${LIBCAOSDB} ${CONAN_LIBS_GTEST} ${CONAN_LIBS_BOOST})
+        PRIVATE ${LIBCAOSDB} ccaosdb ${CONAN_LIBS_GTEST} ${CONAN_LIBS_BOOST})
     target_include_directories(${test_case_name}
-      PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+      PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
     if(_LINTING)
     set_target_properties(${test_case_name}
         PROPERTIES
@@ -57,10 +59,18 @@ foreach (i RANGE "${len_test_cases}")
         CXX_INCLUDE_WHAT_YOU_USE "${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE}")
     endif()
     gtest_discover_tests(${test_case_name}
+        WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
         PROPERTIES
             LABELS "caosdb-cpplib-unit-tests")
 endforeach ()
 
+# copy test data to build dir
+set(TEST_DATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test_data")
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/caosdb_test_utility.h.in
+  ${CMAKE_CURRENT_BINARY_DIR}/caosdb_test_utility.h)
+
+
+
 
 ###################################################
 ### Set up test coverage repor (Gcov + Lcov)
@@ -75,12 +85,12 @@ if (LCOV_PATH)
         NAME unit_test_coverage
         EXECUTABLE ctest -L caosdb-cpplib-unit-tests
         EXCLUDE "${CMAKE_BINARY_DIR}/*"
-        DEPENDENCIES caosdb ${test_cases}
+        DEPENDENCIES caosdb ccaosdb ${test_cases}
         LCOV_ARGS --rc lcov_branch_coverage=1 --no-external
         GENHTML_ARGS --rc lcov_branch_coverage=1
         )
     message(STATUS "Adding COMPILE_FLAGS for coverage: ${COVERAGE_COMPILER_FLAGS}")
-    set_target_properties(caosdb PROPERTIES
+    set_target_properties(caosdb ccaosdb PROPERTIES
         COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}")
 else ()
     message(WARNING "Could not generate code coverage report. Please install lcov.")
diff --git a/test/caosdb_test_utility.h b/test/caosdb_test_utility.h.in
similarity index 97%
rename from test/caosdb_test_utility.h
rename to test/caosdb_test_utility.h.in
index 981c458f86e7658d68e4a032eb8f1408fa655dda..8493a89522185d2aed5f3f2b4b61f93cc2004843 100644
--- a/test/caosdb_test_utility.h
+++ b/test/caosdb_test_utility.h.in
@@ -43,3 +43,5 @@
     },                                                                         \
     exeption_type)
 #endif
+
+const std::string TEST_DATA_DIR = "@TEST_DATA_DIR@";
diff --git a/test/test_ccaosdb.cpp b/test/test_ccaosdb.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bdec5b65be5ec61e705982c52c4741ff8b689b1a
--- /dev/null
+++ b/test/test_ccaosdb.cpp
@@ -0,0 +1,33 @@
+/*
+ *
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <gtest/gtest-message.h>   // for Message
+#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl
+#include <gtest/gtest_pred_impl.h> // for Test, TestInfo, EXPECT_EQ, TEST
+#include <string>                  // for allocator
+#include "ccaosdb.h"               // for caosdb_utils_get_env_var
+
+TEST(test_ccaosdb, test_get_env_var) {
+  const char *const some_var =
+    caosdb_utils_get_env_var("SOME_ENV_VAR", "fall-back");
+  EXPECT_EQ("fall-back", some_var);
+}
diff --git a/test/test_data/test.json b/test/test_data/test.json
new file mode 100644
index 0000000000000000000000000000000000000000..9f938f4520487219d62ef3b421c0fe938a709a55
--- /dev/null
+++ b/test/test_data/test.json
@@ -0,0 +1,8 @@
+{
+  "this": [ "is", "a", "test" ],
+  "it" : "tests",
+  "numbers": [ 1, 2, 3.3 ],
+  "null values": null,
+  "arrays and objects":
+    { "see?": [ true, false ] }
+}
diff --git a/test/test_utils.cpp b/test/test_utils.cpp
index 619caee06a4d1d6981e863058302ee15042f7b3e..c8b5dc84bd4db84f04c7ec84411a44cd82729673 100644
--- a/test/test_utils.cpp
+++ b/test/test_utils.cpp
@@ -20,17 +20,37 @@
  *
  */
 
-#include "caosdb/utils.h"
-#include <string>
-#include <gtest/gtest-message.h>
-#include <gtest/gtest-test-part.h>
-#include "gtest/gtest_pred_impl.h"
-#include <boost/beast/core/detail/base64.hpp>
+#include <gtest/gtest-message.h>              // for Message
+#include <gtest/gtest-test-part.h>            // for TestPartResult, SuiteA...
+#include <gtest/gtest_pred_impl.h>            // for Test, EXPECT_EQ, TestInfo
+#include <boost/beast/core/detail/base64.hpp> // for encoded_size
+#include <string>                             // for allocator, string, ope...
+#include "boost/json/object.hpp"              // for object
+#include "boost/json/value.hpp"               // for value
+#include "caosdb/utils.h"                     // for base64_encode, load_js...
+#include "caosdb_test_utility.h"              // for TEST_DATA_DIR
+#include "gmock/gmock-matchers.h"             // for ElementsAre, EXPECT_THAT
+
+namespace caosdb::utils {
+using ::testing::ElementsAre;
 
 TEST(test_utils, base64_encode) {
   auto test_plain = std::string("admin:caosdb");
   auto test_encoded = std::string("YWRtaW46Y2Fvc2Ri");
-  ASSERT_EQ(12, test_plain.size());
-  ASSERT_EQ(16, boost::beast::detail::base64::encoded_size(test_plain.size()));
-  ASSERT_EQ(test_encoded, caosdb::utils::base64_encode(test_plain));
+  EXPECT_EQ(12, test_plain.size());
+  EXPECT_EQ(16, boost::beast::detail::base64::encoded_size(test_plain.size()));
+  EXPECT_EQ(test_encoded, base64_encode(test_plain));
 }
+
+TEST(test_utils, test_load_json_file) {
+  auto json = load_json_file(TEST_DATA_DIR + "/test.json").as_object();
+
+  EXPECT_EQ(json["it"], "tests");
+  EXPECT_EQ(json["null values"], nullptr);
+  EXPECT_THAT(json["this"].as_array(), ElementsAre("is", "a", "test"));
+  EXPECT_THAT(json["numbers"].as_array(), ElementsAre(1, 2, 3.3));
+  auto sub = json["arrays and objects"].as_object();
+  EXPECT_THAT(sub["see?"].as_array(), ElementsAre(true, false));
+}
+
+} // namespace caosdb::utils