-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
161 lines (129 loc) · 55.7 KB
/
atom.xml
File metadata and controls
161 lines (129 loc) · 55.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>@t2ee</title>
<subtitle>Typescript To Enterprise Edition</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://blog.t2ee.org/"/>
<updated>2017-08-05T07:23:26.000Z</updated>
<id>https://blog.t2ee.org/</id>
<author>
<name>Qiaosen Huang</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>Tutorial Series (1): A Simple Todo List Api Server (3)</title>
<link href="https://blog.t2ee.org/2017/08/05/A-Simple-Todo-List-Api-Server-3/"/>
<id>https://blog.t2ee.org/2017/08/05/A-Simple-Todo-List-Api-Server-3/</id>
<published>2017-08-05T06:54:23.000Z</published>
<updated>2017-08-05T07:23:26.000Z</updated>
<content type="html"><![CDATA[<h1 id="Advanced-Content"><a href="#Advanced-Content" class="headerlink" title="Advanced Content"></a>Advanced Content</h1><p>If you missed the first two articles, you are strongly advised to read them first. <a href="/2017/08/04/A-Simple-Todo-List-Api-Server-1">Chapter-1</a> and <a href="/2017/08/04/A-Simple-Todo-List-Api-Server-2">Chapter-2</a></p>
<p>In this chapter, we will be implementing error handling and input validation.</p>
<h1 id="Validate-Input"><a href="#Validate-Input" class="headerlink" title="Validate Input"></a>Validate Input</h1><p>Let’s first import the necessary components.</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> {</div><div class="line"> Valid,</div><div class="line"> Min,</div><div class="line"> Max,</div><div class="line"> NotNull,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/validation'</span>;</div></pre></td></tr></table></figure>
<p>We will be implementing a TodoItem class which specify how we should validate inputs.</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> TodoItem {</div><div class="line"> id: <span class="built_in">number</span>;</div><div class="line"></div><div class="line"> <span class="meta">@NotNull</span></div><div class="line"> <span class="meta">@Min</span>(<span class="number">1</span>)</div><div class="line"> <span class="meta">@Max</span>(<span class="number">10</span>)</div><div class="line"> name: <span class="built_in">string</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>Now we have a form that takes two fields, <code>id</code> and <code>name</code>. <code>id</code> can be null, and <code>name</code> should be a string with <code>1-10</code> characters.</p>
<p>To make it automatically validate input body, all we have to do is two modifications on method declarations, following is an example on <code>postItem</code>.</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">async</span> postItem(<span class="meta">@Valid</span> <span class="meta">@Body</span> body: TodoItem, <span class="meta">@PathParam</span>(<span class="string">'id'</span>) id)</div></pre></td></tr></table></figure>
<p>First, we need to add <code>@valid</code> decorator to tell our validator that this parameter should be validated, and change its declaration type to the class we implemented above. Thus, all those rules will be validated against request body.</p>
<p>Apply the same to <code>putItem</code>. Now we have all our input validated;</p>
<h1 id="Error-Handling"><a href="#Error-Handling" class="headerlink" title="Error Handling"></a>Error Handling</h1><p>The one problem with this api service is that it is not really <code>RESTful</code>, all errors are not correctly returned to the user.</p>
<p>To achive so, we will implement a configuration class to implement error handling.</p>
<p>First, let’s create a file <code>config.ts</code>, and import it in <code>index.ts</code>, something like <code>import './config'</code>. This makes sure configurations are applied.</p>
<p>Now, import all components that we are gonna use.</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> {</div><div class="line"> Configuration,</div><div class="line"> Bean,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/core'</span>;</div><div class="line"><span class="keyword">import</span> {</div><div class="line"> NotFoundHandler,</div><div class="line"> ErrorHandler,</div><div class="line"> Request,</div><div class="line"> Response,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/vader'</span>;</div><div class="line"><span class="keyword">import</span> {</div><div class="line"> ValidationError,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/validation'</span>;</div></pre></td></tr></table></figure>
<p>To provide autowirable objects globally, we need to have a configuration class.</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Configuration</span></div><div class="line"><span class="keyword">class</span> Config {</div><div class="line">}</div></pre></td></tr></table></figure>
<p>Let’s first handle <code>404</code> properly.</p>
<p>Add following method to class <code>Config</code>.</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Bean</span>(<span class="string">'NotFoundHandler'</span>)</div><div class="line">notFoundHandler(): NotFoundHandler {</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="keyword">async</span> handle(req: Request, data: <span class="built_in">void</span>): <span class="built_in">Promise</span><Response> {</div><div class="line"> <span class="keyword">const</span> res = <span class="keyword">new</span> Response();</div><div class="line"> res.body = {</div><div class="line"> error: <span class="string">'not found'</span>,</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> res;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p><code>@Bean</code> means this is injected as an instance not a class. Passing a string to it, makes us/<code>vader</code> access it by name (since <code>NotFoundHandler</code> is an <code>interface</code>, which is psuedo, that is not generated in js).</p>
<p>In the handler, we make it returnning json response, this makes it easier to handle on client side.</p>
<p>Notice after we added validation, the server responds a <code>internal error</code> to the client if wrong data were requested. It is because there is no handler for this type of error.</p>
<p>Add following to <code>Config</code>.</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Bean</span>(<span class="string">'ErrorHandler'</span>)</div><div class="line">errorHandler(): ErrorHandler {</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="keyword">async</span> handle(req: Request, e: <span class="built_in">Error</span>): <span class="built_in">Promise</span><Response> {</div><div class="line"> <span class="keyword">let</span> error = <span class="string">'internal error'</span>;</div><div class="line"> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> ValidationError) {</div><div class="line"> error = <span class="string">'request error'</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> res = <span class="keyword">new</span> Response();</div><div class="line"> res.body = {</div><div class="line"> error,</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> res;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h1 id="Put-it-together"><a href="#Put-it-together" class="headerlink" title="Put it together"></a>Put it together</h1><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> {</div><div class="line"> Configuration,</div><div class="line"> Bean,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/core'</span>;</div><div class="line"><span class="keyword">import</span> {</div><div class="line"> NotFoundHandler,</div><div class="line"> ErrorHandler,</div><div class="line"> Request,</div><div class="line"> Response,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/vader'</span>;</div><div class="line"><span class="keyword">import</span> {</div><div class="line"> ValidationError,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/validation'</span>;</div><div class="line"></div><div class="line"><span class="meta">@Configuration</span></div><div class="line"><span class="keyword">class</span> Config {</div><div class="line"> <span class="meta">@Bean</span>(<span class="string">'NotFoundHandler'</span>)</div><div class="line"> notFoundHandler(): NotFoundHandler {</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="keyword">async</span> handle(req: Request, data: <span class="built_in">void</span>): <span class="built_in">Promise</span><Response> {</div><div class="line"> <span class="keyword">const</span> res = <span class="keyword">new</span> Response();</div><div class="line"> res.body = {</div><div class="line"> error: <span class="string">'not found'</span>,</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> res;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Bean</span>(<span class="string">'ErrorHandler'</span>)</div><div class="line"> errorHandler(): ErrorHandler {</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="keyword">async</span> handle(req: Request, e: <span class="built_in">Error</span>): <span class="built_in">Promise</span><Response> {</div><div class="line"> <span class="keyword">let</span> error = <span class="string">'internal error'</span>;</div><div class="line"> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> ValidationError) {</div><div class="line"> error = <span class="string">'request error'</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> res = <span class="keyword">new</span> Response();</div><div class="line"> res.body = {</div><div class="line"> error,</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> res;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure>
<h1 id="Lastly"><a href="#Lastly" class="headerlink" title="Lastly"></a>Lastly</h1><p>This is the end of this project. Again, you can access all source codes for each chapter at <a href="https://github.com/t2ee/tutorial-series-1" target="_blank" rel="external">https://github.com/t2ee/tutorial-series-1</a>.</p>
<p>I hope you know a bit more of <code>@t2ee</code> components by now. In the next series, we will be learning how to customize them.</p>
<p>See you soon!</p>
]]></content>
<summary type="html">
<h1 id="Advanced-Content"><a href="#Advanced-Content" class="headerlink" title="Advanced Content"></a>Advanced Content</h1><p>If you missed
</summary>
<category term="tutorials" scheme="https://blog.t2ee.org/tags/tutorials/"/>
<category term="A Simple Todo List Api Server" scheme="https://blog.t2ee.org/tags/A-Simple-Todo-List-Api-Server/"/>
</entry>
<entry>
<title>Tutorial Series (1): A Simple Todo List Api Server (2)</title>
<link href="https://blog.t2ee.org/2017/08/04/A-Simple-Todo-List-Api-Server-2/"/>
<id>https://blog.t2ee.org/2017/08/04/A-Simple-Todo-List-Api-Server-2/</id>
<published>2017-08-04T10:39:10.000Z</published>
<updated>2017-08-04T13:38:08.000Z</updated>
<content type="html"><![CDATA[<h1 id="Welcome-Back"><a href="#Welcome-Back" class="headerlink" title="Welcome Back"></a>Welcome Back</h1><p>If you missed the first episode, check <a href="/2017/08/04/A-Simple-Todo-List-Api-Server-1">here</a>.</p>
<p>Also, source code is available at <a href="https://github.com/t2ee/tutorial-series-1" target="_blank" rel="external">https://github.com/t2ee/tutorial-series-1</a>. Or</p>
<p>In the last section, we built a basic todo list servier with get functions and set all the things right.</p>
<p>Now, we should add <code>create</code>, <code>update</code> and <code>delete</code> abilities to it.</p>
<h1 id="Here-We-Go"><a href="#Here-We-Go" class="headerlink" title="Here We Go"></a>Here We Go</h1><p>As we have already setup properly, now all have to do is to add methods to the controller.</p>
<h1 id="POST-Item"><a href="#POST-Item" class="headerlink" title="POST Item"></a>POST Item</h1><p>Now we are implementing a method for creating todo items (remember to import neccesary components from <code>@t2ee/vader</code>).</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">@Consumes('application/json')</div><div class="line"> @POST</div><div class="line"> @Path('/:id?')</div><div class="line"> async postItem(@Body body, @PathParam('id') id) {</div><div class="line"> if (!id) {</div><div class="line"> id = LIST.length;</div><div class="line"> } else {</div><div class="line"> id = parseInt(id);</div><div class="line"> }</div><div class="line"> const item = {</div><div class="line"> id,</div><div class="line"> name: body.name,</div><div class="line"> };</div><div class="line"> LIST.push(item);</div><div class="line"> const response = new Response();</div><div class="line"> response.body = item;</div><div class="line"> return response;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p><code>@Consumes('application/json')</code> tells <code>vader</code> to consume request body as json.</p>
<p><code>@Body</code> binds the requested body. At this moment, we haven’t done any type checking yet, so it’s an <code>any</code> variable.</p>
<p><code>@Path('/:id?')</code>, the parameter in <code>@Path()</code> is slightly different from what we used in <code>getItem()</code>. In this format, we can make parameter <code>id</code> optional. Thus, we can get <code>id</code> from either body or path.</p>
<h1 id="PUT-and-DELETE-item"><a href="#PUT-and-DELETE-item" class="headerlink" title="PUT and DELETE item"></a>PUT and DELETE item</h1><p>Like how we implmeneted the <code>postItem</code> method, <code>put</code> and <code>delete</code> should be almost the same.</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Consumes</span>(<span class="string">'application/json'</span>)</div><div class="line"><span class="meta">@PUT</span></div><div class="line"><span class="meta">@Path</span>(<span class="string">'/:id?'</span>)</div><div class="line"><span class="keyword">async</span> putItem(<span class="meta">@Body</span> body, <span class="meta">@PathParam</span>(<span class="string">'id'</span>) id) {</div><div class="line"> <span class="keyword">if</span> (!id) {</div><div class="line"> id = body.id;</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> id = <span class="built_in">parseInt</span>(id);</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> item = LIST.find(<span class="function"><span class="params">todo</span> =></span> todo.id === id);</div><div class="line"> <span class="keyword">if</span> (item) {</div><div class="line"> item.name = body.name;</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = item;</div><div class="line"> <span class="keyword">return</span> response;</div><div class="line"> }</div><div class="line">} </div><div class="line"></div><div class="line"><span class="meta">@DELETE</span></div><div class="line"><span class="meta">@Path</span>(<span class="string">'/:id'</span>)</div><div class="line"><span class="keyword">async</span> deleteItem(<span class="meta">@PathParam</span>(<span class="string">'id'</span>) id) {</div><div class="line"> id = <span class="built_in">parseInt</span>(id);</div><div class="line"> <span class="keyword">const</span> index = LIST.findIndex(<span class="function"><span class="params">todo</span> =></span> todo.id === id);</div><div class="line"> <span class="keyword">if</span> (index >= <span class="number">0</span>) {</div><div class="line"> LIST.splice(index, <span class="number">1</span>);</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = <span class="string">'OK'</span>;</div><div class="line"> <span class="keyword">return</span> response;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h1 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h1><p>Now we have a full functional todo item api service. In the next section, we will be discussing some other advantage usages with <code>@t2ee</code> components.</p>
<p>Controller should be look like following:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> {</div><div class="line"> GET,</div><div class="line"> POST,</div><div class="line"> PUT,</div><div class="line"> DELETE,</div><div class="line"> Consumes,</div><div class="line"> Body,</div><div class="line"> Path,</div><div class="line"> Response,</div><div class="line"> PathParam,</div><div class="line"> QueryParam,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/vader'</span>;</div><div class="line"><span class="keyword">import</span> {</div><div class="line"> Component,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/core'</span>;</div><div class="line"></div><div class="line"><span class="keyword">let</span> LIST = [{</div><div class="line"> id: <span class="number">0</span>,</div><div class="line"> name: <span class="string">'a'</span>,</div><div class="line">}, {</div><div class="line"> id: <span class="number">1</span>,</div><div class="line"> name: <span class="string">'b'</span>,</div><div class="line">}];</div><div class="line"></div><div class="line"><span class="meta">@Path</span>(<span class="string">'/todo'</span>)</div><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> Controller {</div><div class="line"> <span class="meta">@GET</span></div><div class="line"> <span class="meta">@Path</span>(<span class="string">'/'</span>)</div><div class="line"> <span class="keyword">async</span> getList(<span class="meta">@QueryParam</span>(<span class="string">'id'</span>) id): <span class="built_in">Promise</span><Response> {</div><div class="line"> <span class="keyword">if</span> (id) {</div><div class="line"> id = <span class="built_in">parseInt</span>(id);</div><div class="line"> <span class="keyword">const</span> item = LIST.find(<span class="function"><span class="params">todo</span> =></span> todo.id === id);</div><div class="line"> <span class="keyword">if</span> (item) {</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = item;</div><div class="line"> <span class="keyword">return</span> response; </div><div class="line"> }</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = LIST;</div><div class="line"> <span class="keyword">return</span> response;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@GET</span></div><div class="line"> <span class="meta">@Path</span>(<span class="string">'/:id'</span>)</div><div class="line"> <span class="keyword">async</span> getItem(<span class="meta">@PathParam</span>(<span class="string">'id'</span>) id) {</div><div class="line"> id = <span class="built_in">parseInt</span>(id);</div><div class="line"> <span class="keyword">const</span> item = LIST.find(<span class="function"><span class="params">todo</span> =></span> todo.id === id);</div><div class="line"> <span class="keyword">if</span> (item) {</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = item;</div><div class="line"> <span class="keyword">return</span> response; </div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Consumes</span>(<span class="string">'application/json'</span>)</div><div class="line"> <span class="meta">@POST</span></div><div class="line"> <span class="meta">@Path</span>(<span class="string">'/:id?'</span>)</div><div class="line"> <span class="keyword">async</span> postItem(<span class="meta">@Body</span> body, <span class="meta">@PathParam</span>(<span class="string">'id'</span>) id) {</div><div class="line"> <span class="keyword">if</span> (!id) {</div><div class="line"> id = LIST.length;</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> id = <span class="built_in">parseInt</span>(id);</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> item = {</div><div class="line"> id,</div><div class="line"> name: body.name,</div><div class="line"> };</div><div class="line"> LIST.push(item);</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = item;</div><div class="line"> <span class="keyword">return</span> response;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Consumes</span>(<span class="string">'application/json'</span>)</div><div class="line"> <span class="meta">@PUT</span></div><div class="line"> <span class="meta">@Path</span>(<span class="string">'/:id?'</span>)</div><div class="line"> <span class="keyword">async</span> putItem(<span class="meta">@Body</span> body, <span class="meta">@PathParam</span>(<span class="string">'id'</span>) id) {</div><div class="line"> <span class="keyword">if</span> (!id) {</div><div class="line"> id = body.id;</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> id = <span class="built_in">parseInt</span>(id);</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> item = LIST.find(<span class="function"><span class="params">todo</span> =></span> todo.id === id);</div><div class="line"> <span class="keyword">if</span> (item) {</div><div class="line"> item.name = body.name;</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = item;</div><div class="line"> <span class="keyword">return</span> response;</div><div class="line"> }</div><div class="line"> } </div><div class="line"> </div><div class="line"> <span class="meta">@DELETE</span></div><div class="line"> <span class="meta">@Path</span>(<span class="string">'/:id'</span>)</div><div class="line"> <span class="keyword">async</span> deleteItem(<span class="meta">@PathParam</span>(<span class="string">'id'</span>) id) {</div><div class="line"> id = <span class="built_in">parseInt</span>(id);</div><div class="line"> <span class="keyword">const</span> index = LIST.findIndex(<span class="function"><span class="params">todo</span> =></span> todo.id === id);</div><div class="line"> <span class="keyword">if</span> (index >= <span class="number">0</span>) {</div><div class="line"> LIST.splice(index, <span class="number">1</span>);</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = <span class="string">'OK'</span>;</div><div class="line"> <span class="keyword">return</span> response;</div><div class="line"> }</div><div class="line"> } </div><div class="line">}</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h1 id="Welcome-Back"><a href="#Welcome-Back" class="headerlink" title="Welcome Back"></a>Welcome Back</h1><p>If you missed the first episod
</summary>
<category term="tutorials" scheme="https://blog.t2ee.org/tags/tutorials/"/>
<category term="A Simple Todo List Api Server" scheme="https://blog.t2ee.org/tags/A-Simple-Todo-List-Api-Server/"/>
</entry>
<entry>
<title>Tutorial Series (1): A Simple Todo List Api Server (1)</title>
<link href="https://blog.t2ee.org/2017/08/04/A-Simple-Todo-List-Api-Server-1/"/>
<id>https://blog.t2ee.org/2017/08/04/A-Simple-Todo-List-Api-Server-1/</id>
<published>2017-08-04T06:59:13.000Z</published>
<updated>2017-08-07T05:26:08.000Z</updated>
<content type="html"><![CDATA[<h1 id="First-of-all"><a href="#First-of-all" class="headerlink" title="First of all"></a>First of all</h1><p>This is the first project of the <a href="/tags/tutorials">Tutorial Serires</a>.</p>
<p>For this project, we will be building a simple TODO list api server, with CRUD abilities.<br>You can download the source code at <a href="https://github.com/t2ee/tutorial-series-1" target="_blank" rel="external">https://github.com/t2ee/tutorial-series-1</a></p>
<h1 id="Preperation"><a href="#Preperation" class="headerlink" title="Preperation"></a>Preperation</h1><p>To get our hands sweaty, first we need to setup some basic dependencies and environment.</p>
<p><code>npm i typescript reflect-metadata @t2ee/core @t2ee/sl4js @t2ee/validation @t2ee/vdaer koa@2</code></p>
<p>These are all the packages we are gonna be using for this tutorial. also don’t forget to create a <code>tsconfig.json</code>, with <code>experimentalDecorators</code> and <code>emitDecoratorMetadata</code> enabled.</p>
<p>If you do not already have one or not familar with <code>typescript</code> yet, following is an good start.</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> <span class="attr">"compilerOptions"</span>: {</div><div class="line"> <span class="attr">"module"</span>: <span class="string">"commonjs"</span>,</div><div class="line"> <span class="attr">"outDir"</span>: <span class="string">"./dist"</span>,</div><div class="line"> <span class="attr">"target"</span>: <span class="string">"ES6"</span>,</div><div class="line"> <span class="attr">"experimentalDecorators"</span>: <span class="literal">true</span>,</div><div class="line"> <span class="attr">"emitDecoratorMetadata"</span>: <span class="literal">true</span>,</div><div class="line"> <span class="attr">"declaration"</span>: <span class="literal">true</span>,</div><div class="line"> <span class="attr">"sourceMap"</span>: <span class="literal">true</span></div><div class="line"> },</div><div class="line"> <span class="attr">"compileOnSave"</span>: <span class="literal">false</span>,</div><div class="line"> <span class="attr">"include"</span>: [</div><div class="line"> <span class="string">"src/**/*.ts"</span></div><div class="line"> ]</div><div class="line">}</div></pre></td></tr></table></figure>
<h1 id="Objectives"><a href="#Objectives" class="headerlink" title="Objectives"></a>Objectives</h1><p>For the first part of this tutorial, we are building two routes first. <code>GET /todo</code> and <code>GET /todo/:id</code>. </p>
<p>The first one will return the list of all todo items, and the second returns an item that matches the id.</p>
<h1 id="First-Controller"><a href="#First-Controller" class="headerlink" title="First Controller"></a>First Controller</h1><p>First, let’s prepare <code>logger.yaml</code>, which will be read by <code>@t2ee/sl4js</code> to configure loggers and formats.</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="attr">default:</span> <span class="string">console</span> </div><div class="line"><span class="attr">level:</span> <span class="string">LogLevel.DEBUG</span></div><div class="line"><span class="attr">appenders:</span></div><div class="line"><span class="attr"> - name:</span> <span class="string">console</span></div><div class="line"><span class="attr"> appender:</span> <span class="string">console</span></div><div class="line"><span class="attr"> pattern:</span> <span class="string">'%d{YYYY-MM-DD HH:mm:ss.SSS} %-7c{[%l]} %10n %5p - %2w %M'</span></div><div class="line"><span class="attr"> level:</span> <span class="string">LogLevel.DEBUG</span></div></pre></td></tr></table></figure>
<p>To move on with this tutorial, we will not be discussing the details of loggin configurations here today, other tutorials will be covering it.</p>
<p>Now, let’s code our controller, which will be responsible how requests are handled.</p>
<p>First import all components we will be using.</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> {</div><div class="line"> GET,</div><div class="line"> Path,</div><div class="line"> Response,</div><div class="line"> PathParam,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/vader'</span>;</div><div class="line"><span class="keyword">import</span> {</div><div class="line"> Component,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/core'</span>;</div></pre></td></tr></table></figure>
<p><code>@GET</code> and <code>@Path</code> define routes. Response is the object you will be returning at the end of request. <code>@PathParam</code> is used to acquire parameters in path. <code>@Component</code> needs to be added to class to trigger bindings, otherwise, none of the above would work.</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Path</span>(<span class="string">'/todo'</span>)</div><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> Controller {</div><div class="line"> <span class="meta">@GET</span></div><div class="line"> <span class="meta">@Path</span>(<span class="string">'/'</span>)</div><div class="line"> <span class="keyword">async</span> getList(): <span class="built_in">Promise</span><Response> {</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = LIST;</div><div class="line"> <span class="keyword">return</span> response;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>let’s also define a local variable to store todo items in memory.<br><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> LIST = [{</div><div class="line"> id: <span class="number">0</span>,</div><div class="line"> name: <span class="string">'a'</span>,</div><div class="line">}, {</div><div class="line"> id: <span class="number">1</span>,</div><div class="line"> name: <span class="string">'b'</span>,</div><div class="line">}];</div></pre></td></tr></table></figure></p>
<p>So now we have our class and first route. </p>
<p>In order to run the controller, we also need to have an entry file to register our controller into the router, see <a href="#index-ts">index.ts</a></p>
<p>Now run <code>tsc -p tsconfig.json && node dist</code>, this will compile all the source codes and start the server.</p>
<p>You should be able to see something like below</p>
<p><img src="1.png" alt=""></p>
<p>Now navigate your brower to <code>http://localhost:8080/todo</code>, you should be able to all the todo items.</p>
<h1 id="Add-Another-Route"><a href="#Add-Another-Route" class="headerlink" title="Add Another Route"></a>Add Another Route</h1><p>Now we want to access single todo items by id, <code>@PathParam</code> comes in for us to get id from path.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">@GET</div><div class="line">@Path('/:id')</div><div class="line">async getItem(@PathParam('id') id) {</div><div class="line"> id = parseInt(id);</div><div class="line"> const item = LIST.find(todo => todo.id === id);</div><div class="line"> if (item) {</div><div class="line"> const response = new Response();</div><div class="line"> response.body = item;</div><div class="line"> return response; </div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>This will be able to handle path like <code>/todo/1</code>.</p>
<p>If you want to take id from query, you can change the first route to following (don’t forget to import <code>QueryParam</code> form <code>@t2ee/vader</code>).</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@GET</span></div><div class="line"><span class="meta">@Path</span>(<span class="string">'/'</span>)</div><div class="line"><span class="keyword">async</span> getList(<span class="meta">@QueryParam</span>(<span class="string">'id'</span>) id): <span class="built_in">Promise</span><Response> {</div><div class="line"> <span class="keyword">if</span> (id) {</div><div class="line"> id = <span class="built_in">parseInt</span>(id);</div><div class="line"> <span class="keyword">const</span> item = LIST.find(<span class="function"><span class="params">todo</span> =></span> todo.id === id);</div><div class="line"> <span class="keyword">if</span> (item) {</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = item;</div><div class="line"> <span class="keyword">return</span> response; </div><div class="line"> }</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = LIST;</div><div class="line"> <span class="keyword">return</span> response;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h1 id="All-together"><a href="#All-together" class="headerlink" title="All together"></a>All together</h1><h2 id="index-ts"><a href="#index-ts" class="headerlink" title="index.ts"></a>index.ts</h2><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> <span class="string">'reflect-metadata'</span>;</div><div class="line"><span class="keyword">import</span> * <span class="keyword">as</span> path <span class="keyword">from</span> <span class="string">'path'</span>;</div><div class="line"><span class="keyword">import</span> * <span class="keyword">as</span> Koa <span class="keyword">from</span> <span class="string">'koa'</span>;</div><div class="line"><span class="keyword">import</span> {</div><div class="line"> ConfigurationStore,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/core'</span>;</div><div class="line">ConfigurationStore.loadFile(path.resolve(__dirname, <span class="string">'../logger'</span>));</div><div class="line"><span class="keyword">import</span> {</div><div class="line"> Router,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/vader'</span>;</div><div class="line"><span class="keyword">import</span> Controller <span class="keyword">from</span> <span class="string">'./controller'</span>;</div><div class="line"></div><div class="line"></div><div class="line"><span class="keyword">const</span> router = Router.newInstance();</div><div class="line">router.use(Controller);</div><div class="line"></div><div class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa();</div><div class="line">app.use(router.routes());</div><div class="line">app.listen(<span class="number">8080</span>);</div></pre></td></tr></table></figure>
<h2 id="logger-yaml"><a href="#logger-yaml" class="headerlink" title="logger.yaml"></a>logger.yaml</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="attr">default:</span> <span class="string">console</span> </div><div class="line"><span class="attr">level:</span> <span class="string">LogLevel.DEBUG</span></div><div class="line"><span class="attr">appenders:</span></div><div class="line"><span class="attr"> - name:</span> <span class="string">console</span></div><div class="line"><span class="attr"> appender:</span> <span class="string">console</span></div><div class="line"><span class="attr"> pattern:</span> <span class="string">'%d{YYYY-MM-DD HH:mm:ss.SSS} %-7c{[%l]} %10n %5p - %2w %M'</span></div><div class="line"><span class="attr"> level:</span> <span class="string">LogLevel.DEBUG</span></div></pre></td></tr></table></figure>
<h2 id="controller-ts"><a href="#controller-ts" class="headerlink" title="controller.ts"></a>controller.ts</h2><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> {</div><div class="line"> GET,</div><div class="line"> Path,</div><div class="line"> Response,</div><div class="line"> PathParam,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/vader'</span>;</div><div class="line"><span class="keyword">import</span> {</div><div class="line"> Component,</div><div class="line">} <span class="keyword">from</span> <span class="string">'@t2ee/core'</span>;</div><div class="line"></div><div class="line"><span class="keyword">let</span> LIST = [{</div><div class="line"> id: <span class="number">0</span>,</div><div class="line"> name: <span class="string">'a'</span>,</div><div class="line">}, {</div><div class="line"> id: <span class="number">1</span>,</div><div class="line"> name: <span class="string">'b'</span>,</div><div class="line">}];</div><div class="line"></div><div class="line"><span class="meta">@Path</span>(<span class="string">'/todo'</span>)</div><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> Controller {</div><div class="line"> <span class="meta">@GET</span></div><div class="line"> <span class="meta">@Path</span>(<span class="string">'/'</span>)</div><div class="line"> <span class="keyword">async</span> getList(<span class="meta">@QueryParam</span>(<span class="string">'id'</span>) id): <span class="built_in">Promise</span><Response> {</div><div class="line"> <span class="keyword">if</span> (id) {</div><div class="line"> id = <span class="built_in">parseInt</span>(id);</div><div class="line"> <span class="keyword">const</span> item = LIST.find(<span class="function"><span class="params">todo</span> =></span> todo.id === id);</div><div class="line"> <span class="keyword">if</span> (item) {</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = item;</div><div class="line"> <span class="keyword">return</span> response; </div><div class="line"> }</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = LIST;</div><div class="line"> <span class="keyword">return</span> response;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@GET</span></div><div class="line"> <span class="meta">@Path</span>(<span class="string">'/:id'</span>)</div><div class="line"> <span class="keyword">async</span> getItem(<span class="meta">@PathParam</span>(<span class="string">'id'</span>) id) {</div><div class="line"> id = <span class="built_in">parseInt</span>(id);</div><div class="line"> <span class="keyword">const</span> item = LIST.find(<span class="function"><span class="params">todo</span> =></span> todo.id === id);</div><div class="line"> <span class="keyword">if</span> (item) {</div><div class="line"> <span class="keyword">const</span> response = <span class="keyword">new</span> Response();</div><div class="line"> response.body = item;</div><div class="line"> <span class="keyword">return</span> response; </div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h1 id="Last-but-not-the-least"><a href="#Last-but-not-the-least" class="headerlink" title="Last but not the least"></a>Last but not the least</h1><p>This should conclude our first part of our first tutorial. In the next part, we will be finishing it up, adding <strong>create</strong>, <strong>update</strong> and <strong>delete</strong> routes.</p>
]]></content>
<summary type="html">
<h1 id="First-of-all"><a href="#First-of-all" class="headerlink" title="First of all"></a>First of all</h1><p>This is the first project of t
</summary>
<category term="tutorials" scheme="https://blog.t2ee.org/tags/tutorials/"/>
<category term="A Simple Todo List Api Server" scheme="https://blog.t2ee.org/tags/A-Simple-Todo-List-Api-Server/"/>
</entry>
</feed>