diff --git a/backend/Pipfile.lock b/backend/Pipfile.lock
index f95df1d107fa5850f49deb004b1a789d3b80f5c1..b351a390990d9030f12ffe9bf396760161d4e84e 100644
--- a/backend/Pipfile.lock
+++ b/backend/Pipfile.lock
@@ -183,25 +183,25 @@
         },
         "numpy": {
             "hashes": [
-                "sha256:03e311b0a4c9f5755da7d52161280c6a78406c7be5c5cc7facfbcebb641efb7e",
-                "sha256:0cdd229a53d2720d21175012ab0599665f8c9588b3b8ffa6095dd7b90f0691dd",
-                "sha256:312bb18e95218bedc3563f26fcc9c1c6bfaaf9d453d15942c0839acdd7e4c473",
-                "sha256:464b1c48baf49e8505b1bb754c47a013d2c305c5b14269b5c85ea0625b6a988a",
-                "sha256:5adfde7bd3ee4864536e230bcab1c673f866736698724d5d28c11a4d63672658",
-                "sha256:7724e9e31ee72389d522b88c0d4201f24edc34277999701ccd4a5392e7d8af61",
-                "sha256:8d36f7c53ae741e23f54793ffefb2912340b800476eb0a831c6eb602e204c5c4",
-                "sha256:910d2272403c2ea8a52d9159827dc9f7c27fb4b263749dca884e2e4a8af3b302",
-                "sha256:951fefe2fb73f84c620bec4e001e80a80ddaa1b84dce244ded7f1e0cbe0ed34a",
-                "sha256:9588c6b4157f493edeb9378788dcd02cb9e6a6aeaa518b511a1c79d06cbd8094",
-                "sha256:9ce8300950f2f1d29d0e49c28ebfff0d2f1e2a7444830fbb0b913c7c08f31511",
-                "sha256:be39cca66cc6806652da97103605c7b65ee4442c638f04ff064a7efd9a81d50a",
-                "sha256:c3ab2d835b95ccb59d11dfcd56eb0480daea57cdf95d686d22eff35584bc4554",
-                "sha256:eb0fc4a492cb896346c9e2c7a22eae3e766d407df3eb20f4ce027f23f76e4c54",
-                "sha256:ec0c56eae6cee6299f41e780a0280318a93db519bbb2906103c43f3e2be1206c",
-                "sha256:f4e4612de60a4f1c4d06c8c2857cdcb2b8b5289189a12053f37d3f41f06c60d0"
-            ],
-            "index": "pypi",
-            "version": "==1.17.0"
+                "sha256:03f2ebcbffcce2dec8860633b89a93e80c6a239d21a77ae8b241450dc21e8c35",
+                "sha256:078c8025da5ab9e8657edc9c2a1e9642e06e953bc7baa2e65c1aa9d9dfb7e98b",
+                "sha256:0fbfa98c5d5c3c6489cc1e852ec94395d51f35d9ebe70c6850e47f465038cdf4",
+                "sha256:1c841033f4fe6801648180c3033c45b3235a8bbd09bc7249010f99ea27bb6790",
+                "sha256:2c0984a01ddd0aeec89f0ce46ef21d64761048cd76c0074d0658c91f9131f154",
+                "sha256:4c166dcb0fff7cb3c0bbc682dfb5061852a2547efb6222e043a7932828c08fb5",
+                "sha256:8c2d98d0623bd63fb883b65256c00454d5f53127a5a7bcdaa8bdc582814e8cb4",
+                "sha256:8cb4b6ae45aad6d26712a1ce0a3f2556c5e1484867f9649e03496e45d6a5eba4",
+                "sha256:93050e73c446c82065b7410221b07682e475ac51887cd9368227a5d944afae80",
+                "sha256:a3f6b3024f8826d8b1490e6e2a9b99e841cd2c375791b1df62991bd8f4c00b89",
+                "sha256:bede70fd8699695363f39e86c1e869b2c8b74fb5ef135a67b9e1eeebff50322a",
+                "sha256:c304b2221f33489cd15a915237a84cdfe9420d7e4d4828c78a0820f9d990395c",
+                "sha256:f11331530f0eff69a758d62c2461cd98cdc2eae0147279d8fc86e0464eb7e8ca",
+                "sha256:fa5f2a8ef1e07ba258dc07d4dd246de23ef4ab920ae0f3fa2a1cc5e90f0f1888",
+                "sha256:fb6178b0488b0ce6a54bc4accbdf5225e937383586555604155d64773f6beb2b",
+                "sha256:fd5e830d4dc31658d61a6452cd3e842213594d8c15578cdae6829e36ad9c0930"
+            ],
+            "index": "pypi",
+            "version": "==1.17.1"
         },
         "packaging": {
             "hashes": [
@@ -213,23 +213,24 @@
         },
         "pandas": {
             "hashes": [
-                "sha256:074a032f99bb55d178b93bd98999c971542f19317829af08c99504febd9e9b8b",
-                "sha256:20f1728182b49575c2f6f681b3e2af5fac9e84abdf29488e76d569a7969b362e",
-                "sha256:2745ba6e16c34d13d765c3657bb64fa20a0e2daf503e6216a36ed61770066179",
-                "sha256:32c44e5b628c48ba17703f734d59f369d4cdcb4239ef26047d6c8a8bfda29a6b",
-                "sha256:3b9f7dcee6744d9dcdd53bce19b91d20b4311bf904303fa00ef58e7df398e901",
-                "sha256:544f2033250980fb6f069ce4a960e5f64d99b8165d01dc39afd0b244eeeef7d7",
-                "sha256:58f9ef68975b9f00ba96755d5702afdf039dea9acef6a0cfd8ddcde32918a79c",
-                "sha256:9023972a92073a495eba1380824b197ad1737550fe1c4ef8322e65fe58662888",
-                "sha256:914341ad2d5b1ea522798efa4016430b66107d05781dbfe7cf05eba8f37df995",
-                "sha256:9d151bfb0e751e2c987f931c57792871c8d7ff292bcdfcaa7233012c367940ee",
-                "sha256:b932b127da810fef57d427260dde1ad54542c136c44b227a1e367551bb1a684b",
-                "sha256:cfb862aa37f4dd5be0730731fdb8185ac935aba8b51bf3bd035658111c9ee1c9",
-                "sha256:de7ecb4b120e98b91e8a2a21f186571266a8d1faa31d92421e979c7ca67d8e5c",
-                "sha256:df7e1933a0b83920769611c5d6b9a1bf301e3fa6a544641c6678c67621fe9843"
-            ],
-            "index": "pypi",
-            "version": "==0.25.0"
+                "sha256:18d91a9199d1dfaa01ad645f7540370ba630bdcef09daaf9edf45b4b1bca0232",
+                "sha256:3f26e5da310a0c0b83ea50da1fd397de2640b02b424aa69be7e0784228f656c9",
+                "sha256:4182e32f4456d2c64619e97c58571fa5ca0993d1e8c2d9ca44916185e1726e15",
+                "sha256:426e590e2eb0e60f765271d668a30cf38b582eaae5ec9b31229c8c3c10c5bc21",
+                "sha256:5eb934a8f0dc358f0e0cdf314072286bbac74e4c124b64371395e94644d5d919",
+                "sha256:717928808043d3ea55b9bcde636d4a52d2236c246f6df464163a66ff59980ad8",
+                "sha256:8145f97c5ed71827a6ec98ceaef35afed1377e2d19c4078f324d209ff253ecb5",
+                "sha256:8744c84c914dcc59cbbb2943b32b7664df1039d99e834e1034a3372acb89ea4d",
+                "sha256:c1ac1d9590d0c9314ebf01591bd40d4c03d710bfc84a3889e5263c97d7891dee",
+                "sha256:cb2e197b7b0687becb026b84d3c242482f20cbb29a9981e43604eb67576da9f6",
+                "sha256:d4001b71ad2c9b84ff18b182cea22b7b6cbf624216da3ea06fb7af28d1f93165",
+                "sha256:d8930772adccb2882989ab1493fa74bd87d47c8ac7417f5dd3dd834ba8c24dc9",
+                "sha256:dfbb0173ee2399bc4ed3caf2d236e5c0092f948aafd0a15fbe4a0e77ee61a958",
+                "sha256:eebfbba048f4fa8ac711b22c78516e16ff8117d05a580e7eeef6b0c2be554c18",
+                "sha256:f1b21bc5cf3dbea53d33615d1ead892dfdae9d7052fa8898083bec88be20dcd2"
+            ],
+            "index": "pypi",
+            "version": "==0.25.1"
         },
         "psycopg2": {
             "hashes": [
@@ -964,11 +965,11 @@
         },
         "zipp": {
             "hashes": [
-                "sha256:4970c3758f4e89a7857a973b1e2a5d75bcdc47794442f2e2dd4fe8e0466e809a",
-                "sha256:8a5712cfd3bb4248015eb3b0b3c54a5f6ee3f2425963ef2a0125b8bc40aafaec"
+                "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e",
+                "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"
             ],
             "index": "pypi",
-            "version": "==0.5.2"
+            "version": "==0.6.0"
         }
     }
 }
