diff --git a/src/Entity.js b/src/Entity.js
new file mode 100644
index 0000000000000000000000000000000000000000..8ebdae9e7b54ec13b0ad5583d4645a1208663b2b
--- /dev/null
+++ b/src/Entity.js
@@ -0,0 +1,93 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2021 Florian Spreckelsen <f.spreckelsen@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/>.
+ */
+import {
+    api
+} from "./EntityApi";
+
+import {
+    Property
+} from "./Property";
+
+function _getRoleString(role) {
+    const roles = api.v1.EntityRole;
+    switch (role) {
+    case roles.ENTITY_ROLE_UNSPECIFIED:
+        return undefined;
+    case roles.ENTITY_ROLE_RECORD_TYPE:
+        return "RECORD_TYPE";
+    case roles.ENTITY_ROLE_RECORD:
+        return "RECORD";
+    case roles.ENTITY_ROLE_PROPERTY:
+        return "PROPERTY";
+    case roles.ENTITY_ROLE_FILE:
+        return "FILE";
+    default:
+        throw `Unknown role ${role}.`;
+    }
+}
+
+export class Entity {
+
+    constructor(protoEntity) {
+        this.wrappedEntity = protoEntity;
+    }
+
+    getDescription() {
+        return this.wrappedEntity.getDescription();
+    }
+
+    getId() {
+        return this.wrappedEntity.getId();
+    }
+
+    getName() {
+        return this.wrappedEntity.getName();
+    }
+
+    getParents() {
+        // TODO(fspreck): Use Parent objects here in the future.
+        return this.wrappedEntity.getParentsList();
+    }
+
+    getProperties() {
+        const protoProperties = this.wrappedEntity.getPropertiesList();
+        return protoProperties.map(prop => new Property(prop));
+    }
+
+    getProperty(name) {
+        const properties = this.getProperties();
+        if (properties) {
+            for (let prop of properties) {
+                if (prop.getName().toLowerCase() === name.toLowerCase()) {
+                    return prop;
+                }
+            }
+        }
+        return undefined;
+    }
+
+    getRole() {
+        return _getRoleString(this.wrappedEntity.getRole());
+    }
+
+    isRecord() {
+        return this.wrappedEntity.getRole() === api.v1.EntityRole.ENTITY_ROLE_RECORD;
+    }
+}
diff --git a/src/EntityApi.js b/src/EntityApi.js
index fd3455e31e7a8a4eb677e974f33fd7236287cebe..01c02029305940e8ec680eefb982f7ffb3d3790c 100644
--- a/src/EntityApi.js
+++ b/src/EntityApi.js
@@ -1,5 +1,4 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
@@ -17,8 +16,6 @@
  *
  * 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/>.
- *
- * ** end header
  */
 
 const api = {};
diff --git a/src/Property.js b/src/Property.js
new file mode 100644
index 0000000000000000000000000000000000000000..2fa7a5c232bc8bbbbb4fd6bd7cae1990f3199448
--- /dev/null
+++ b/src/Property.js
@@ -0,0 +1,94 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2021 Florian Spreckelsen <f.spreckelsen@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/>.
+ */
+import {
+    api
+} from "./EntityApi";
+
+function _getScalarValue(value) {
+    const valueCases = api.v1.ScalarValue.ScalarValueCase;
+    switch (value.getScalarValueCase()) {
+    case valueCases.SCALAR_VALUE_NOT_SET:
+        return undefined;
+    case valueCases.INTEGER_VALUE:
+        return value.getIntegerValue();
+    case valueCases.DOUBLE_VALUE:
+        return value.getDoubleValue();
+    case valueCases.BOOLEAN_VALUE:
+        return value.getBooleanValue();
+    case valueCases.STRING_VALUE:
+        return value.getStringValue();
+    case valueCases.SPECIAL_VALUE:
+        return value.getSpecialValue();
+    default:
+        throw `Unkown value type ${value.getScalarValueCase()}.`;
+    }
+}
+
+function _getListValue(values) {
+    return values.map(value => _getScalarValue(value));
+}
+
+export class Property {
+
+    constructor(protoProperty) {
+        this.wrappedProperty = protoProperty;
+    }
+
+    getName() {
+        return this.wrappedProperty.getName();
+    }
+
+    getId() {
+        return this.wrappedProperty.getId();
+    }
+
+    getUnit() {
+        return this.wrappedProperty.getUnit();
+    }
+
+    getValue() {
+        const wrappedValue = this.wrappedProperty.getValue();
+        if (wrappedValue === undefined) {
+            // Empty values are undefined regardless of data type
+            return undefined;
+        }
+        const valueCases = api.v1.Value.ValueCase;
+        switch (wrappedValue.getValueCase()) {
+        case valueCases.VALUE_NOT_SET:
+            return undefined;
+        case valueCases.SCALAR_VALUE:
+            return _getScalarValue(wrappedValue.getScalarValue());
+        case valueCases.LIST_VALUES:
+            return _getListValue(wrappedValue.getListValues().getValuesList());
+        default:
+            throw `Unknown value type ${wrappedValue.getValueCase()}.`;
+        }
+    }
+
+    isReference() {
+        const wrappedDataType = this.wrappedProperty.getDataType()
+        const dtypeCase = wrappedDataType.getDataTypeCase();
+        return (
+            (dtypeCase === api.v1.DataType.DataTypeCase.REFERENCE_DATA_TYPE) ||
+            (dtypeCase === api.v1.DataType.DataTypeCase.LIST_DATA_TYPE &&
+                wrappedDataType.getListDataType().getListDataTypeCase() === api.v1.ListDataType.ListDataTypeCase.REFERENCE_DATA_TYPE)
+        );
+    }
+}
diff --git a/src/TransactionService.js b/src/TransactionService.js
index 3d2e9ed8e826c6fb3f895a7ff341264edfa1e73b..cfe1328990d3327f6fb1acb7d9105290817990a3 100644
--- a/src/TransactionService.js
+++ b/src/TransactionService.js
@@ -1,5 +1,4 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
@@ -17,8 +16,6 @@
  *
  * 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/>.
- *
- * ** end header
  */
 import {
     api
diff --git a/src/index.js b/src/index.js
index 748dbd281fbe9ca7df8f06ef2de8488a3a89e924..3bf2d0d958637ce1a46eb8f4e26a3514e183ac0f 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,5 +1,4 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
@@ -17,9 +16,18 @@
  *
  * 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/>.
- *
- * ** end header
  */
+export {
+    Entity
+}
+from "./Entity";
+
+export {
+    Property
+}
+from "./Property";
+
 export {
     TransactionService
-} from "./TransactionService";
+}
+from "./TransactionService";