{"info":{"_postman_id":"8fcbd36a-1ac7-4800-8eb8-59cda5c86b5e","name":"Retrofit Web Server (Kotlin & Node.js)","description":"<html><head></head><body><p>This documentation provides details for the RESTful API created using Node.js and Express. The API includes endpoints for creating, reading, updating, and deleting items.</p>\n<p>Creating a web server with node and express. The in client side of android with Kotlin used retrofit for HTTP connection with server.</p>\n<p>Github Repository : <a href=\"https://github.com/abtaaahi/Retrofit-Web-Server-Kotlin-Node.js\">https://github.com/abtaaahi/Retrofit-Web-Server-Kotlin-Node.js</a></p>\n<h3 id=\"base-url\">Base URL</h3>\n<p><code>http://localhost:1113</code></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-kotlin\">dependencies {\n    implementation(\"com.squareup.retrofit2:retrofit:2.9.0\")\n    implementation(\"com.squareup.retrofit2:converter-gson:2.9.0\")\n    implementation(\"com.squareup.okhttp3:logging-interceptor:4.9.0\")\n}\n\n</code></pre>\n<p>Create a <code>ApiService</code> for RESTful API:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-kotlin\">import retrofit2.Call\nimport retrofit2.http.*\ninterface ApiService {\n    @GET(\"get\")\n    fun getItems(): Call&lt;List&lt;Item&gt;&gt;\n    @GET(\"get/{id}\")\n    fun getItem(@Path(\"id\") id: Int): Call&lt;Item&gt;\n    @POST(\"post\")\n    fun createItem(@Body item: Item): Call&lt;Item&gt;\n    @PUT(\"update/{id}\")\n    fun updateItem(@Path(\"id\") id: Int, @Body item: Item): Call&lt;Item&gt;\n    @DELETE(\"delete/{id}\")\n    fun deleteItem(@Path(\"id\") id: Int): Call&lt;Void&gt;\n}\n\n</code></pre>\n<p>Create a data class <code>Item</code> :</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-kotlin\">data class Item (\n    val id: Int = 0,\n    val name: String,\n    val course: String\n)\n\n</code></pre>\n<p>Initialize Retrofit in a singleton</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-kotlin\">import retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\nimport okhttp3.OkHttpClient\nimport okhttp3.logging.HttpLoggingInterceptor\nobject RetrofitInstance {\n    private const val BASE_URL = \"http://192.168.1.13:1113/\"\n    private val loggingInterceptor = HttpLoggingInterceptor().apply {\n        setLevel(HttpLoggingInterceptor.Level.BODY)\n    }\n    private val client = OkHttpClient.Builder()\n        .addInterceptor(loggingInterceptor)\n        .build()\n    val retrofit: Retrofit by lazy {\n        Retrofit.Builder()\n            .baseUrl(BASE_URL)\n            .client(client)\n            .addConverterFactory(GsonConverterFactory.create())\n            .build()\n    }\n    val apiService: ApiService by lazy {\n        retrofit.create(ApiService::class.java)\n    }\n}\n\n</code></pre>\n<p><code>MainActivity.kt</code></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-kotlin\">import android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport retrofit2.Call\nimport retrofit2.Callback\nimport retrofit2.Response\nimport android.widget.Button\nimport android.widget.EditText\nimport android.widget.TextView\nclass MainActivity : AppCompatActivity() {\n    private val apiService = RetrofitInstance.apiService\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.activity_main)\n        btnGet.setOnClickListener { getItem() }\n        btnPost.setOnClickListener { createItem() }\n        btnPut.setOnClickListener { updateItem() }\n        btnDelete.setOnClickListener { deleteItem() }\n    }\n    private fun getItem() {\n        val idText = etId.text.toString()\n        if (idText.isEmpty()) {\n            apiService.getItems().enqueue(object : Callback&lt;List&lt;Item&gt;&gt; {\n                override fun onResponse(call: Call&lt;List&lt;Item&gt;&gt;, response: Response&lt;List&lt;Item&gt;&gt;) {\n                    if (response.isSuccessful) {\n                        val items = response.body() ?: emptyList()\n                        tvResult.text = if (items.isNotEmpty()) {\n                            \"${items.joinToString(separator = \"\\n\") { \"${it.id}: ${it.name}, ${it.course}\" }}\"\n                        } else {\n                            \"No items found.\"\n                        }\n                    } else {\n                        tvResult.text = \"GET ALL Error: ${response.code()}\"\n                    }\n                }\n                override fun onFailure(call: Call&lt;List&lt;Item&gt;&gt;, t: Throwable) {\n                    tvResult.text = \"GET ALL Failure: ${t.message}\"\n                }\n            })\n        } else {\n            val id = idText.toIntOrNull()\n            if (id != null) {\n                apiService.getItem(id).enqueue(object : Callback&lt;Item&gt; {\n                    override fun onResponse(call: Call&lt;Item&gt;, response: Response&lt;Item&gt;) {\n                        if (response.isSuccessful) {\n                            tvResult.text = \"${response.body()?.let { \"${it.id}: ${it.name}, ${it.course}\" }}\"\n                        } else {\n                            tvResult.text = \"GET Error: ${response.code()}\"\n                        }\n                    }\n                    override fun onFailure(call: Call&lt;Item&gt;, t: Throwable) {\n                        tvResult.text = \"GET Failure: ${t.message}\"\n                    }\n                })\n            } else {\n                tvResult.text = \"Invalid ID. Please enter a valid numeric ID.\"\n            }\n        }\n    }\n    private fun createItem() {\n        val id = etId.text.toString().toIntOrNull() ?: 0\n        val name = etName.text.toString()\n        val course = etDescription.text.toString()\n        val newItem = Item(id = id, name = name, course = course)\n        apiService.createItem(newItem).enqueue(object : Callback&lt;Item&gt; {\n            override fun onResponse(call: Call&lt;Item&gt;, response: Response&lt;Item&gt;) {\n                if (response.isSuccessful) {\n                    tvResult.text = \"POST Success: ${response.body()}\"\n                } else {\n                    tvResult.text = \"POST Error: ${response.code()}\"\n                }\n            }\n            override fun onFailure(call: Call&lt;Item&gt;, t: Throwable) {\n                tvResult.text = \"POST Failure: ${t.message}\"\n            }\n        })\n    }\n    private fun updateItem() {\n        val id = etId.text.toString().toIntOrNull()\n        if (id != null) {\n            val name = etName.text.toString()\n            val course = etDescription.text.toString()\n            val updatedItem = Item(id = id, name = name, course = course)\n            apiService.updateItem(id, updatedItem).enqueue(object : Callback&lt;Item&gt; {\n                override fun onResponse(call: Call&lt;Item&gt;, response: Response&lt;Item&gt;) {\n                    if (response.isSuccessful) {\n                        tvResult.text = \"PUT Success: ${response.body()}\"\n                    } else {\n                        tvResult.text = \"PUT Error: ${response.code()}\"\n                    }\n                }\n                override fun onFailure(call: Call&lt;Item&gt;, t: Throwable) {\n                    tvResult.text = \"PUT Failure: ${t.message}\"\n                }\n            })\n        } else {\n            tvResult.text = \"Please enter a valid ID\"\n        }\n    }\n    private fun deleteItem() {\n        val id = etId.text.toString().toIntOrNull()\n        if (id != null) {\n            apiService.deleteItem(id).enqueue(object : Callback&lt;Void&gt; {\n                override fun onResponse(call: Call&lt;Void&gt;, response: Response&lt;Void&gt;) {\n                    if (response.isSuccessful) {\n                        tvResult.text = \"DELETE Success: ${response.code()}\"\n                    } else {\n                        tvResult.text = \"DELETE Error: ${response.code()}\"\n                    }\n                }\n                override fun onFailure(call: Call&lt;Void&gt;, t: Throwable) {\n                    tvResult.text = \"DELETE Failure: ${t.message}\"\n                }\n            })\n        } else {\n            tvResult.text = \"Please enter a valid ID\"\n        }\n    }\n}\n\n</code></pre>\n<p><code>network-security-config.xml</code> in res/xml:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-xml\">&lt;network-security-config&gt;\n&lt;base-config cleartextTrafficPermitted=\"true\"&gt;\n    &lt;trust-anchors&gt;\n        &lt;certificates src=\"system\" /&gt;\n    &lt;/trust-anchors&gt;\n&lt;/base-config&gt;\n&lt;/network-security-config&gt;\n\n</code></pre>\n<p><code>AndroidManifest.xml</code></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-xml\">&lt;manifest&gt;\n    &lt;uses-permission android:name=\"android.permission.INTERNET\"&gt;&lt;/uses-permission&gt;\n            android:networkSecurityConfig=\"@xml/network_security_config\"\n        android:usesCleartextTraffic=\"true\"\n    &lt;/application&gt;\n&lt;/manifest&gt;\n\n</code></pre>\n<p>Server Setup:</p>\n<p><code>mkdir node-web-server</code></p>\n<p><code>cd node-web-server</code></p>\n<p><code>npm init -y</code></p>\n<p><code>npm install express</code></p>\n<p><code>Server.js :</code></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">const express = require('express')\nconst app = express()\nconst port = 1113\napp.use(express.json())\nlet data = [\n    { id: 1, name: 'Alex', course: 'Computer Science'},\n    { id: 2, name: 'John', course: 'Electrical'},\n    { id: 3, name: 'Charlie', course: 'Civil'},\n    { id: 4, name: 'JohnWick', course: 'Mathematics' },\n    { id: 5, name: 'Jane', course: 'Physics' },\n    { id: 6, name: 'Smith', course: 'Chemistry' }\n]\n//Read all items\napp.get('/get', (req, res) =&gt; {\n    res.json(data)\n})\n//Read specific item\napp.get('/get/:id', (req, res) =&gt; {\n    const id = parseInt(req.params.id)\n    const item = data.find( i =&gt; i.id === id)\n    if(item){\n        res.json(item)\n    } else{\n        res.status(404).send('Item Not Fund')\n    }\n})\n//Create a new item\napp.post('/post', (req, res) =&gt; {\n    const newItem =  { id: data.length + 1, ...req.body}\n    data.push(newItem)\n    res.status(201).json(newItem)\n})\n//Update an item\napp.put('/update/:id', (req, res) =&gt; {\n    const id = parseInt(req.params.id);\n    const index = data.findIndex(i =&gt; i.id === id);\n    if (index !== -1) {\n      data[index] = { id, ...req.body };\n      res.json(data[index]);\n    } else {\n      res.status(404).send('Item not found');\n    }\n});\n// Delete an item\napp.delete('/delete/:id', (req, res) =&gt; {\n    const id = parseInt(req.params.id);\n    const index = data.findIndex(i =&gt; i.id === id);\n    if (index !== -1) {\n      const deletedItem = data.splice(index, 1);\n      res.json(deletedItem[0]);\n      res.send(`Item deleted`);\n    } else {\n      res.status(404).send('Item not found');\n    }\n});\napp.get('/', (req, res) =&gt; {\n  res.send(`Server running successfully`);\n});\napp.listen(port, () =&gt; {\n    console.log(`Server running at http://localhost:${port}/`)\n})\n\n</code></pre>\n<p><code>package.json:</code></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\n  \"name\": \"web-server\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" &amp;&amp; exit 1\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"description\": \"\",\n  \"dependencies\": {\n    \"express\": \"^4.19.2\"\n  }\n}\n\n</code></pre>\n<p><code>node server.js</code></p>\n</body></html>","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","toc":[],"owner":"36920253","collectionId":"8fcbd36a-1ac7-4800-8eb8-59cda5c86b5e","publishedId":"2sA3s3GWia","public":true,"customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"},"publishDate":"2024-08-09T20:13:26.000Z"},"item":[{"name":"Get All","id":"6019521d-7fba-483c-bf78-0b5486ffafb7","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[],"url":"http://localhost:1113/get","description":"<p><img src=\"https://content.pstmn.io/a2a8bfbf-4113-42c9-a299-aaa039518024/R2V0IEFsbC5qcGc=\" alt=\"Get%20All%20Items\n&lt;br&gt;endpoint%20/get\" height=\"380\" width=\"227\" /><img src=\"https://content.pstmn.io/85e74f93-3778-4216-ad14-f5068d8d80e7/R2V0IHdpdGggSUQuanBn\" alt=\"Get%20specific%20item%20with%20id\n&lt;br&gt;endpoint%20/get/id\" height=\"396\" width=\"227\" /></p>\n","urlObject":{"protocol":"http","port":"1113","path":["get"],"host":["localhost"],"query":[],"variable":[]}},"response":[{"id":"532890c3-5e5a-419c-b62c-42c9d910e4c9","name":"New Request","originalRequest":{"method":"GET","header":[],"url":"http://localhost:1113/get"},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"X-Powered-By","value":"Express"},{"key":"Content-Type","value":"application/json; charset=utf-8"},{"key":"Content-Length","value":"274"},{"key":"ETag","value":"W/\"112-kSgJqz0iPqswfisr537pblSgQOY\""},{"key":"Date","value":"Fri, 09 Aug 2024 19:06:29 GMT"},{"key":"Connection","value":"keep-alive"},{"key":"Keep-Alive","value":"timeout=5"}],"cookie":[],"responseTime":null,"body":"[\n    {\n        \"id\": 1,\n        \"name\": \"Alex\",\n        \"course\": \"Computer Science\"\n    },\n    {\n        \"id\": 2,\n        \"name\": \"John\",\n        \"course\": \"Electrical\"\n    },\n    {\n        \"id\": 4,\n        \"name\": \"JohnWick\",\n        \"course\": \"Mathematics\"\n    },\n    {\n        \"id\": 5,\n        \"name\": \"Jane\",\n        \"course\": \"Physics\"\n    },\n    {\n        \"id\": 6,\n        \"course\": \"Biology\",\n        \"name\": \"Smith\"\n    },\n    {\n        \"id\": 7,\n        \"course\": \"English\",\n        \"name\": \"Siri\"\n    }\n]"}],"_postman_id":"6019521d-7fba-483c-bf78-0b5486ffafb7"},{"name":"Post","id":"575ae56b-c3ec-4b16-bd3b-0684d279881a","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json","type":"text"}],"body":{"mode":"raw","raw":"{\r\n    \"name\": \"Crimson\",\r\n    \"course\": \"Biology\"\r\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:1113/post","description":"<img src=\"https://content.pstmn.io/6f113461-7776-45f4-a914-39630aa4893a/QWRkIEl0ZW0uanBn\" alt=\"Add%20a%20new%20item\" height=\"322\" width=\"271\" />","urlObject":{"protocol":"http","port":"1113","path":["post"],"host":["localhost"],"query":[{"disabled":true,"key":"content-type","value":null}],"variable":[]}},"response":[{"id":"1ed48f23-8c64-4e1d-8617-0d920bc6ba06","name":"http://localhost:1111/post","originalRequest":{"method":"POST","header":[{"key":"Content-Type","value":"application/json","type":"text"}],"body":{"mode":"raw","raw":"{\r\n    \"name\": \"Crimson\",\r\n    \"course\": \"Biology\"\r\n}","options":{"raw":{"language":"json"}}},"url":{"raw":"http://localhost:1113/post","protocol":"http","host":["localhost"],"port":"1113","path":["post"],"query":[{"key":"content-type","value":null,"type":"text","disabled":true}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"X-Powered-By","value":"Express"},{"key":"Content-Type","value":"application/json; charset=utf-8"},{"key":"Content-Length","value":"44"},{"key":"ETag","value":"W/\"2c-brnm8JcWWmXw7HH/OUEEOtqLfrc\""},{"key":"Date","value":"Fri, 09 Aug 2024 19:12:09 GMT"},{"key":"Connection","value":"keep-alive"},{"key":"Keep-Alive","value":"timeout=5"}],"cookie":[],"responseTime":null,"body":"{\n    \"id\": 8,\n    \"name\": \"Crimson\",\n    \"course\": \"Biology\"\n}"}],"_postman_id":"575ae56b-c3ec-4b16-bd3b-0684d279881a"},{"name":"Delete","id":"f3c64d53-5bb3-4527-871b-d06cfcd0b10f","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"DELETE","header":[],"url":"http://localhost:1113/delete/7","description":"<img src=\"https://content.pstmn.io/d466e0d7-e43f-4ba7-a31f-c8259ad542c3/RGVsZXRlIEl0ZW0uanBn\" alt=\"Delete\" width=\"295\" height=\"342\" />","urlObject":{"protocol":"http","port":"1113","path":["delete","7"],"host":["localhost"],"query":[],"variable":[]}},"response":[{"id":"5da46882-07d3-4519-91a9-338fae4e6969","name":"Delete","originalRequest":{"method":"DELETE","header":[],"url":"http://localhost:1113/delete/7"},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"X-Powered-By","value":"Express"},{"key":"Content-Type","value":"application/json; charset=utf-8"},{"key":"Content-Length","value":"41"},{"key":"ETag","value":"W/\"29-mWRfHu+f9k+QyeirJBcI+RgyRPk\""},{"key":"Date","value":"Fri, 09 Aug 2024 19:08:46 GMT"},{"key":"Connection","value":"keep-alive"},{"key":"Keep-Alive","value":"timeout=5"}],"cookie":[],"responseTime":null,"body":"{\n    \"id\": 7,\n    \"course\": \"English\",\n    \"name\": \"Siri\"\n}"}],"_postman_id":"f3c64d53-5bb3-4527-871b-d06cfcd0b10f"},{"name":"Update","id":"8f223cec-11db-46f5-b6fa-44c22413d3e8","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json","type":"text"}],"body":{"mode":"raw","raw":"{\r\n    \"name\": \"Doe\",\r\n    \"course\": \"Computer\"\r\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:1113/update/7","description":"<img src=\"https://content.pstmn.io/7a574316-b785-41d1-942d-cca8d063274e/VXBkYXRlIEl0ZW0uanBn\" alt=\"Update\" height=\"364\" width=\"315\" />\n\n<img src=\"https://content.pstmn.io/af4872a1-aa27-443d-85bf-409c030e7cdb/QWZ0ZXIgVXBkYXRlICYgRGVsZXRlLmpwZw==\" alt=\"After%20Update%20&amp;%20Delete\" height=\"462\" width=\"319\" />","urlObject":{"protocol":"http","port":"1113","path":["update","7"],"host":["localhost"],"query":[],"variable":[]}},"response":[{"id":"a104d3f1-cc9c-4426-892c-fa537bd9e2a7","name":"Update","originalRequest":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json","type":"text"}],"body":{"mode":"raw","raw":"{\r\n    \"name\": \"Doe\",\r\n    \"course\": \"Computer\"\r\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:1113/update/7"},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"X-Powered-By","value":"Express"},{"key":"Content-Type","value":"application/json; charset=utf-8"},{"key":"Content-Length","value":"41"},{"key":"ETag","value":"W/\"29-mzXcLdO1gUTqTi2QXolxwy09YBM\""},{"key":"Date","value":"Fri, 09 Aug 2024 19:11:15 GMT"},{"key":"Connection","value":"keep-alive"},{"key":"Keep-Alive","value":"timeout=5"}],"cookie":[],"responseTime":null,"body":"{\n    \"id\": 7,\n    \"name\": \"Doe\",\n    \"course\": \"Computer\"\n}"}],"_postman_id":"8f223cec-11db-46f5-b6fa-44c22413d3e8"}]}