diff --git a/backend/metagenedb/api/catalog/views/function.py b/backend/metagenedb/api/catalog/views/function.py
index 12bd29c48ee178506748b1c1c29604620eaa3e05..1419970bc4a932914ac98f18d62734afafffbb07 100644
--- a/backend/metagenedb/api/catalog/views/function.py
+++ b/backend/metagenedb/api/catalog/views/function.py
@@ -1,4 +1,6 @@
+from rest_framework import status
 from rest_framework.viewsets import ModelViewSet
+from rest_framework.response import Response
 
 from metagenedb.api.catalog.filters import FunctionFilter
 from metagenedb.apps.catalog.models import Function
@@ -10,3 +12,13 @@ class FunctionViewSet(ModelViewSet):
     serializer_class = FunctionSerializer
     lookup_field = 'function_id'
     filterset_class = FunctionFilter
+
+    def create(self, request, *args, **kwargs):
+        if isinstance(request.data, list):
+            serializer = self.get_serializer(data=request.data, many=True)
+        else:
+            serializer = self.get_serializer(data=request.data)
+        serializer.is_valid(raise_exception=True)
+        self.perform_create(serializer)
+        headers = self.get_success_headers(serializer.data)
+        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
diff --git a/backend/metagenedb/apps/catalog/serializers/bulk_list.py b/backend/metagenedb/apps/catalog/serializers/bulk_list.py
new file mode 100644
index 0000000000000000000000000000000000000000..6bd4f26807e9ee1bd626320b0c084c88efa8500d
--- /dev/null
+++ b/backend/metagenedb/apps/catalog/serializers/bulk_list.py
@@ -0,0 +1,28 @@
+from rest_framework import serializers
+from rest_framework.utils import model_meta
+
+
+class BulkListSerializer(serializers.ListSerializer):
+
+    def _extract_many_to_many(self, validated_data, info):
+        many_to_many = [{} for v in validated_data]
+        for field_name, relation_info in info.relations.items():
+            if relation_info.to_many:
+                for data_item, many_to_many_item in zip(validated_data, many_to_many):
+                    print(many_to_many_item, data_item)
+                    if field_name in data_item:
+                        many_to_many_item[field_name] = data_item.pop(field_name)
+                    else:
+                        print("missing key")
+                    print(many_to_many_item, data_item)
+        return many_to_many
+
+    def create(self, validated_data):
+        ModelClass = self.Meta.model
+        instances = ModelClass.objects.bulk_create(
+            [ModelClass(**item) for item in validated_data]
+        )
+        return instances
+
+    class Meta:
+        model = NotImplemented
\ No newline at end of file
diff --git a/backend/metagenedb/apps/catalog/serializers/function.py b/backend/metagenedb/apps/catalog/serializers/function.py
index 48cc43510f21750246df097a242e13f92fe69ad3..120c604db0fea01e76e45178870026df7caf34a8 100644
--- a/backend/metagenedb/apps/catalog/serializers/function.py
+++ b/backend/metagenedb/apps/catalog/serializers/function.py
@@ -1,10 +1,18 @@
 from rest_framework import serializers
 from metagenedb.apps.catalog.models import Function
 
