diff --git a/API/Functions.py b/API/Functions.py
index 4858e26a571deb8d84f81efbeb0aeaebae4732ca..88ca32cdf02a8461b6e8f7cc76d7d5e28ab059f1 100644
--- a/API/Functions.py
+++ b/API/Functions.py
@@ -5,6 +5,7 @@ from dotenv import load_dotenv
 
 
 def safe_url(query):
+    """ convert query to URL-safe style """
     load_dotenv()
     host = os.getenv('SERVER_HOST')
     return host + Parser.url_safe(query)
@@ -24,6 +25,7 @@ def get(query):
 
 
 def put(query, json_file):
+    """ PUT request """
     print("PUT: " + query)
     url = safe_url(query)
     req = requests.put(url, json=json_file)
@@ -32,6 +34,7 @@ def put(query, json_file):
 
 
 def post(query, json_file):
+    """ POST request """
     print("POST: " + query)
     url = safe_url(query)
     req = requests.post(url, json=json_file)
@@ -40,6 +43,7 @@ def post(query, json_file):
 
 
 def delete(query):
+    """ DELETE request """
     print("DELETE: " + query)
     url = safe_url(query)
     req = requests.delete(url)
diff --git a/Crawler/Scrape.py b/Crawler/Scrape.py
index a2f97aefca3dde45cec48686887942fcfbc52771..bb0c4a9a300a56ed1cf6d781071880358016993c 100644
--- a/Crawler/Scrape.py
+++ b/Crawler/Scrape.py
@@ -4,6 +4,7 @@ from DataBase import mongoDB as db
 
 
 def scrape_api(url_api, book_num_api, author_num_api):
+    """ Function called by other module to run the scraper """
     print('Request received, start scraping')
     book_dict_api = {}
     author_dict_api = {}
diff --git a/DataBase/JsonParser.py b/DataBase/JsonParser.py
index 2a5e5247d8663b0d08c0a921c5b79317307d0e26..fa805723b49854ee50c49e375bcbdc2daa4a0c5d 100644
--- a/DataBase/JsonParser.py
+++ b/DataBase/JsonParser.py
@@ -1,5 +1,6 @@
 
 def parse_book_dict_to_json(dictionary):
+    """ parse books dictionary to json file """
     item_list = list(dictionary.items())
     return_list = []
     for items in item_list:
diff --git a/DataBase/mongoDB.py b/DataBase/mongoDB.py
index ef437fce2c84554924a3b4a118cacd9010c77783..1072256e8e01d6fed2f7194bcafa4c1ce3901ebf 100644
--- a/DataBase/mongoDB.py
+++ b/DataBase/mongoDB.py
@@ -16,9 +16,9 @@ def get_db():
 def insert_document(docu, opt):
     db = get_db()
     if opt == 0:
-        records = db.test_books
+        records = db.books
     elif opt == 1:
-        records = db.test_authors
+        records = db.authors
     else:
         print("failed to get json file: wrong opt for selecting collection")
         return
@@ -34,9 +34,9 @@ def insert_dicts(dictionary, opt):
     """
     db = get_db()
     if opt == 0:
-        records = db.test_books
+        records = db.books
     elif opt == 1:
-        records = db.test_authors
+        records = db.authors
     else:
         print("failed to get json file: wrong opt for selecting collection")
         return
@@ -58,12 +58,13 @@ def update_dicts(opt, identifier, content):
     """
     db = get_db()
     if opt == 0:
-        records = db.test_books
+        records = db.books
     elif opt == 1:
-        records = db.test_authors
+        records = db.authors
     else:
         print("failed to get json file: wrong opt for selecting collection")
         return