+from .bulk_list import BulkListSerializer
+
+
+class FunctionListSerializer(BulkListSerializer):
+
+    class Meta:
+        model = Function
+
 
 class FunctionSerializer(serializers.ModelSerializer):
-    name = serializers.CharField(required=False)
 
     class Meta:
         model = Function
+        list_serializer_class = FunctionListSerializer
         fields = ('function_id', 'source', 'name')
diff --git a/backend/metagenedb/apps/catalog/serializers/test_bulk_list.py b/backend/metagenedb/apps/catalog/serializers/test_bulk_list.py
new file mode 100644
index 0000000000000000000000000000000000000000..e1d396865e3f168775ba5b98624906a0ee76a61a
--- /dev/null
+++ b/backend/metagenedb/apps/catalog/serializers/test_bulk_list.py
@@ -0,0 +1,55 @@
+from copy import deepcopy
+from unittest import TestCase
+from unittest.mock import Mock
+from rest_framework import serializers
+
+from metagenedb.apps.catalog.serializers.bulk_list import BulkListSerializer
+
+
+class BulkListSerializerTestExtractManyToMany(BulkListSerializer):
+    """
+    overload to skip __init__() and just test the _extract_many_to_many method.
+    """
+
+    def __init__(self):
+        pass
+
+
+class TestExtractManyToMany(TestCase):
+
+    def setUp(self):
+        self.data = [
+            {'field1': 'value1', 'field2': 'value2'},
+            {'field1': 'value3', 'field2': 'value4'}
+        ]
+        self.bulk_list_serializer = BulkListSerializerTestExtractManyToMany()
+        self.info = Mock()
+        
+
+    def test_extract_many_to_many(self):
+        self.info.relations = {
+            'field1': Mock(to_many=True),
+            'field2': Mock(to_many=False)
+        }
+        ori_list = deepcopy(self.data)
+        expected_list = [
+            {'field1': 'value1'},
+            {'field1': 'value3'}
+        ]
+        tested_list = self.bulk_list_serializer._extract_many_to_many(self.data, self.info)
+        self.assertListEqual(tested_list, expected_list)
+        self.assertNotEqual(ori_list, self.data)
+
+    def test_extract_no_many_to_many(self):
+        self.info.relations = {
+            'field1': Mock(to_many=False),
+            'field2': Mock(to_many=False)
+        }
+        ori_list = deepcopy(self.data)
+        expected_list = [
+            {},
+            {}
+        ]
+        tested_list = self.bulk_list_serializer._extract_many_to_many(self.data, self.info)
+        self.assertListEqual(tested_list, expected_list)
+        self.assertListEqual(ori_list, self.data)