+    print(content)
     result = records.update_one(
         identifier,
         {"$set": content},
@@ -82,9 +83,9 @@ def get_documents_json(opt, identifier):
     """
     db = get_db()
     if opt == 0:
-        records = db.test_books
+        records = db.books
     elif opt == 1:
-        records = db.test_authors
+        records = db.authors
     else:
         print("failed to get json file: wrong opt for selecting collection")
         return json.dumps({})
@@ -127,10 +128,10 @@ def clean(opt, identifier):
     """
     db = get_db()
     if opt == 0:
-        records = db.test_books
+        records = db.books
     elif opt == 1:
-        records = db.test_authors
+        records = db.authors
     else:
         print("failed to get json file: wrong opt for selecting collection")
-        return
-    records.delete_many(identifier)
+        return 0
+    return records.delete_many(identifier).deleted_count
diff --git a/ManualTest.md b/ManualTest.md
new file mode 100644
index 0000000000000000000000000000000000000000..5367e98eadd3dc95964d5575abcfc730de42efea
--- /dev/null
+++ b/ManualTest.md
@@ -0,0 +1,194 @@
+# GoodRead Crawler Manual Test
+
+This is a manual test plan for cs242-sp21 assignment2: GoodRead Crawler
+
+## System Requirements
+
+
+#### OS:
+The following OS are appropriate system for running the GoodRead Crawler application and testing.
+- Windows 7 or above
+- Mac OS X
+- Linux
+
+#### Testing Prerequisites
+Softwares/applications are required for running the test.
+
+- Python 3
+- Javascript
+- React JS
+
+
+## Scenes for Testing
+![1](./ManualTestPictures/1(1).png)
+
+## 1.GET function
+
+#### Test1.1
+- test content: 
+    Test GET exist book data 
+- expect result: 
+    Successfully get book data and showing in table 
+![2](./ManualTestPictures/1(5).png)
+
+#### Test1.2
+- test content: 
+     Test GET exist author data 
+- expect result: 
+    Successfully get author data and showing in table
+![3](./ManualTestPictures/1(4).png)
+
+#### Test1.3
+- test content: 
+     Test GET unexist book data 
+- expect result: 
+     Request failed
+![4](./ManualTestPictures/1(2).png)
+
+#### Test1.4
+- test content: 
+     Test GET unexist author data 
+- expect result: 
+     Request failed
+![5](./ManualTestPictures/1(3).png)
+
+#### Test1.5
+- test content: 
+     Test search exist book data with id
+- expect result: 
+     Successfully find the data and print in table
+![6](./ManualTestPictures/1(6).png)
+
+#### Test1.6
+- test content: 
+     Test search exist book data with flexible search condition
+- expect result: 
+     Successfully find the data and showing in table
+![7](./ManualTestPictures/1(7).png)
+
+#### Test1.7
+- test content: 
+     Test search unexist book data 
+- expect result: 
+     Request failed
+![8](./ManualTestPictures/1(8).png)
+
+#### Test1.8
+- test content: 
+     Test using scrape in GET command
+- expect result: 
+     Request failed
+![9](./ManualTestPictures/1(24).png)
+
+## 2.PUT function
+
+#### Test2.1
+- test content: 
+     Test PUT update data for exist book documentation
+- expect result: 
+     No new document is created, existing document updated
+![10](./ManualTestPictures/1(9).png)
+![11](./ManualTestPictures/1(10).png)
+![12](./ManualTestPictures/1(12).png)
+
+#### Test2.2
+- test content: 
+     Test PUT new book documentation
+- expect result: 
+     A new book document is created
+![13](./ManualTestPictures/1(13).png)
+![14](./ManualTestPictures/1(15).png)
+
+#### Test2.3
+- test content: 
+     Test PUT update data for exist author documentation
+- expect result: 
+     No new document is created, existing document updated
+![15](./ManualTestPictures/1(16).png)
+![16](./ManualTestPictures/1(17).png)
+![17](./ManualTestPictures/1(19).png)
+
+#### Test2.4
+- test content: 
+     Test PUT new author documentation
+- expect result: 
+     A new author document is created
+![18](./ManualTestPictures/1(20).png)
+![19](./ManualTestPictures/1(22).png)
+
+
+## 3.POST function
+
+#### Test3.1
+- test content: 
+     Test POST book data
+- expect result: 
+     Successfully post new book documtent
+![20](./ManualTestPictures/1(26).png)
+![21](./ManualTestPictures/1(28).png)
+
+
+#### Test3.2
+- test content: 
+     Test POST author data
+- expect result: 
+    Successfully post new author documtent
+![22](./ManualTestPictures/1(29).png)
+![23](./ManualTestPictures/1(30).png)
+
+#### Test3.3
+- test content: 
+      Test Scrape data
+- expect result: 
+     New book and author data is added into collections
+![24](./ManualTestPictures/1(23).png)
+![25](./ManualTestPictures/1(25).png)
+
+#### Test3.4
+- test content: 
+      Test the input form of book-type doc can be closed by click 'back' button and another form of author-type can be opened
+- expect result: 
+     Successfully changed the form
+![26](./ManualTestPictures/1(29).png)
+![27](./ManualTestPictures/1(31).png)
+
+## 4.DELETE function
+
+#### Test4.1
+- test content: 
+    Test DELETE book data by id
+- expect result: 
+    Successfully delete the book data
+![28](./ManualTestPictures/1(34).png)
+     
+#### Test4.2
+- test content: 
+    Test DELETE author data by name
+- expect result: 
+    Successfully delete the author data
+![29](./ManualTestPictures/1(37).png)
+
+#### Test4.3
+- test content: 
+    Test DELETE unexist data
+- expect result: 
+    Request failed
+![30](./ManualTestPictures/1(38).png)
+
+## 5.Responsive UI Test
+
+#### Test5.1
+- test content: 
+    Test main page can fit mobile device
+- expect result: 
+    All elements can be fitted in screen of mobile-device size
+![31](./ManualTestPictures/1(40).png)
+
+#### Test5.2
+- test content: 
+    Test data table can scroll
+- expect result: 
+    Data table can be scroll to right and left when the screen's size is not large enough
+![32](./ManualTestPictures/1(41).png)
+![33](./ManualTestPictures/1(42).png)
+![34](./ManualTestPictures/1(43).png)
diff --git a/ManualTestPictures/1(1).png b/ManualTestPictures/1(1).png
new file mode 100644
index 0000000000000000000000000000000000000000..bb437397c142ee4232d32d5026c6f3cbf5888883
Binary files /dev/null and b/ManualTestPictures/1(1).png differ
diff --git a/ManualTestPictures/1(10).png b/ManualTestPictures/1(10).png
new file mode 100644
index 0000000000000000000000000000000000000000..7f601e45013cb14660c7e375a3ac3eb9142bd6d8
Binary files /dev/null and b/ManualTestPictures/1(10).png differ
diff --git a/ManualTestPictures/1(11).png b/ManualTestPictures/1(11).png
new file mode 100644
index 0000000000000000000000000000000000000000..4c6a9f0907ced79d183ad8ebd9ce812cf3e52eb7
Binary files /dev/null and b/ManualTestPictures/1(11).png differ
diff --git a/ManualTestPictures/1(12).png b/ManualTestPictures/1(12).png
new file mode 100644
index 0000000000000000000000000000000000000000..893c8d7272f037899890293826e60a41db7dde28
Binary files /dev/null and b/ManualTestPictures/1(12).png differ
diff --git a/ManualTestPictures/1(13).png b/ManualTestPictures/1(13).png
new file mode 100644
index 0000000000000000000000000000000000000000..19d06b7b94e853ce18d13dad7e45fb10cd5ebdd8
Binary files /dev/null and b/ManualTestPictures/1(13).png differ
diff --git a/ManualTestPictures/1(14).png b/ManualTestPictures/1(14).png
new file mode 100644
index 0000000000000000000000000000000000000000..097f76faa00adf8d52dbaf2705a56d96858a9a02
Binary files /dev/null and b/ManualTestPictures/1(14).png differ
diff --git a/ManualTestPictures/1(15).png b/ManualTestPictures/1(15).png
new file mode 100644
index 0000000000000000000000000000000000000000..d72f119d0e141018f678922e1d8ede3063380706
Binary files /dev/null and b/ManualTestPictures/1(15).png differ
diff --git a/ManualTestPictures/1(16).png b/ManualTestPictures/1(16).png
new file mode 100644
index 0000000000000000000000000000000000000000..574b028828c023fa85d82525fa1f4dba6b95c41a
Binary files /dev/null and b/ManualTestPictures/1(16).png differ
diff --git a/ManualTestPictures/1(17).png b/ManualTestPictures/1(17).png
new file mode 100644
index 0000000000000000000000000000000000000000..a2abcd90a2b9e83d3210cf3ceb8c24cbdd429b45
Binary files /dev/null and b/ManualTestPictures/1(17).png differ
diff --git a/ManualTestPictures/1(18).png b/ManualTestPictures/1(18).png
new file mode 100644
index 0000000000000000000000000000000000000000..eb5050e53bb5d9955d7691435fbfbbfe91da5d8d
Binary files /dev/null and b/ManualTestPictures/1(18).png differ
diff --git a/ManualTestPictures/1(19).png b/ManualTestPictures/1(19).png
new file mode 100644
index 0000000000000000000000000000000000000000..e7292195a4b80ab5976fea51ee03aaeb41b652de
Binary files /dev/null and b/ManualTestPictures/1(19).png differ
diff --git a/ManualTestPictures/1(2).png b/ManualTestPictures/1(2).png
new file mode 100644
index 0000000000000000000000000000000000000000..9e5fb84266b77a39f1c7f2e4f0661645ed37afda
Binary files /dev/null and b/ManualTestPictures/1(2).png differ
diff --git a/ManualTestPictures/1(20).png b/ManualTestPictures/1(20).png
new file mode 100644
index 0000000000000000000000000000000000000000..419607853c30236feaae1967fa88b1603637f3db
Binary files /dev/null and b/ManualTestPictures/1(20).png differ
diff --git a/ManualTestPictures/1(21).png b/ManualTestPictures/1(21).png
new file mode 100644
index 0000000000000000000000000000000000000000..41ee7ea4168d90b873daccd1244317f9bbed33ef
Binary files /dev/null and b/ManualTestPictures/1(21).png differ
diff --git a/ManualTestPictures/1(22).png b/ManualTestPictures/1(22).png
new file mode 100644
index 0000000000000000000000000000000000000000..c6f6c4a09669d06a0f806fd4e9523f9ca027a8d5
Binary files /dev/null and b/ManualTestPictures/1(22).png differ
diff --git a/ManualTestPictures/1(23).png b/ManualTestPictures/1(23).png
new file mode 100644
index 0000000000000000000000000000000000000000..488a1dbca0b5be3eea3f4ccb176f4ed15a4fbd39
Binary files /dev/null and b/ManualTestPictures/1(23).png differ
diff --git a/ManualTestPictures/1(24).png b/ManualTestPictures/1(24).png
new file mode 100644
index 0000000000000000000000000000000000000000..f3637fb440cbef3b0d24d4e12607e99c1ca4a46a
Binary files /dev/null and b/ManualTestPictures/1(24).png differ
diff --git a/ManualTestPictures/1(25).png b/ManualTestPictures/1(25).png
new file mode 100644
index 0000000000000000000000000000000000000000..3ce602746194e20f7b4a55e07a945067c0ab3dab
Binary files /dev/null and b/ManualTestPictures/1(25).png differ
diff --git a/ManualTestPictures/1(26).png b/ManualTestPictures/1(26).png
new file mode 100644
index 0000000000000000000000000000000000000000..49d7ae60bc1eed4b8a3da127ea6cdbf70d8cd884
Binary files /dev/null and b/ManualTestPictures/1(26).png differ
diff --git a/ManualTestPictures/1(27).png b/ManualTestPictures/1(27).png
new file mode 100644
index 0000000000000000000000000000000000000000..ce54d5fdc51b64b6ff921d09364282446f562ed3
Binary files /dev/null and b/ManualTestPictures/1(27).png differ
diff --git a/ManualTestPictures/1(28).png b/ManualTestPictures/1(28).png
new file mode 100644
index 0000000000000000000000000000000000000000..b73b9f1dee6ce1558db4ac4dbf2532a6929abddf
Binary files /dev/null and b/ManualTestPictures/1(28).png differ
diff --git a/ManualTestPictures/1(29).png b/ManualTestPictures/1(29).png
new file mode 100644
index 0000000000000000000000000000000000000000..7aef2893769482716c76b20a6cbb5e930928c8ad
Binary files /dev/null and b/ManualTestPictures/1(29).png differ
diff --git a/ManualTestPictures/1(3).png b/ManualTestPictures/1(3).png
new file mode 100644
index 0000000000000000000000000000000000000000..e3b768c83fae0ec686e7cd12a9a56fa6652967b6
Binary files /dev/null and b/ManualTestPictures/1(3).png differ
diff --git a/ManualTestPictures/1(30).png b/ManualTestPictures/1(30).png
new file mode 100644
index 0000000000000000000000000000000000000000..5c382a26d3d031a1379196ef63a7292822b55f7c
Binary files /dev/null and b/ManualTestPictures/1(30).png differ
diff --git a/ManualTestPictures/1(31).png b/ManualTestPictures/1(31).png
new file mode 100644
index 0000000000000000000000000000000000000000..eea95251b868948ccddd0a3ae25fabbd56f0d47c
Binary files /dev/null and b/ManualTestPictures/1(31).png differ
diff --git a/ManualTestPictures/1(32).png b/ManualTestPictures/1(32).png
new file mode 100644
index 0000000000000000000000000000000000000000..e66d2d2573b6498276cf0727c9cb28891c7e1c40
Binary files /dev/null and b/ManualTestPictures/1(32).png differ
diff --git a/ManualTestPictures/1(33).png b/ManualTestPictures/1(33).png
new file mode 100644
index 0000000000000000000000000000000000000000..7e90af77068ae10d0e15d360387d0dec4ef6ef72
Binary files /dev/null and b/ManualTestPictures/1(33).png differ
diff --git a/ManualTestPictures/1(34).png b/ManualTestPictures/1(34).png
new file mode 100644
index 0000000000000000000000000000000000000000..060c442c3c9255f4d255444c54c69131d3df59e3
Binary files /dev/null and b/ManualTestPictures/1(34).png differ
diff --git a/ManualTestPictures/1(35).png b/ManualTestPictures/1(35).png
new file mode 100644
index 0000000000000000000000000000000000000000..630e4d96bdb97833e638e4b3ae9df2f3d41cce32
Binary files /dev/null and b/ManualTestPictures/1(35).png differ
diff --git a/ManualTestPictures/1(36).png b/ManualTestPictures/1(36).png
new file mode 100644
index 0000000000000000000000000000000000000000..ac972262e918c5b0263d3d2dd21e452db18ec0e1
Binary files /dev/null and b/ManualTestPictures/1(36).png differ
diff --git a/ManualTestPictures/1(37).png b/ManualTestPictures/1(37).png
new file mode 100644
index 0000000000000000000000000000000000000000..8364184f3e5a6db598602f6e5fd8bd8c4e98845b
Binary files /dev/null and b/ManualTestPictures/1(37).png differ
diff --git a/ManualTestPictures/1(38).png b/ManualTestPictures/1(38).png
new file mode 100644
index 0000000000000000000000000000000000000000..494bf6af8f566350b24729a5998f4f8c20a5c8ff
Binary files /dev/null and b/ManualTestPictures/1(38).png differ
diff --git a/ManualTestPictures/1(39).png b/ManualTestPictures/1(39).png
new file mode 100644
index 0000000000000000000000000000000000000000..67ec08f55f3fac0517964f1e9f89581d1567eda6
Binary files /dev/null and b/ManualTestPictures/1(39).png differ
diff --git a/ManualTestPictures/1(4).png b/ManualTestPictures/1(4).png
new file mode 100644
index 0000000000000000000000000000000000000000..c8ec3d1fe121f7618030d2fab73cc7dd7be92a52
Binary files /dev/null and b/ManualTestPictures/1(4).png differ
diff --git a/ManualTestPictures/1(40).png b/ManualTestPictures/1(40).png
new file mode 100644
index 0000000000000000000000000000000000000000..af02e1706318ac567fcaa791ff2444e56d4981aa
Binary files /dev/null and b/ManualTestPictures/1(40).png differ
diff --git a/ManualTestPictures/1(41).png b/ManualTestPictures/1(41).png
new file mode 100644
index 0000000000000000000000000000000000000000..0a94119d042088916124856d16130246f99d9353
Binary files /dev/null and b/ManualTestPictures/1(41).png differ
diff --git a/ManualTestPictures/1(42).png b/ManualTestPictures/1(42).png
new file mode 100644
index 0000000000000000000000000000000000000000..422f04cc1b1912d2c3b3351f1d0bacd880d7224a
Binary files /dev/null and b/ManualTestPictures/1(42).png differ
diff --git a/ManualTestPictures/1(43).png b/ManualTestPictures/1(43).png
new file mode 100644
index 0000000000000000000000000000000000000000..5e4cc792f4cbbdba448482f753f4f5c6fbdde3f9
Binary files /dev/null and b/ManualTestPictures/1(43).png differ
diff --git a/ManualTestPictures/1(5).png b/ManualTestPictures/1(5).png
new file mode 100644
index 0000000000000000000000000000000000000000..6eee82818c0b0e66bba706f0753c7448406db87f
Binary files /dev/null and b/ManualTestPictures/1(5).png differ
diff --git a/ManualTestPictures/1(6).png b/ManualTestPictures/1(6).png
new file mode 100644
index 0000000000000000000000000000000000000000..da05b568cbc3e62e77ad16751efdb8350e05cc5d
Binary files /dev/null and b/ManualTestPictures/1(6).png differ
diff --git a/ManualTestPictures/1(7).png b/ManualTestPictures/1(7).png
new file mode 100644
index 0000000000000000000000000000000000000000..39a8c55f2ea4ad98bfa080610253e1d037a89a84
Binary files /dev/null and b/ManualTestPictures/1(7).png differ
diff --git a/ManualTestPictures/1(8).png b/ManualTestPictures/1(8).png
new file mode 100644
index 0000000000000000000000000000000000000000..4e2021cc24ce9a54751302cb26c2e6edbcf67fe5
Binary files /dev/null and b/ManualTestPictures/1(8).png differ
diff --git a/ManualTestPictures/1(9).png b/ManualTestPictures/1(9).png
new file mode 100644
index 0000000000000000000000000000000000000000..f61bc9ce59d139f225148865f7a8d4624a159e27
Binary files /dev/null and b/ManualTestPictures/1(9).png differ
diff --git a/README.md b/README.md
index 52026e13ba87f3145b72067e2920db8322249ed8..127b886d46c6f0ab22ba73a4f5d81988a8ded974 100644
--- a/README.md
+++ b/README.md
@@ -3,12 +3,13 @@ This project is for sp21 CS242 assignment2
 
 Current version is 2.1
 
-###Function
+### Function
     1. Scrap data of books and authors from GoodReads
     2. Store scraped data on cloud or local files
     3. Query cloud for certain documents of books or authors
     
-###Composition:
-####Client: Command line interface (interactive)
-####Server: Simple server based on Flask
-####Database: MongoDB
\ No newline at end of file
+### Composition:
+#### Client: Command line interface (interactive)
+#### Server: Simple server based on Flask
+#### Database: MongoDB
+#### Website: Javascipt HTML with React
diff --git a/RegularExpressionParser/Parser.py b/RegularExpressionParser/Parser.py
index e361d2ff6b2141a2fa20dc133d29fc1bb631ec46..abb8e0299e6d4df202b168f895786afb818146d0 100644
--- a/RegularExpressionParser/Parser.py
+++ b/RegularExpressionParser/Parser.py
@@ -20,7 +20,7 @@ def parse_query_to_url(query):
     if count == 2:
         # can only be A.B:C or wrong
         if re.search("^[0-9a-zA-Z_.]+:[0-9a-zA-Z_\".]", query):
-            return url_safe(elements[0] + "%3A" + elements[1])
+            return url_safe(elements[0] + ":3A" + elements[1])
         else:
             print("Invalid query1.")
             return ""
@@ -48,6 +48,12 @@ def parse_query_to_url(query):
             return ""
 
 
+def parse_url_to_query(url):
+    query_str = url.replace("%20", " ").replace("%22", "\"").replace("%3C", "<").replace("%3E", ">").replace("%26", "&")
+    return query_str.replace(" AND ", "&AND&").replace(" OR ", "&OR&").replace(": ", ":").replace(" > ", ">")\
+        .replace(" < ", "<")
+
+
 def parse_query_to_json(pair):
     elements = re.findall("[0-9A-Za-z\"_.]+", pair)
     count = len(elements)
@@ -61,7 +67,7 @@ def parse_query_to_json(pair):
         else:
             return {elements[0].split(".")[1]: {"$not": {"$regex": elements[2], "$options": "i"}}}
     else:
-        # can be A.B: C or A.B: "C"
+        # can be A.B: C, A.B: "C", A.B > C, or A.B < C
         if re.search(":", pair):
             if re.search("^[0-9.]*$", elements[1]) and not re.search("id", elements[0]):
                 return {elements[0].split(".")[1]: float(elements[1])}
@@ -71,6 +77,8 @@ def parse_query_to_json(pair):
                 else:
                     return {elements[0].split(".")[1]: {"$regex": elements[1], "$options": "i"}}
         else:
+            if len(elements[0].split(".")) != 2:
+                return {"wrong": "True"}
             if re.search(">", pair):
                 return {elements[0].split(".")[1]: {"$gt": float(elements[1])}}
             elif re.search("<", pair):
diff --git a/Server/SimpleServer.py b/Server/SimpleServer.py
index 6507c3295f45f588090b8c0e48f0d714687611a0..58b59dd47753aba28f7ee03d0306ab0fa5aa99bf 100644
--- a/Server/SimpleServer.py
+++ b/Server/SimpleServer.py
@@ -2,99 +2,144 @@ import DataBase.mongoDB as DataBase
 import RegularExpressionParser.Parser as Parser
 import re
 import Crawler.Scrape as Scrape
+import json
 from flask import Flask
 from flask import request
 from flask import abort
 from flask import jsonify
 from urllib.parse import urlparse, parse_qs
+from flask_cors import CORS, cross_origin
 
 app = Flask(__name__)
-# app.config["DEBUG"] = True
+cors = CORS(app)
+# cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
 
-
-@app.route("/", methods=['GET'])
-def home():
-    """ homepage of server """
-    return "200: successfully connected to home page\n"
+@app.route("/api/book/", methods=["GET", "PUT", "POST", "DELETE", "OPTIONS"])
+def data_base_book():
+    print(request.url)
+    """ data base page of server """    
+    if request.method == "GET":
+        url_parsed = urlparse(request.url)
+        qs_parsed = parse_qs(url_parsed.query)
+        if qs_parsed == {}:
+            return DataBase.get_documents_json(0, {})
+        result = search_document(["book.id:" + qs_parsed["id"][0]])
+        if result == {"wrong": "True"}:
+            abort(400, "Bad Request")
+        elif result == json.dumps({'books': []}):
+            abort(400, "Book not found")
+        else:
+            return result
+    elif request.method == "PUT":
+        if request.headers["Content-Type"] != "application/json":
+            abort(415, "content should be JSON file")
+        print(request)
+        json_update_info = request.json
+        opt = 0
+        DataBase.update_dicts(opt, request.args.to_dict(), json_update_info)
+        return "200: PUT succeeded"
+    elif request.method == "POST":
+        if request.headers["Content-Type"] != "application/json":
+            abort(415, "content should be JSON file")
+        print(request)
+        json_file = request.json
+        DataBase.insert_document(json_file, 0)
+        return "200: POST succeeded"
+    elif request.method == "DELETE":
+        identifier = request.args.to_dict()
+        print(identifier)
+        opt=0
+        deleted_count = DataBase.clean(opt, identifier)
+        if deleted_count > 0:
+            return "200: DELETE succeeded"
+        else:
+            abort(400, "Failed to delete: target not found")
+    elif request.method == "OPTIONS":
+        return '200 OK'
 
 
-@app.route("/api/<collection>/", methods=["GET", "PUT", "POST", "DELETE"])
-def data_base(collection):
-    """ data base page of server """
-    print("\n===============================\n")
-    print(collection)
-    print("\n===============================\n")
+@app.route("/api/author/", methods=["GET", "PUT", "POST", "DELETE", "OPTIONS"])
+def data_base_author():
+    print(request.url)
+    """ data base page of server """    
     if request.method == "GET":
-        if collection == "book":
-            url_parsed = urlparse(request.url)
-            qs_parsed = parse_qs(url_parsed.query)
-            if qs_parsed == {}:
-                return jsonify(DataBase.get_documents_json(0, {}))
-            return jsonify(search_document(["book.id:" + qs_parsed["id"][0]]))
-        elif collection == "author":
-            url_parsed = urlparse(request.url)
-            qs_parsed = parse_qs(url_parsed.query)
-            if qs_parsed == {}:
-                return jsonify(DataBase.get_documents_json(1, {}))
-            return jsonify(search_document(["author.id:" + qs_parsed["id"][0]]))
-        elif collection == "search":
-            url_parsed = urlparse(request.url)
-            qs_parsed = parse_qs(url_parsed.query)
-            result = jsonify(search_document(qs_parsed["q"][0].split("&")))
-            return jsonify(result)
+        url_parsed = urlparse(request.url)
+        qs_parsed = parse_qs(url_parsed.query)
+        if qs_parsed == {}:
+            return DataBase.get_documents_json(1, {})
+        result = search_document(["author.id:" + qs_parsed["id"][0]])
+        if result == {"wrong": "True"}:
+            abort(400, "Bad Request")
+        elif result == json.dumps({'authors': []}):
+            abort(400, "Author not found")
         else:
-            abort(404)
+            return result
     elif request.method == "PUT":
         if request.headers["Content-Type"] != "application/json":
-            abort(415)
+            abort(415, "content should be JSON file")
         json_update_info = request.json
-        if collection == "book":
-            opt = 0
-        elif collection == "author":
-            opt = 1
-        else:
-            abort(404)
+        opt = 1
         DataBase.update_dicts(opt, request.args.to_dict(), json_update_info)
         return "200: PUT succeeded"
     elif request.method == "POST":
         if request.headers["Content-Type"] != "application/json":
             abort(415, "content should be JSON file")
         json_file = request.json
-        if collection == "books":
-            DataBase.insert_dicts(json_file, 0)
-        elif collection == "authors":
-            DataBase.insert_dicts(json_file, 1)
-        elif collection == "book":
-            DataBase.insert_document(json_file, 0)
-        elif collection == "author":
-            DataBase.insert_document(json_file, 1)
-        elif collection == "scrape":
-            param = request.args.to_dict()
-            url = param["url"]
-            max_book = param["max_book"]
-            max_author = param["max_author"]
-            Scrape.scrape_api(url, max_book, max_author)
-            return "200: new data has been added to database"
-        else:
-            abort(404)
+        DataBase.insert_document(json_file, 1)
         return "200: POST succeeded"
     elif request.method == "DELETE":
         identifier = request.args.to_dict()
         print(identifier)
-        if collection == "book":
-            opt = 0
-        elif collection == "author":
-            opt = 1
+        opt=1
+        deleted_count = DataBase.clean(opt, identifier)
+        if deleted_count > 0:
+            return "200: DELETE succeeded"
         else:
-            abort(404, "Unknown Collection to DELETE")
-        DataBase.clean(opt, identifier)
-        return "200: DELETE succeeded"
+            abort(400, "Failed to delete: target not found")
+    elif request.method == "OPTIONS":
+        return '200 OK'
+
 
+@app.route("/api/scrape/", methods=["GET", "PUT", "POST", "DELETE", "OPTIONS"])
+def data_base_scrape():
+    print(request.url)
+    param = request.args.to_dict()
+    url = param["url"]
+    max_book = param["max_book"]
+    max_author = param["max_author"]
+    Scrape.scrape_api(url, max_book, max_author)
+    return "200: new data has been added to database"
+
+
+@app.route("/api/search/", methods=["GET", "PUT", "POST", "DELETE", "OPTIONS"])
+def data_base_search():
+    print(request.url)
+    url_parsed = urlparse(request.url)
+    query = Parser.parse_url_to_query(url_parsed.query)
+    qs_parsed = query.replace("q=", "")
+    result = search_document(qs_parsed.split("&"))
+    if result == {"wrong": "True"}:
+        abort(400, "Bad Request")
+    elif result == json.dumps({'books': []}):
+        abort(400, "Target not found")
+    else:
+        return result
+
+# @app.route("/api/", methods=["GET", "PUT", "POST", "DELETE", "OPTIONS"])
+# def data_api():
+#     print('====================')
+#     print(request.url)
+#     print(request.collection)
+#     print('====================')
+
+#     return '200: nice!'
 
 def search_document(identifiers):
     """ function used to find one or several document in database """
     if len(identifiers) == 1:
         json_idt = Parser.parse_query_to_json(identifiers[0])
+        if json_idt == {"wrong": "True"}:
+            return {"wrong": "True"}
         print(json_idt)
         if re.search("^book.*", identifiers[0]):
             return DataBase.get_documents_json(0, json_idt)
@@ -115,6 +160,8 @@ def search_document(identifiers):
                 opt = 1
         json_idt1 = Parser.parse_query_to_json(identifiers[0])
         json_idt2 = Parser.parse_query_to_json(identifiers[2])
+        if json_idt1 == {"wrong": "True"} or json_idt2 == {"wrong": "True"}:
+            return {"wrong": "True"}
         if identifiers[1] == "AND":
             exp = {"$and": [json_idt1, json_idt2]}
         elif identifiers[1] == "OR":
@@ -126,7 +173,10 @@ def search_document(identifiers):
         print(exp)
         return DataBase.get_documents_json(opt, exp)
     else:
-        return "Error, unknown identifiers"
+        print("Error, unknown identifiers")
+        return {}
+
+
 
 
 if __name__ == "__main__":
diff --git a/Tests/ParserTests.py b/Tests/ParserTests.py
index a46915da4b4b7f4fd3fecb9b2544f6fe719bea2a..d1a5bb38f8799e7dd8c937be37aca75ea0d1ad6f 100644
--- a/Tests/ParserTests.py
+++ b/Tests/ParserTests.py
@@ -34,3 +34,7 @@ class DataBaseTests(unittest.TestCase):
         output_json = Parser.parse_query_to_json(query)
         self.assertEqual(output_json, expect_json)
 
+
+if __name__ == '__main__':
+    unittest.main()
+
diff --git a/Tests/ServerTests.py b/Tests/ServerTests.py
index 2bad5db37bf035d513efd4bd12e115425e2c8dfd..e0aff9964a5d8311bb3fb839a8da837c2c4aa6d7 100644
--- a/Tests/ServerTests.py
+++ b/Tests/ServerTests.py
@@ -44,7 +44,7 @@ class DataBaseTests(unittest.TestCase):
         db.insert_document(self.test_data1, 0)
         url = "http://127.0.0.1:5000/api/bookssss?id=38746485"
         res = requests.get(url)
-        self.assertEqual(res.status_code, 200)
+        self.assertEqual(res.status_code, 400)
 
     def test_valid_put(self):
         db.insert_document(self.test_data1, 0)
diff --git a/web/app/src/App.css b/web/app/src/App.css
new file mode 100644
index 0000000000000000000000000000000000000000..74b5e053450a48a6bdb4d71aad648e7af821975c
--- /dev/null
+++ b/web/app/src/App.css
@@ -0,0 +1,38 @@
+.App {
+  text-align: center;
+}
+
+.App-logo {
+  height: 40vmin;
+  pointer-events: none;
+}
+
+@media (prefers-reduced-motion: no-preference) {
+  .App-logo {
+    animation: App-logo-spin infinite 20s linear;
+  }
+}
+
+.App-header {
+  background-color: #282c34;
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  font-size: calc(10px + 2vmin);
+  color: white;
+}
+
+.App-link {
+  color: #61dafb;
+}
+
+@keyframes App-logo-spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
diff --git a/web/app/src/App.js b/web/app/src/App.js
new file mode 100644
index 0000000000000000000000000000000000000000..4fcf33981540bf93c2f72c8d19888402f7770e55
--- /dev/null
+++ b/web/app/src/App.js
@@ -0,0 +1,383 @@
+import React from 'react';
+import FourButtons from './Components/FourButtons';
+import { Dialog } from 'react-overlay-pack';
+import CorrectSign from './material/sign_correct.png';
+import ErrorSign from './material/sign_error.png';
+import ElementDisplay from './Components/ElementDisplay';
+
+
+function App() {
+    /* Activate Hooks */
+    const { useState } = React;
+
+    /* Initializing Hooks for component values showed in main page */
+    const [queryStr, setQueryStr] = useState('')
+    const [sign, setSign] = useState(CorrectSign)
+    const [text, setText] = useState('This area shows the result of requests..')
+    const [formState, setFormState] = useState('hide')
+    const [dialogState, setDialogState] = useState(false)
+    const [renderState, setRenderState] = useState(false)
+
+    /* initializing form-like input of bookData for PUT and POST */
+    const [bookURLState, setBookURLState] = useState('')
+    const [bookTitleState, setBookTitleState] = useState('')
+    const [bookIDState, setBookIDState] = useState('')
+    const [bookISBNState, setBookISBNState] = useState('')
+    const [bookAuthorState, setBookAuthorState] = useState('')
+    const [bookAuthorURLState, setBookAuthorURLState] = useState('')
+    const [bookRatingState, setBookRatingState] = useState('')
+    const [bookRatingCountState, setBookRatingCountState] = useState('')
+    const [bookReviewCountState, setBookReviewCountState] = useState('')
+    const [bookImageURLState, setBookImageURLState] = useState('')
+    const [bookSimilarBooksState, setBookSimilarBooksState] = useState('')
+
+    /* Initializing form-like input of authorData for PUT AND POST */
+    const [authorNameState, setAuthorNameState] = useState('')
+    const [authorURLState, setAuthorURLState] = useState('')
+    const [authorIDState, setAuthorIDState] = useState('')
+    const [authorRatingState, setAuthorRatingState] = useState('')
+    const [authorRatingCountState, setAuthorRatingCountState] = useState('')
+    const [authorReviewCountState, setAuthorReviewCountState] = useState('')
+    const [authorImageURLState, setAuthorImageURLState] = useState('')
+    const [authorRelatedAuthorsState, setAuthorRelatedAuthorsState] = useState('')
+    const [authorAuthorBooksState, setAuthorAuthorBooksState] = useState('')
+
+    /* Hooks used to show whether the data passed in is bookData or authorData */
+    const [dataState, setDataState] = useState({})
+
+    /* Activating axios */
+    const axios = require('axios').default;
+    var center = {display: 'flex', justifyContent: 'center', alignItems: 'center'}
+
+    /** Function used in GET Requests */
+    function get() {
+        axios.get('http://127.0.0.1:5000/api/' + queryStr)
+        .then(function (response) {
+            // handle success
+            setSign(CorrectSign)
+            showDialog()
+            document.getElementById('dialog').value = response.status + ":\n" + response.statusText
+            if (queryStr.includes('book')) {
+                setDataState(response.data['books'][0])
+            } else {
+                setDataState(response.data['authors'][0])
+            }
+            setRenderState(true)
+        })
+        .catch(function (error) {
+            // handle error
+            setSign(ErrorSign)
+            showDialog()
+            document.getElementById('dialog').value = error //.response.status + ":\n" + error.response.statusText
+        })
+    }
+
+    /** Function used in PUT and POST: extract user input of bookData from the form and parse into JSON */
+    function createJson() {
+        var bookData = {}
+        bookData.type = 'book' 
+        /* Set corresponding value if the input is not empty */
+        if(bookURLState !== '') {bookData.book_url = bookURLState}
+        if(bookTitleState !== '') {bookData.title = bookTitleState}
+        if(bookIDState !== '') {bookData.id = bookIDState}
+        if(bookISBNState !== '') {bookData.ISBN = bookISBNState}
+        if(bookAuthorURLState !== '') {bookData.author_url = bookAuthorURLState}
+        if(bookAuthorState !== '') {bookData.author = bookAuthorState}
+        if(bookRatingState !== '') {bookData.rating = parseFloat(bookRatingState)}
+        if(bookRatingCountState !== '') {bookData.rating_count = parseInt(bookRatingCountState)}
+        if(bookReviewCountState !== '') {bookData.review_count = parseInt(bookReviewCountState)}
+        if(bookSimilarBooksState !== '') {bookData.similar_books = bookSimilarBooksState.split(', ')}
+        setDataState(bookData)
+        return bookData
+    }
+    
+    /** Function used in PUT and POST: extract user input of authorData from the form and parse into JSON */
+    function createJsonAuthor() {
+        var authorData = {}
+        authorData.type = 'author' 
+        /* Set corresponding value if the input is not empty */
+        if(authorNameState !== '') {authorData.name = authorNameState}
+        if(authorURLState !== '') {authorData.author_url = authorURLState}
+        if(authorIDState !== '') {authorData.id = authorIDState}
+        if(authorRatingState !== '') {authorData.rating = parseFloat(authorRatingState)}
+        if(authorRatingCountState !== '') {authorData.rating_count = parseInt(authorRatingCountState)}
+        if(authorReviewCountState !== '') {authorData.review_count = parseInt(authorReviewCountState)}
+        if(authorImageURLState !== '') {authorData.image_url = authorImageURLState}
+        if(authorRelatedAuthorsState !== '') {authorData.related_authors = authorRelatedAuthorsState.split(', ')}
+        if(authorAuthorBooksState !== '') {authorData.author_books = authorAuthorBooksState.split(',')}
+        setDataState(authorData)
+        return authorData
+    }
+    
+    /** Function used to hide the input form */
+    function hideForm() {
+        setFormState('hide')
+    }
+
+    /** Function used to show bookData input form */
+    function changeFormStateBook() {
+        setRenderState(false)
+        if (formState === 'hide') {
+            setFormState('showBook')
+        } else {
+            setFormState('hide')
+        }
+    }
+
+    /** Function used to show authorData input form */
+    function changeFormStateAuthor() {
+        setRenderState(false)
+        if (formState === 'hide') {
+            setFormState('showAuthor')
+        } else {
+            setFormState('hide')
+        }
+    }
+    
+    /** Function used to set Dialog visible, which is called when a response is received */
+    function showDialog() {
+        setDialogState(true)
+    }
+
+    /** Function used in PUT requests */
+    function put() {
+        setRenderState(false)
+        hideForm()
+        var data;
+        if (queryStr.includes('book')) {
+            data = createJson()
+        } else {
+            data = createJsonAuthor()
+        }
+        let config = {
+            headers: {
+                'Content-Type': 'application/json',
+            }
+        }
+        axios.put('http://127.0.0.1:5000/api/' + queryStr, data, config)
+        .then(function (response) {
+            // handle success
+            setSign(CorrectSign)
+            showDialog()
+            document.getElementById('dialog').value = response.status + ":\n" + response.statusText
+        })
+        .catch(function (error) {
+            // handle error
+            setSign(ErrorSign)
+            showDialog()
+            document.getElementById('dialog').value = error //.response.status + ":\n" + error.response.statusText
+        })
+    }
+    
+    /** Function used in POST requests */
+    function post() {
+        setRenderState(false)
+        let config = {
+            headers: {
+                'Content-Type': 'application/json',
+            }
+        }          
+        hideForm()
+        var data;
+        if (queryStr.includes('book')) {
+            data = createJson()
+        } else {
+            data = createJsonAuthor()
+        }
+        axios.post('http://127.0.0.1:5000/api/' + queryStr, JSON.stringify(data), config)
+        .then(function (response) {
+            // handle success
+            setSign(CorrectSign)
+            showDialog()
+            document.getElementById('dialog').value = response.status + ":\n" + response.statusText
+        })
+        .catch(function (error) {
+            // handle error
+            setSign(ErrorSign);
+            showDialog();
+            document.getElementById('dialog').value = error //.response.status + ":\n" + error.response.statusText
+        })
+    }
+    
+    /** Function used in DELETE requests */
+    function delete_data() {
+        setRenderState(false)
+        axios.delete('http://127.0.0.1:5000/api/' + queryStr)
+        .then(function (response) {
+            // handle success
+            setSign(CorrectSign)
+            showDialog()
+            document.getElementById('dialog').value = response.status + ":\n" + response.statusText
+        })
+        .catch(function (error) {
+            // handle error
+            setSign(ErrorSign)
+            showDialog()
+            document.getElementById('dialog').value = error //.response.status + ":\n" + error.response.statusText
+        })
+    }
+    
+    /** HTML code */
+    return (
+        <div>
+            {/* Dialog used when received response from server */}
+            {dialogState === true &&
+            <Dialog  //Warning, not responsive!
+                show={dialogState}
+                onOutsideClick={() => setDialogState(false)}>
+                    <div style={{marginTop:'20%'}}>
+                        <div style={{
+                                    marginLeft: '35%'
+                                }}>
+                            <img alt='responseStatus' src={sign}/>
+                        </div>
+                        <div>
+                            <textarea id='dialog' 
+                                readOnly={true}
+                                rows="3" 
+                                cols="38"
+                                style={{
+                                    border:'2px solid silver', 
+                                    backgroundColor: 'white', 
+                                    fontSize: '200%',
+                                    margin: '50px'
+                                }}>
+                                {text}
+                            </textarea>
+                        </div>
+                    </div>
+            </Dialog>}
+                <h1 style={center}> 
+                    Welcome to the home page of GoodReads Crawler! </h1>
+                <h3 style={center}> Please input your query string:</h3>
+                <div style={center}>
+                    <input 
+                        id='queryString' 
+                        type='text' 
+                        placeholder='example: book/?id=12345678' 
+                        size='40' 
+                        value={queryStr}
+                        onChange={(e) => setQueryStr(e.target.value)}
+                    />
+                </div>
+                <h3 style={center}> Please input your data for uploading (only effective for POST and PUT):</h3>
+                {/* Buttons used to select book or author data to be passed to PUT/POST */}
+                {formState === 'hide' && 
+                    <div style={center}>
+                        <button style={{width:60}} onClick={changeFormStateBook}>
+                            book
+                        </button>
+                        <button style={{marginLeft:10, width:60}} onClick={changeFormStateAuthor}>
+                            author
+                        </button>
+                    </div>}
+                {/* Form-like input for bookData */}
+                {formState === 'showBook' &&
+                    <form style={{border:'2px solid silver', marginLeft:'180px', marginRight: '180px'}}>
+                        <div style={center}>
+                            <input id='bookURL' style={center} type='text' size='40' value={bookURLState} placeholder='bookURL' onChange={(e) => setBookURLState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='bookTtile' style={center} type='text' size='40' value={bookTitleState} placeholder='bookTtile' onChange={(e) => setBookTitleState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='bookID' type='text' size='40' value={bookIDState} placeholder='bookID' onChange={(e) => setBookIDState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='bookISBN' type='text' size='40' value={bookISBNState} placeholder='bookISBN' onChange={(e) => setBookISBNState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='bookAuthorURL' type='text' size='40' value={bookAuthorURLState} placeholder='bookAuthorURL' onChange={(e) => setBookAuthorURLState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='bookAuthor' type='text' size='40' value={bookAuthorState} placeholder='bookAuthor' onChange={(e) => setBookAuthorState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='bookRating' type='text' size='40' value={bookRatingState} placeholder='bookRating' onChange={(e) => setBookRatingState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='bookRatingCount' type='text' size='40' value={bookRatingCountState} placeholder='bookRatingCount' onChange={(e) => setBookRatingCountState(e.target.value)}/>
+                            </div>
+                        <div style={center}>    
+                            <input id='bookReviewCount' type='text' size='40' value={bookReviewCountState} placeholder='bookReviewCount' onChange={(e) => setBookReviewCountState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='bookImageURL' type='text' size='40' value={bookImageURLState} placeholder='bookImageURL' onChange={(e) => setBookImageURLState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='bookSimilarBooks' type='text' size='40' value={bookSimilarBooksState} placeholder='bookSimilarBooks' onChange={(e) => setBookSimilarBooksState(e.target.value)}/>
+                            </div>
+                    </form>
+                }
+                {/* Form-like input for authorData */}
+                {formState === 'showAuthor' &&
+                    <form style={{border:'2px solid silver', marginLeft:'180px', marginRight: '180px'}}>
+                        <div style={center}>
+                            <input id='authorName' style={center} type='text' size='40' value={authorNameState} placeholder='authorName' onChange={(e) => setAuthorNameState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='authorURL' style={center} type='text' size='40' value={authorURLState} placeholder='authorURL' onChange={(e) => setAuthorURLState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='authorID' type='text' size='40' value={authorIDState} placeholder='authorID' onChange={(e) => setAuthorIDState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='authorRating' type='text' size='40' value={authorRatingState} placeholder='authorRating' onChange={(e) => setAuthorRatingState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='authorRatingCount' type='text' size='40' value={authorRatingCountState} placeholder='authorRatingCount' onChange={(e) => setAuthorRatingCountState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='authorReviewCount' type='text' size='40' value={authorReviewCountState} placeholder='authorReviewCount' onChange={(e) => setAuthorReviewCountState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='authorImageURL' type='text' size='40' value={authorImageURLState} placeholder='authorImageURL' onChange={(e) => setAuthorImageURLState(e.target.value)}/>
+                            </div>
+                        <div style={center}>
+                            <input id='authorRelatedAuthors' type='text' size='40' value={authorRelatedAuthorsState} placeholder='authorRelatedAuthors' onChange={(e) => setAuthorRelatedAuthorsState(e.target.value)}/>
+                            </div>
+                        <div style={center}>    
+                            <input id='authorBooks' type='text' size='40' value={authorAuthorBooksState} placeholder='authorBooks' onChange={(e) => setAuthorAuthorBooksState(e.target.value)}/>
+                            </div>
+                    </form>
+                }
+                {formState !== 'hide' &&
+                    <button style={{marginLeft:600, width:60}} onClick={hideForm}> back </button>
+                }
+            <div style={{marginTop:20, display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
+                {/* Fout buttons for GET PUT POST and DELETE */}
+                <FourButtons 
+                    backColor='blue' 
+                    borderColor='RoyalBlue'
+                    leftMargin={0}
+                    text='GET'
+                    func={get} />
+                <FourButtons 
+                    backColor='black' 
+                    borderColor='gray'
+                    leftMargin={20}
+                    text='PUT'
+                    func={put} />
+                <FourButtons 
+                    backColor='green' 
+                    borderColor='seagreen'
+                    leftMargin={20}
+                    text='POST'
+                    func={post} />
+                <FourButtons 
+                    backColor='red' 
+                    borderColor='indianred'
+                    leftMargin={20}
+                    text='DELETE'
+                    func={delete_data} />
+            </div>
+            {renderState === true &&
+                <div>
+                    {/* Data Table used to render response json data */}
+                    <ElementDisplay data={dataState}/>
+                </div>}
+        </div>
+    );
+}
+
+export default App;
\ No newline at end of file
diff --git a/web/app/src/App.test.js b/web/app/src/App.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..1f03afeece5ac28064fa3c73a29215037465f789
--- /dev/null
+++ b/web/app/src/App.test.js
@@ -0,0 +1,8 @@
+import { render, screen } from '@testing-library/react';
+import App from './App';
+
+test('renders learn react link', () => {
+  render(<App />);
+  const linkElement = screen.getByText(/learn react/i);
+  expect(linkElement).toBeInTheDocument();
+});
diff --git a/web/app/src/Components/ElementDisplay.js b/web/app/src/Components/ElementDisplay.js
new file mode 100644
index 0000000000000000000000000000000000000000..a8d53ad28d9a3f4119ce123dba91f21eb2ae6eaf
--- /dev/null
+++ b/web/app/src/Components/ElementDisplay.js
@@ -0,0 +1,25 @@
+import MUIDataTable from "mui-datatables";
+
+/** MUI dataTable component, used for rendering received response */
+const ElementDisplay = (props) => {
+
+    var fields = Object.keys(props.data)
+    var values = Object.values(props.data)
+    console.log(Object.values(props.data))
+    console.log(Object.keys(props.data))
+    const columns = fields
+    const data = [values]
+    const options = {
+        'responsive':'vertical'
+    }
+    return (
+        <MUIDataTable 
+            title={"GET Data"}
+            data={data}
+            columns={columns}
+            options={options}
+        />
+    )
+}
+
+export default ElementDisplay
diff --git a/web/app/src/Components/FourButtons.js b/web/app/src/Components/FourButtons.js
new file mode 100644
index 0000000000000000000000000000000000000000..e51eb2c582fc27602beb94d57e0c29e37d672e86
--- /dev/null
+++ b/web/app/src/Components/FourButtons.js
@@ -0,0 +1,21 @@
+/** Button Component for GET PUT POST DELETE */
+const FourButtons = ({backColor, borderColor, leftMargin, text, func}) => {
+    return (
+        <button 
+            style={{
+                width:70,
+                height:30,
+                backgroundColor:backColor, 
+                color:'white',
+                marginLeft:leftMargin, 
+                borderLeftWidth:4, 
+                borderTopWidth:3,
+                borderColor:borderColor,
+                borderBottomWidth:3}} 
+            className='btn' 
+            onClick={func}> {text}
+        </button>
+    )
+}
+
+export default FourButtons
diff --git a/web/app/src/index.css b/web/app/src/index.css
new file mode 100644
index 0000000000000000000000000000000000000000..ec2585e8c0bb8188184ed1e0703c4c8f2a8419b0
--- /dev/null
+++ b/web/app/src/index.css
@@ -0,0 +1,13 @@
+body {
+  margin: 0;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+    sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+    monospace;
+}
diff --git a/web/app/src/index.js b/web/app/src/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..ef2edf8ea3fc42258464231e29140c8723458c1e
--- /dev/null
+++ b/web/app/src/index.js
@@ -0,0 +1,17 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import './index.css';
+import App from './App';
+import reportWebVitals from './reportWebVitals';
+
+ReactDOM.render(
+  <React.StrictMode>
+    <App />
+  </React.StrictMode>,
+  document.getElementById('root')
+);
+
+// If you want to start measuring performance in your app, pass a function
+// to log results (for example: reportWebVitals(console.log))
+// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
+reportWebVitals();
diff --git a/web/app/src/material/sign_correct.png b/web/app/src/material/sign_correct.png
new file mode 100644
index 0000000000000000000000000000000000000000..f4c62915fc0b2c590da99790c4369a3e73f75cfe
Binary files /dev/null and b/web/app/src/material/sign_correct.png differ
diff --git a/web/app/src/material/sign_error.png b/web/app/src/material/sign_error.png
new file mode 100644
index 0000000000000000000000000000000000000000..52c8cb30694f970a86ed6baf14741e1a03183ca1
Binary files /dev/null and b/web/app/src/material/sign_error.png differ
diff --git a/web/app/src/reportWebVitals.js b/web/app/src/reportWebVitals.js
new file mode 100644
index 0000000000000000000000000000000000000000..5253d3ad9e6be6690549cb255f5952337b02401d
--- /dev/null
+++ b/web/app/src/reportWebVitals.js
@@ -0,0 +1,13 @@
+const reportWebVitals = onPerfEntry => {
+  if (onPerfEntry && onPerfEntry instanceof Function) {
+    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
+      getCLS(onPerfEntry);
+      getFID(onPerfEntry);
+      getFCP(onPerfEntry);
+      getLCP(onPerfEntry);
+      getTTFB(onPerfEntry);
+    });
+  }
+};
+
+export default reportWebVitals;
diff --git a/web/app/src/setupTests.js b/web/app/src/setupTests.js
new file mode 100644
index 0000000000000000000000000000000000000000..8f2609b7b3e0e3897ab3bcaad13caf6876e48699
--- /dev/null
+++ b/web/app/src/setupTests.js
@@ -0,0 +1,5 @@
+// jest-dom adds custom jest matchers for asserting on DOM nodes.
+// allows you to do things like:
+// expect(element).toHaveTextContent(/react/i)
+// learn more: https://github.com/testing-library/jest-dom
+import '@testing-library/jest-dom';